[maven-release-plugin] copy for tag cxf-dosgi-ri-1.6.0
git-svn-id: https://svn.apache.org/repos/asf/cxf/dosgi/tags/cxf-dosgi-ri-1.6.0@1558808 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/trunk/.gitignore b/trunk/.gitignore
new file mode 100644
index 0000000..9629ccf
--- /dev/null
+++ b/trunk/.gitignore
@@ -0,0 +1,10 @@
+*.i??
+.DS_Store
+.checkstyle
+.classpath
+.pmd
+.project
+.ruleset
+.settings/
+target/
+velocity.log
diff --git a/trunk/discovery/distributed/cxf-discovery/pom.xml b/trunk/discovery/distributed/cxf-discovery/pom.xml
new file mode 100644
index 0000000..608af11
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/pom.xml
@@ -0,0 +1,117 @@
+<?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>
+ <artifactId>cxf-dosgi-ri-discovery-distributed</artifactId>
+ <packaging>bundle</packaging>
+ <name>CXF DOSGi ZooKeeper-based Discovery Service Bundle</name>
+ <description>An implementation of the Distributed OSGi Discovery Service using ZooKeeper</description>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>../../..</topDirectoryLocation>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </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>
+ <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>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-discovery-local</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Activator>org.apache.cxf.dosgi.discovery.zookeeper.Activator</Bundle-Activator>
+ <Export-Package>
+ !*
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/Activator.java b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/Activator.java
new file mode 100644
index 0000000..cbbea58
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/Activator.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.cxf.dosgi.discovery.zookeeper;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedService;
+
+public class Activator implements BundleActivator {
+
+ private ZooKeeperDiscovery zkd;
+
+ public synchronized void start(BundleContext bc) throws Exception {
+ zkd = new ZooKeeperDiscovery(bc);
+ Dictionary<String, String> props = new Hashtable<String, String>();
+ props.put(Constants.SERVICE_PID, "org.apache.cxf.dosgi.discovery.zookeeper");
+ bc.registerService(ManagedService.class.getName(), zkd, props);
+ }
+
+ public synchronized void stop(BundleContext bc) throws Exception {
+ zkd.stop(true);
+ }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/ZooKeeperDiscovery.java b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/ZooKeeperDiscovery.java
new file mode 100644
index 0000000..191ced2
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/ZooKeeperDiscovery.java
@@ -0,0 +1,153 @@
+/**
+ * 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.discovery.zookeeper;
+
+import java.io.IOException;
+import java.util.Dictionary;
+
+import org.apache.cxf.dosgi.discovery.zookeeper.publish.PublishingEndpointListenerFactory;
+import org.apache.cxf.dosgi.discovery.zookeeper.subscribe.EndpointListenerTracker;
+import org.apache.cxf.dosgi.discovery.zookeeper.subscribe.InterfaceMonitorManager;
+import org.apache.cxf.dosgi.discovery.zookeeper.util.Utils;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooKeeper;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ZooKeeperDiscovery implements Watcher, ManagedService {
+
+ public static final String DISCOVERY_ZOOKEEPER_ID = "org.apache.cxf.dosgi.discovery.zookeeper";
+
+ private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperDiscovery.class);
+
+ private final BundleContext bctx;
+
+ private PublishingEndpointListenerFactory endpointListenerFactory;
+ private ServiceTracker<EndpointListener, EndpointListener> endpointListenerTracker;
+ private InterfaceMonitorManager imManager;
+ private ZooKeeper zk;
+ private boolean closed;
+ private boolean started;
+
+ private Dictionary<String , ?> curConfiguration;
+
+ public ZooKeeperDiscovery(BundleContext bctx) {
+ this.bctx = bctx;
+ this.curConfiguration = null;
+ }
+
+ public synchronized void updated(Dictionary<String, ?> configuration) throws ConfigurationException {
+ LOG.debug("Received configuration update for Zookeeper Discovery: {}", configuration);
+
+ stop(false);
+
+ if (configuration == null) {
+ return;
+ }
+ curConfiguration = configuration;
+ createZooKeeper(configuration);
+ }
+
+ private synchronized void start() {
+ if (closed) {
+ return;
+ }
+ if (started) {
+ // we must be re-entrant, i.e. can be called when already started
+ LOG.debug("ZookeeperDiscovery already started");
+ return;
+ }
+ LOG.debug("starting ZookeeperDiscovery");
+ endpointListenerFactory = new PublishingEndpointListenerFactory(zk, bctx);
+ endpointListenerFactory.start();
+ imManager = new InterfaceMonitorManager(bctx, zk);
+ endpointListenerTracker = new EndpointListenerTracker(bctx, imManager);
+ endpointListenerTracker.open();
+ started = true;
+ }
+
+ public synchronized void stop(boolean close) {
+ if (started) {
+ LOG.debug("stopping ZookeeperDiscovery");
+ }
+ started = false;
+ closed |= close;
+ if (endpointListenerFactory != null) {
+ endpointListenerFactory.stop();
+ }
+ if (endpointListenerTracker != null) {
+ endpointListenerTracker.close();
+ }
+ if (imManager != null) {
+ imManager.close();
+ }
+ if (zk != null) {
+ try {
+ zk.close();
+ } catch (InterruptedException e) {
+ LOG.error("Error closing ZooKeeper", e);
+ }
+ }
+ }
+
+ private synchronized void createZooKeeper(Dictionary<String, ?> props) {
+ if (closed) {
+ return;
+ }
+ String host = Utils.getProp(props, "zookeeper.host", "localhost");
+ String port = Utils.getProp(props, "zookeeper.port", "2181");
+ int timeout = Utils.getProp(props, "zookeeper.timeout", 3000);
+ LOG.debug("ZooKeeper configuration: 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);
+ }
+ }
+
+ /* Callback for ZooKeeper */
+ public void process(WatchedEvent event) {
+ LOG.debug("got ZooKeeper event " + event);
+ switch (event.getState()) {
+ case SyncConnected:
+ LOG.info("Connection to ZooKeeper established");
+ // this event can be triggered more than once in a row (e.g. after Disconnected event),
+ // so we must be re-entrant here
+ start();
+ break;
+
+ case Expired:
+ LOG.info("Connection to ZooKeeper expired. Trying to create a new connection");
+ stop(false);
+ createZooKeeper(curConfiguration);
+ break;
+
+ default:
+ // ignore other events
+ break;
+ }
+ }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/DiscoveryPlugin.java b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/DiscoveryPlugin.java
new file mode 100644
index 0000000..5d46585
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/DiscoveryPlugin.java
@@ -0,0 +1,54 @@
+/**
+ * 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.discovery.zookeeper.publish;
+
+import java.util.Map;
+
+/**
+ * This interface allows transformation of service registration information before it is pushed into the ZooKeeper
+ * discovery system.
+ * It can be useful for situations where a host name or port number needs to be changed in cases where the host running
+ * the service is known differently from the outside to what the local Java process thinks it is.
+ * Extra service properties can also be added to the registration which can be useful to refine the remote service
+ * lookup process. <p/>
+ *
+ * DiscoveryPlugins use the OSGi WhiteBoard pattern. To add one to the system, register an instance under this interface
+ * with the OSGi Service Registry. All registered DiscoveryPlugin instances are visited and given a chance to
+ * process the information before it is pushed into ZooKeeper. <p/>
+ *
+ * Note that the changes made using this plugin do not modify the local service registration.
+ *
+ */
+public interface DiscoveryPlugin {
+
+ /**
+ * Process service registration information. Plugins can change this information before it is published into the
+ * ZooKeeper discovery system.
+ *
+ * @param mutableProperties A map of service registration properties. The map is mutable and any changes to the map
+ * will be reflected in the ZooKeeper registration.
+ * @param endpointKey The key under which the service is registered in ZooKeeper. This key typically has the
+ * following format: hostname#port##context. While the actual value of this key is not actually used by the
+ * system (people can use it as a hint to understand where the service is located), the value <i>must</i> be
+ * unique for all services of a given type.
+ * @return The <tt>endpointKey</tt> value to be used. If there is no need to change this simply return the value
+ * of the <tt>endpointKey</tt> parameter.
+ */
+ String process(Map<String, Object> mutableProperties, String endpointKey);
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListener.java b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListener.java
new file mode 100644
index 0000000..c703b9f
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListener.java
@@ -0,0 +1,216 @@
+/**
+ * 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.discovery.zookeeper.publish;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.discovery.zookeeper.util.Utils;
+import org.apache.cxf.dosgi.endpointdesc.EndpointDescriptionParser;
+import org.apache.cxf.dosgi.endpointdesc.PropertiesMapper;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.KeeperException.NoNodeException;
+import org.apache.zookeeper.KeeperException.NodeExistsException;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.xmlns.rsa.v1_0.EndpointDescriptionType;
+import org.osgi.xmlns.rsa.v1_0.PropertyType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Listens for local Endpoints and publishes them to ZooKeeper.
+ */
+public class PublishingEndpointListener implements EndpointListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PublishingEndpointListener.class);
+
+ private final ZooKeeper zk;
+ private final ServiceTracker<DiscoveryPlugin, DiscoveryPlugin> discoveryPluginTracker;
+ private final List<EndpointDescription> endpoints = new ArrayList<EndpointDescription>();
+ private boolean closed;
+
+ private final EndpointDescriptionParser endpointDescriptionParser;
+
+ public PublishingEndpointListener(ZooKeeper zk, BundleContext bctx) {
+ this.zk = zk;
+ discoveryPluginTracker = new ServiceTracker<DiscoveryPlugin, DiscoveryPlugin>(bctx,
+ DiscoveryPlugin.class, null);
+ discoveryPluginTracker.open();
+ endpointDescriptionParser = new EndpointDescriptionParser();
+ }
+
+ public void endpointAdded(EndpointDescription endpoint, String matchedFilter) {
+ LOG.info("Local EndpointDescription added: {}", endpoint);
+
+ synchronized (endpoints) {
+ if (closed) {
+ return;
+ }
+ if (endpoints.contains(endpoint)) {
+ // TODO -> Should the published endpoint be updated here?
+ return;
+ }
+
+ try {
+ addEndpoint(endpoint);
+ endpoints.add(endpoint);
+ } catch (Exception ex) {
+ LOG.error("Exception while processing the addition of an endpoint.", ex);
+ }
+ }
+ }
+
+ private void addEndpoint(EndpointDescription endpoint) throws URISyntaxException, KeeperException,
+ InterruptedException, IOException {
+ Collection<String> interfaces = endpoint.getInterfaces();
+ String endpointKey = getKey(endpoint.getId());
+ Map<String, Object> props = new HashMap<String, Object>(endpoint.getProperties());
+
+ // process plugins
+ Object[] plugins = discoveryPluginTracker.getServices();
+ if (plugins != null) {
+ for (Object plugin : plugins) {
+ if (plugin instanceof DiscoveryPlugin) {
+ endpointKey = ((DiscoveryPlugin)plugin).process(props, endpointKey);
+ }
+ }
+ }
+
+ for (String name : interfaces) {
+ String path = Utils.getZooKeeperPath(name);
+ String fullPath = path + '/' + endpointKey;
+ LOG.debug("Creating ZooKeeper node: {}", fullPath);
+ ensurePath(path, zk);
+ List<PropertyType> propsOut = new PropertiesMapper().fromProps(props);
+ EndpointDescriptionType epd = new EndpointDescriptionType();
+ epd.getProperty().addAll(propsOut);
+ byte[] epData = endpointDescriptionParser.getData(epd);
+ createEphemeralNode(fullPath, epData);
+ }
+ }
+
+ private void createEphemeralNode(String fullPath, byte[] data) throws KeeperException, InterruptedException {
+ try {
+ zk.create(fullPath, data, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
+ } catch (NodeExistsException nee) {
+ // this sometimes happens after a ZooKeeper node dies and the ephemeral node
+ // that belonged to the old session was not yet deleted. We need to make our
+ // session the owner of the node so it won't get deleted automatically -
+ // we do this by deleting and recreating it ourselves.
+ LOG.info("node for endpoint already exists, recreating: {}", fullPath);
+ try {
+ zk.delete(fullPath, -1);
+ } catch (NoNodeException nne) {
+ // it's a race condition, but as long as it got deleted - it's ok
+ }
+ zk.create(fullPath, data, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
+ }
+ }
+
+ public void endpointRemoved(EndpointDescription endpoint, String matchedFilter) {
+ LOG.info("Local EndpointDescription removed: {}", endpoint);
+
+ synchronized (endpoints) {
+ if (closed) {
+ return;
+ }
+ if (!endpoints.contains(endpoint)) {
+ return;
+ }
+
+ try {
+ removeEndpoint(endpoint);
+ endpoints.remove(endpoint);
+ } catch (Exception ex) {
+ LOG.error("Exception while processing the removal of an endpoint", ex);
+ }
+ }
+ }
+
+ private void removeEndpoint(EndpointDescription endpoint) throws UnknownHostException, URISyntaxException {
+ Collection<String> interfaces = endpoint.getInterfaces();
+ String endpointKey = getKey(endpoint.getId());
+
+ for (String name : interfaces) {
+ String path = Utils.getZooKeeperPath(name);
+ String fullPath = path + '/' + endpointKey;
+ LOG.debug("Removing ZooKeeper node: {}", fullPath);
+ try {
+ zk.delete(fullPath, -1);
+ } catch (Exception ex) {
+ LOG.debug("Error while removing endpoint: {}", ex); // e.g. session expired
+ }
+ }
+ }
+
+ private static void ensurePath(String path, ZooKeeper zk) throws KeeperException, InterruptedException {
+ StringBuilder current = new StringBuilder();
+ String[] parts = Utils.removeEmpty(path.split("/"));
+ for (String part : parts) {
+ current.append('/');
+ current.append(part);
+ try {
+ zk.create(current.toString(), new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ } catch (NodeExistsException nee) {
+ // it's not the first node with this path to ever exist - that's normal
+ }
+ }
+ }
+
+ 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();
+ }
+
+ public void close() {
+ LOG.debug("closing - removing all endpoints");
+ synchronized (endpoints) {
+ closed = true;
+ for (EndpointDescription endpoint : endpoints) {
+ try {
+ removeEndpoint(endpoint);
+ } catch (Exception ex) {
+ LOG.error("Exception while removing endpoint during close", ex);
+ }
+ }
+ endpoints.clear();
+ }
+ discoveryPluginTracker.close();
+ }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerFactory.java b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerFactory.java
new file mode 100644
index 0000000..c505bb4
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerFactory.java
@@ -0,0 +1,105 @@
+/**
+ * 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.discovery.zookeeper.publish;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.cxf.dosgi.discovery.zookeeper.ZooKeeperDiscovery;
+import org.apache.cxf.dosgi.discovery.zookeeper.util.Utils;
+import org.apache.zookeeper.ZooKeeper;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Creates local EndpointListeners that publish to ZooKeeper.
+ */
+public class PublishingEndpointListenerFactory implements ServiceFactory<PublishingEndpointListener> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PublishingEndpointListenerFactory.class);
+
+ private final BundleContext bctx;
+ private final ZooKeeper zk;
+ private final List<PublishingEndpointListener> listeners = new ArrayList<PublishingEndpointListener>();
+ private ServiceRegistration serviceRegistration;
+
+ public PublishingEndpointListenerFactory(ZooKeeper zk, BundleContext bctx) {
+ this.bctx = bctx;
+ this.zk = zk;
+ }
+
+ public PublishingEndpointListener getService(Bundle b, ServiceRegistration<PublishingEndpointListener> sr) {
+ LOG.debug("new EndpointListener from factory");
+ synchronized (listeners) {
+ PublishingEndpointListener pel = new PublishingEndpointListener(zk, bctx);
+ listeners.add(pel);
+ return pel;
+ }
+ }
+
+ public void ungetService(Bundle b, ServiceRegistration<PublishingEndpointListener> sr,
+ PublishingEndpointListener pel) {
+ LOG.debug("remove EndpointListener");
+ synchronized (listeners) {
+ if (listeners.remove(pel)) {
+ pel.close();
+ }
+ }
+ }
+
+ public synchronized void start() {
+ Dictionary<String, String> props = new Hashtable<String, String>();
+ props.put(EndpointListener.ENDPOINT_LISTENER_SCOPE,
+ "(&(" + Constants.OBJECTCLASS + "=*)(" + RemoteConstants.ENDPOINT_FRAMEWORK_UUID
+ + "=" + Utils.getUUID(bctx) + "))");
+ props.put(ZooKeeperDiscovery.DISCOVERY_ZOOKEEPER_ID, "true");
+ serviceRegistration = bctx.registerService(EndpointListener.class.getName(), this, props);
+ }
+
+ public synchronized void stop() {
+ if (serviceRegistration != null) {
+ serviceRegistration.unregister();
+ serviceRegistration = null;
+ }
+ synchronized (listeners) {
+ for (PublishingEndpointListener pel : listeners) {
+ pel.close();
+ }
+ listeners.clear();
+ }
+ }
+
+ /**
+ * Only for the test case!
+ */
+ protected List<PublishingEndpointListener> getListeners() {
+ synchronized (listeners) {
+ return listeners;
+ }
+ }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/EndpointListenerTracker.java b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/EndpointListenerTracker.java
new file mode 100644
index 0000000..59e8c66
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/EndpointListenerTracker.java
@@ -0,0 +1,53 @@
+/**
+ * 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.discovery.zookeeper.subscribe;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Tracks interest in EndpointListeners. Delegates to InterfaceMonitorManager to manage
+ * interest in the scopes of each EndpointListener.
+ */
+public class EndpointListenerTracker extends ServiceTracker<EndpointListener, EndpointListener> {
+ private final InterfaceMonitorManager imManager;
+
+ public EndpointListenerTracker(BundleContext bctx, InterfaceMonitorManager imManager) {
+ super(bctx, EndpointListener.class, null);
+ this.imManager = imManager;
+ }
+
+ public EndpointListener addingService(ServiceReference<EndpointListener> endpointListener) {
+ imManager.addInterest(endpointListener);
+ return null;
+ }
+
+ public void modifiedService(ServiceReference<EndpointListener> endpointListener, EndpointListener service) {
+ // called when an EndpointListener updates its service properties,
+ // e.g. when its interest scope is expanded/reduced
+ imManager.addInterest(endpointListener);
+ }
+
+ public void removedService(ServiceReference<EndpointListener> endpointListener, EndpointListener service) {
+ imManager.removeInterest(endpointListener);
+ }
+
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitor.java b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitor.java
new file mode 100644
index 0000000..95277d3
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitor.java
@@ -0,0 +1,262 @@
+/**
+ * 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.discovery.zookeeper.subscribe;
+
+import java.io.ByteArrayInputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.discovery.zookeeper.util.Utils;
+import org.apache.cxf.dosgi.endpointdesc.EndpointDescriptionParser;
+import org.apache.cxf.dosgi.endpointdesc.PropertiesMapper;
+import org.apache.zookeeper.AsyncCallback.StatCallback;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.KeeperException.Code;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.data.Stat;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.osgi.xmlns.rsa.v1_0.EndpointDescriptionType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Monitors ZooKeeper for changes in published endpoints.
+ * <p>
+ * Specifically, it monitors the node path associated with a given interface class,
+ * whose data is a serialized version of an EndpointDescription, and notifies an
+ * EndpointListener when changes are detected (which can then propagate the
+ * notification to other EndpointListeners with a matching scope).
+ * <p>
+ * Note that the EndpointListener is used here as a decoupling interface for
+ * convenience, and is not necessarily used according to its documented contract.
+ */
+public class InterfaceMonitor implements Watcher, StatCallback {
+
+ private static final Logger LOG = LoggerFactory.getLogger(InterfaceMonitor.class);
+
+ private final String znode;
+ private final ZooKeeper zk;
+ private final EndpointListener endpointListener;
+ private final boolean recursive;
+ private volatile boolean closed;
+
+ // This map reference changes, so don't synchronize on it
+ private Map<String, EndpointDescription> nodes = new HashMap<String, EndpointDescription>();
+
+ private EndpointDescriptionParser parser;
+
+ public InterfaceMonitor(ZooKeeper zk, String objClass, EndpointListener endpointListener, String scope) {
+ this.zk = zk;
+ this.znode = Utils.getZooKeeperPath(objClass);
+ this.recursive = objClass == null || objClass.isEmpty();
+ this.endpointListener = endpointListener;
+ this.parser = new EndpointDescriptionParser();
+ LOG.debug("Creating new InterfaceMonitor {} for scope [{}] and objectClass [{}]",
+ new Object[] {recursive ? "(recursive)" : "", scope, objClass});
+ }
+
+ /**
+ * Returns all endpoints that are currently known to this monitor.
+ *
+ * @return all endpoints that are currently known to this monitor
+ */
+ public synchronized List<EndpointDescription> getEndpoints() {
+ return new ArrayList<EndpointDescription>(nodes.values());
+ }
+
+ public void start() {
+ watch();
+ }
+
+ private void watch() {
+ LOG.debug("registering a ZooKeeper.exists({}) callback", znode);
+ zk.exists(znode, this, this, null);
+ }
+
+ /**
+ * Zookeeper Watcher interface callback.
+ */
+ public void process(WatchedEvent event) {
+ LOG.debug("ZooKeeper watcher callback on node {} for event {}", znode, event);
+ processDelta();
+ }
+
+ /**
+ * Zookeeper StatCallback interface callback.
+ */
+ @SuppressWarnings("deprecation")
+ public void processResult(int rc, String path, Object ctx, Stat stat) {
+ LOG.debug("ZooKeeper callback on node: {} code: {}", znode, rc);
+
+ switch (rc) {
+ case Code.Ok:
+ case Code.NoNode:
+ processDelta();
+ return;
+
+ case Code.SessionExpired:
+ case Code.NoAuth:
+ case Code.ConnectionLoss:
+ return;
+
+ default:
+ watch();
+ }
+ }
+
+ private void processDelta() {
+ if (closed) {
+ return;
+ }
+
+ if (zk.getState() != ZooKeeper.States.CONNECTED) {
+ LOG.debug("ZooKeeper connection was already closed! Not processing changed event.");
+ return;
+ }
+
+ try {
+ if (zk.exists(znode, false) != null) {
+ zk.getChildren(znode, this);
+ refreshNodes();
+ } else {
+ LOG.debug("znode {} doesn't exist -> not processing any changes", znode);
+ }
+ } catch (Exception e) {
+ if (zk.getState() != ZooKeeper.States.CONNECTED) {
+ LOG.debug("Error getting Zookeeper data: " + e); // e.g. session expired, handled by ZooKeeperDiscovery
+ } else {
+ LOG.error("Error getting ZooKeeper data.", e);
+ }
+ }
+ }
+
+ public synchronized void close() {
+ closed = true;
+ for (EndpointDescription endpoint : nodes.values()) {
+ endpointListener.endpointRemoved(endpoint, null);
+ }
+ nodes.clear();
+ }
+
+ private synchronized void refreshNodes() {
+ if (closed) {
+ return;
+ }
+ LOG.info("Processing change on node: {}", znode);
+
+ Map<String, EndpointDescription> newNodes = new HashMap<String, EndpointDescription>();
+ Map<String, EndpointDescription> prevNodes = new HashMap<String, EndpointDescription>(nodes);
+ processChildren(znode, newNodes, prevNodes);
+
+ // whatever is left in prevNodes now has been removed from Discovery
+ LOG.debug("processChildren done. Nodes that are missing now and need to be removed: {}", prevNodes.values());
+ for (EndpointDescription endpoint : prevNodes.values()) {
+ endpointListener.endpointRemoved(endpoint, null);
+ }
+ nodes = newNodes;
+ }
+
+ /**
+ * Iterates through all child nodes of the given node and tries to find
+ * endpoints. If the recursive flag is set it also traverses into the child
+ * nodes.
+ *
+ * @return true if an endpoint was found and if the node therefore needs to
+ * be monitored for changes
+ */
+ private boolean processChildren(String zn, Map<String, EndpointDescription> newNodes,
+ Map<String, EndpointDescription> prevNodes) {
+ List<String> children;
+ try {
+ LOG.debug("Processing the children of {}", zn);
+ children = zk.getChildren(zn, false);
+
+ boolean foundANode = false;
+ for (String child : children) {
+ String childZNode = zn + '/' + child;
+ EndpointDescription endpoint = getEndpointDescriptionFromNode(childZNode);
+ if (endpoint != null) {
+ EndpointDescription prevEndpoint = prevNodes.get(child);
+ LOG.info("found new node " + zn + "/[" + child + "] ( []->child ) props: "
+ + endpoint.getProperties().values());
+ newNodes.put(child, endpoint);
+ prevNodes.remove(child);
+ foundANode = true;
+ LOG.debug("Properties: {}", endpoint.getProperties());
+ if (prevEndpoint == null) {
+ // This guy is new
+ endpointListener.endpointAdded(endpoint, null);
+ } else if (!prevEndpoint.getProperties().equals(endpoint.getProperties())) {
+ // TODO
+ }
+ }
+ if (recursive && processChildren(childZNode, newNodes, prevNodes)) {
+ zk.getChildren(childZNode, this);
+ }
+ }
+
+ return foundANode;
+ } catch (KeeperException e) {
+ LOG.error("Problem processing ZooKeeper node", e);
+ } catch (InterruptedException e) {
+ LOG.error("Problem processing ZooKeeper node", e);
+ }
+ return false;
+ }
+
+ /**
+ * Retrieves data from the given node and parses it into an EndpointDescription.
+ *
+ * @param node a node path
+ * @return endpoint found in the node or null if no endpoint was found
+ */
+ private EndpointDescription getEndpointDescriptionFromNode(String node) {
+ try {
+ Stat stat = zk.exists(node, false);
+ if (stat == null || stat.getDataLength() <= 0) {
+ return null;
+ }
+ byte[] data = zk.getData(node, false, null);
+ LOG.debug("Got data for node: {}", node);
+
+ EndpointDescription endpoint = getFirstEnpointDescription(data);
+ if (endpoint != null) {
+ return endpoint;
+ }
+ LOG.warn("No Discovery information found for node: {}", node);
+ } catch (Exception e) {
+ LOG.error("Problem getting EndpointDescription from node " + node, e);
+ }
+ return null;
+ }
+
+ public EndpointDescription getFirstEnpointDescription(byte[] data) {
+ List<EndpointDescriptionType> elements = parser.getEndpointDescriptions(new ByteArrayInputStream(data));
+ if (elements.isEmpty()) {
+ return null;
+ }
+ Map<String, Object> props = new PropertiesMapper().toProps(elements.get(0).getProperty());
+ return new EndpointDescription(props);
+ }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorManager.java b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorManager.java
new file mode 100644
index 0000000..240e5ea
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorManager.java
@@ -0,0 +1,218 @@
+/**
+ * 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.discovery.zookeeper.subscribe;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.cxf.dosgi.discovery.zookeeper.ZooKeeperDiscovery;
+import org.apache.cxf.dosgi.discovery.zookeeper.util.Utils;
+import org.apache.zookeeper.ZooKeeper;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.cxf.dosgi.discovery.local.util.Utils.matchFilter;
+
+/**
+ * Manages the EndpointListeners and the scopes they are interested in.
+ * For each scope with interested EndpointListeners an InterfaceMonitor is created.
+ * The InterfaceMonitor calls back when it detects added or removed external Endpoints.
+ * These events are then forwarded to all interested EndpointListeners.
+ */
+public class InterfaceMonitorManager {
+
+ private static final Logger LOG = LoggerFactory.getLogger(InterfaceMonitorManager.class);
+
+ private final BundleContext bctx;
+ private final ZooKeeper zk;
+ // map of EndpointListeners and the scopes they are interested in
+ private final Map<ServiceReference<EndpointListener>, List<String>> endpointListenerScopes =
+ new HashMap<ServiceReference<EndpointListener>, List<String>>();
+ // map of scopes and their interest data
+ private final Map<String, Interest> interests = new HashMap<String, Interest>();
+
+ protected static class Interest {
+ List<ServiceReference<EndpointListener>> endpointListeners =
+ new CopyOnWriteArrayList<ServiceReference<EndpointListener>>();
+ InterfaceMonitor monitor;
+ }
+
+ public InterfaceMonitorManager(BundleContext bctx, ZooKeeper zk) {
+ this.bctx = bctx;
+ this.zk = zk;
+ }
+
+ public void addInterest(ServiceReference<EndpointListener> endpointListener) {
+ if (isOurOwnEndpointListener(endpointListener)) {
+ LOG.debug("Skipping our own EndpointListener");
+ return;
+ }
+
+ LOG.info("updating EndpointListener interests: {}", endpointListener);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("updated EndpointListener properties: {}", Utils.getProperties(endpointListener));
+ }
+ for (String scope : Utils.getScopes(endpointListener)) {
+ String objClass = Utils.getObjectClass(scope);
+ LOG.debug("Adding interest in scope {}, objectClass {}", scope, objClass);
+ addInterest(endpointListener, scope, objClass);
+ }
+ }
+
+ private static boolean isOurOwnEndpointListener(ServiceReference<EndpointListener> endpointListener) {
+ return Boolean.parseBoolean(String.valueOf(
+ endpointListener.getProperty(ZooKeeperDiscovery.DISCOVERY_ZOOKEEPER_ID)));
+ }
+
+ @SuppressWarnings("unchecked")
+ public synchronized void addInterest(ServiceReference<EndpointListener> endpointListener,
+ String scope, String objClass) {
+ // get or create interest for given scope and add listener to it
+ Interest interest = interests.get(scope);
+ if (interest == null) {
+ // create interest, add listener and start monitor
+ interest = new Interest();
+ interests.put(scope, interest);
+ interest.endpointListeners.add(endpointListener); // add it before monitor starts so we don't miss events
+ interest.monitor = createInterfaceMonitor(scope, objClass, interest);
+ interest.monitor.start();
+ } else {
+ // interest already exists, so just add listener to it
+ if (!interest.endpointListeners.contains(endpointListener)) {
+ interest.endpointListeners.add(endpointListener);
+ }
+ // notify listener of all known endpoints for given scope
+ // (as EndpointListener contract requires of all added/modified listeners)
+ for (EndpointDescription endpoint : interest.monitor.getEndpoints()) {
+ notifyListeners(endpoint, scope, true, Arrays.asList(endpointListener));
+ }
+ }
+
+ // add scope to listener's scopes list
+ List<String> scopes = endpointListenerScopes.get(endpointListener);
+ if (scopes == null) {
+ scopes = new ArrayList<String>(1);
+ endpointListenerScopes.put(endpointListener, scopes);
+ }
+ if (!scopes.contains(scope)) {
+ scopes.add(scope);
+ }
+ }
+
+ public synchronized void removeInterest(ServiceReference<EndpointListener> endpointListener) {
+ LOG.info("removing EndpointListener interests: {}", endpointListener);
+ List<String> scopes = endpointListenerScopes.get(endpointListener);
+ if (scopes == null) {
+ return;
+ }
+
+ for (String scope : scopes) {
+ Interest interest = interests.get(scope);
+ if (interest != null) {
+ interest.endpointListeners.remove(endpointListener);
+ if (interest.endpointListeners.isEmpty()) {
+ interest.monitor.close();
+ interests.remove(scope);
+ }
+ }
+ }
+ endpointListenerScopes.remove(endpointListener);
+ }
+
+ protected InterfaceMonitor createInterfaceMonitor(final String scope, String objClass, final Interest interest) {
+ // holding this object's lock in the callbacks can lead to a deadlock with InterfaceMonitor
+ EndpointListener endpointListener = new EndpointListener() {
+
+ public void endpointRemoved(EndpointDescription endpoint, String matchedFilter) {
+ notifyListeners(endpoint, scope, false, interest.endpointListeners);
+ }
+
+ public void endpointAdded(EndpointDescription endpoint, String matchedFilter) {
+ notifyListeners(endpoint, scope, true, interest.endpointListeners);
+ }
+ };
+ return new InterfaceMonitor(zk, objClass, endpointListener, scope);
+ }
+
+ private void notifyListeners(EndpointDescription endpoint, String currentScope, boolean isAdded,
+ List<ServiceReference<EndpointListener>> endpointListeners) {
+ for (ServiceReference<EndpointListener> endpointListenerRef : endpointListeners) {
+ EndpointListener service = bctx.getService(endpointListenerRef);
+ try {
+ EndpointListener endpointListener = (EndpointListener)service;
+ LOG.trace("matching {} against {}", endpoint, currentScope);
+ if (matchFilter(bctx, currentScope, endpoint)) {
+ LOG.debug("Matched {} against {}", endpoint, currentScope);
+ notifyListener(endpoint, currentScope, isAdded, endpointListenerRef.getBundle(),
+ endpointListener);
+ }
+ } finally {
+ if (service != null) {
+ bctx.ungetService(endpointListenerRef);
+ }
+ }
+ }
+ }
+
+ private void notifyListener(EndpointDescription endpoint, String currentScope, boolean isAdded,
+ Bundle endpointListenerBundle, EndpointListener endpointListener) {
+ if (endpointListenerBundle == null) {
+ LOG.info("listening service was unregistered, ignoring");
+ } else if (isAdded) {
+ LOG.info("calling EndpointListener.endpointAdded: " + endpointListener + " from bundle "
+ + endpointListenerBundle.getSymbolicName() + " for endpoint: " + endpoint);
+ endpointListener.endpointAdded(endpoint, currentScope);
+ } else {
+ LOG.info("calling EndpointListener.endpointRemoved: " + endpointListener + " from bundle "
+ + endpointListenerBundle.getSymbolicName() + " for endpoint: " + endpoint);
+ endpointListener.endpointRemoved(endpoint, currentScope);
+ }
+ }
+
+ public synchronized void close() {
+ for (Interest interest : interests.values()) {
+ interest.monitor.close();
+ }
+ interests.clear();
+ endpointListenerScopes.clear();
+ }
+
+ /**
+ * Only for test case!
+ */
+ protected synchronized Map<String, Interest> getInterests() {
+ return interests;
+ }
+
+ /**
+ * Only for test case!
+ */
+ protected synchronized Map<ServiceReference<EndpointListener>, List<String>> getEndpointListenerScopes() {
+ return endpointListenerScopes;
+ }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/util/Utils.java b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/util/Utils.java
new file mode 100644
index 0000000..121546e
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/util/Utils.java
@@ -0,0 +1,140 @@
+/**
+ * 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.discovery.zookeeper.util;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+
+public final class Utils {
+
+ static final String PATH_PREFIX = "/osgi/service_registry";
+ static final Pattern OBJECTCLASS_PATTERN = Pattern.compile(".*\\(objectClass=([^)]+)\\).*");
+
+ private Utils() {
+ // never constructed
+ }
+
+ public static String getZooKeeperPath(String name) {
+ return name == null || name.isEmpty() ? PATH_PREFIX : PATH_PREFIX + '/' + name.replace('.', '/');
+ }
+
+ /**
+ * Returns the value of a "string+" property as an array of strings.
+ * <p>
+ * A "string+" property can have a value which is either a string,
+ * an array of strings, or a collection of strings.
+ * <p>
+ * If the given value is not of one of the valid types, or is null,
+ * an empty array is returned.
+ *
+ * @param property a "string+" property value
+ * @return the property value as an array of strings, or an empty array
+ */
+ public static String[] getStringPlusProperty(Object property) {
+ if (property instanceof String) {
+ return new String[] {(String)property};
+ } else if (property instanceof String[]) {
+ return (String[])property;
+ } else if (property instanceof Collection) {
+ try {
+ @SuppressWarnings("unchecked")
+ Collection<String> strings = (Collection<String>)property;
+ return strings.toArray(new String[strings.size()]);
+ } catch (ArrayStoreException ase) {
+ // ignore collections with wrong type
+ }
+ }
+ return new String[0];
+ }
+
+ /**
+ * Removes nulls and empty strings from the given string array.
+ *
+ * @param strings an array of strings
+ * @return a new array containing the non-null and non-empty
+ * elements of the original array in the same order
+ */
+ public static String[] removeEmpty(String[] strings) {
+ String[] result = new String[strings.length];
+ int copied = 0;
+ for (String s : strings) {
+ if (s != null && !s.isEmpty()) {
+ result[copied++] = s;
+ }
+ }
+ return copied == result.length ? result : Arrays.copyOf(result, copied);
+ }
+
+ public static String[] getScopes(ServiceReference<?> sref) {
+ return removeEmpty(getStringPlusProperty(sref.getProperty(EndpointListener.ENDPOINT_LISTENER_SCOPE)));
+ }
+
+ // copied from the DSW OSGiUtils class
+ public static String getUUID(BundleContext bc) {
+ synchronized ("org.osgi.framework.uuid") {
+ String uuid = bc.getProperty("org.osgi.framework.uuid");
+ if (uuid == null) {
+ uuid = UUID.randomUUID().toString();
+ System.setProperty("org.osgi.framework.uuid", uuid);
+ }
+ return uuid;
+ }
+ }
+
+ public static <K, V> String getProp(Dictionary<K, V> props, String key, String def) {
+ V val = props.get(key);
+ return val == null ? def : val.toString();
+ }
+
+ public static <K, V> int getProp(Dictionary<K, V> props, String key, int def) {
+ V val = props.get(key);
+ return val == null ? def : Integer.parseInt(val.toString());
+ }
+
+ 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;
+ }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/DiscoveryDriverTest.java b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/DiscoveryDriverTest.java
new file mode 100644
index 0000000..84470c2
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/DiscoveryDriverTest.java
@@ -0,0 +1,135 @@
+/**
+ * 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.discovery.zookeeper;
+
+import junit.framework.TestCase;
+
+public class DiscoveryDriverTest extends TestCase {
+
+ public void testDUMMY() {
+ assertTrue(true);
+ }
+
+// public void testDiscoveryDriver() throws Exception {
+// BundleContext bc = getDefaultBundleContext();
+// Dictionary<String, String> props = getDefaultProps();
+//
+// final StringBuilder sb = new StringBuilder();
+// DiscoveryDriver dd = new DiscoveryDriver(bc, props) {
+// @Override
+// ZooKeeper createZooKeeper() throws IOException {
+// sb.append(zkHost + ":" + zkPort);
+// ZooKeeper zk = EasyMock.createMock(ZooKeeper.class);
+// EasyMock.replay(zk);
+// return zk;
+// }
+// };
+// EasyMock.verify(bc);
+// assertEquals("somehost:1910", sb.toString());
+//
+// EasyMock.verify(dd.zooKeeper);
+// EasyMock.reset(dd.zooKeeper);
+// dd.zooKeeper.close();
+// EasyMock.expectLastCall();
+// EasyMock.replay(dd.zooKeeper);
+//
+// ServiceTracker st1 = EasyMock.createMock(ServiceTracker.class);
+// st1.close();
+// EasyMock.expectLastCall();
+// EasyMock.replay(st1);
+// ServiceTracker st2 = EasyMock.createMock(ServiceTracker.class);
+// st2.close();
+// EasyMock.expectLastCall();
+// EasyMock.replay(st2);
+//
+// dd.lookupTracker = st1;
+// dd.publicationTracker = st2;
+//
+// dd.destroy();
+// }
+//
+// private void expectServiceTrackerCalls(BundleContext bc, String objectClass)
+// throws InvalidSyntaxException {
+// Filter filter = EasyMock.createNiceMock(Filter.class);
+// EasyMock.replay(filter);
+//
+// EasyMock.expect(bc.createFilter("(objectClass=" + objectClass + ")"))
+// .andReturn(filter).anyTimes();
+// bc.addServiceListener((ServiceListener) EasyMock.anyObject(),
+// EasyMock.eq("(objectClass=" + objectClass + ")"));
+// EasyMock.expectLastCall().anyTimes();
+// EasyMock.expect(bc.getServiceReferences(objectClass, null))
+// .andReturn(new ServiceReference [0]).anyTimes();
+// }
+//
+// public void testProcessEvent() throws Exception {
+// DiscoveryDriver db = new DiscoveryDriver(getDefaultBundleContext(), getDefaultProps()) {
+// @Override
+// ZooKeeper createZooKeeper() throws IOException {
+// return null;
+// }
+// };
+//
+// FindInZooKeeperCustomizer fc = new FindInZooKeeperCustomizer(null, null);
+// List<InterfaceMonitor> l1 = new ArrayList<InterfaceMonitor>();
+// InterfaceMonitor dm1a = EasyMock.createMock(InterfaceMonitor.class);
+// dm1a.process();
+// EasyMock.expectLastCall();
+// EasyMock.replay(dm1a);
+// InterfaceMonitor dm1b = EasyMock.createMock(InterfaceMonitor.class);
+// dm1b.process();
+// EasyMock.expectLastCall();
+// EasyMock.replay(dm1b);
+// l1.add(dm1a);
+// l1.add(dm1b);
+//
+// List<InterfaceMonitor> l2 = new ArrayList<InterfaceMonitor>();
+// InterfaceMonitor dm2 = EasyMock.createMock(InterfaceMonitor.class);
+// dm2.process();
+// EasyMock.expectLastCall();
+// EasyMock.replay(dm2);
+// l2.add(dm2);
+//
+// fc.watchers.put(EasyMock.createMock(DiscoveredServiceTracker.class), l1);
+// fc.watchers.put(EasyMock.createMock(DiscoveredServiceTracker.class), l2);
+//
+// db.finderCustomizer = fc;
+// db.process(null);
+//
+// EasyMock.verify(dm1a);
+// EasyMock.verify(dm1b);
+// EasyMock.verify(dm2);
+// }
+//
+// private BundleContext getDefaultBundleContext() throws InvalidSyntaxException {
+// BundleContext bc = EasyMock.createMock(BundleContext.class);
+// expectServiceTrackerCalls(bc, ServicePublication.class.getName());
+// expectServiceTrackerCalls(bc, DiscoveredServiceTracker.class.getName());
+// EasyMock.replay(bc);
+// return bc;
+// }
+//
+// private Dictionary<String, String> getDefaultProps() {
+// Dictionary<String, String> props = new Hashtable<String, String>();
+// props.put("zookeeper.host", "somehost");
+// props.put("zookeeper.port", "1910");
+// props.put("zookeeper.timeout", "1500");
+// return props;
+// }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/FindInZooKeeperCustomizerTest.java b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/FindInZooKeeperCustomizerTest.java
new file mode 100644
index 0000000..cb2180b
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/FindInZooKeeperCustomizerTest.java
@@ -0,0 +1,301 @@
+/**
+ * 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.discovery.zookeeper;
+
+import junit.framework.TestCase;
+
+public class FindInZooKeeperCustomizerTest extends TestCase {
+
+ public void testDUMMY() {
+ assertTrue(true);
+ }
+
+// public void testAddingServiceInterface() {
+// DiscoveredServiceTracker dst = new DiscoveredServiceTracker() {
+// public void serviceChanged(DiscoveredServiceNotification dsn) {}
+// };
+//
+// ServiceReference sr = EasyMock.createMock(ServiceReference.class);
+// EasyMock.expect(sr.getProperty(DiscoveredServiceTracker.INTERFACE_MATCH_CRITERIA))
+// .andReturn(Collections.singleton(String.class.getName()));
+// EasyMock.expect(sr.getProperty(DiscoveredServiceTracker.FILTER_MATCH_CRITERIA))
+// .andReturn(null);
+// EasyMock.replay(sr);
+//
+// DiscoveredServiceTracker dst2 = new DiscoveredServiceTracker() {
+// public void serviceChanged(DiscoveredServiceNotification dsn) {}
+// };
+//
+// ServiceReference sr2 = EasyMock.createMock(ServiceReference.class);
+// EasyMock.expect(sr2.getProperty(DiscoveredServiceTracker.INTERFACE_MATCH_CRITERIA))
+// .andReturn(Arrays.asList(Integer.class.getName(), Comparable.class.getName()));
+// EasyMock.expect(sr2.getProperty(DiscoveredServiceTracker.FILTER_MATCH_CRITERIA))
+// .andReturn(null);
+// EasyMock.replay(sr2);
+//
+// BundleContext bc = EasyMock.createMock(BundleContext.class);
+// EasyMock.expect(bc.getService(sr)).andReturn(dst);
+// EasyMock.expect(bc.getService(sr2)).andReturn(dst2);
+// EasyMock.replay(bc);
+//
+// ZooKeeper zk = EasyMock.createMock(ZooKeeper.class);
+// zkExpectExists(zk, String.class.getName());
+// zkExpectExists(zk, Integer.class.getName());
+// zkExpectExists(zk, Comparable.class.getName());
+// EasyMock.expectLastCall();
+// EasyMock.replay(zk);
+//
+// FindInZooKeeperCustomizer fc = new FindInZooKeeperCustomizer(bc, zk);
+//
+// // ---------------------------------------------------------------
+// // Test the addingService APIs
+// // ---------------------------------------------------------------
+//
+// assertEquals("Precondition failed", 0, fc.watchers.size());
+// fc.addingService(sr);
+// assertEquals(1, fc.watchers.size());
+//
+// DiscoveredServiceTracker key = fc.watchers.keySet().iterator().next();
+// assertSame(dst, key);
+// List<InterfaceMonitor> dmList = fc.watchers.get(key);
+// assertEquals(1, dmList.size());
+// InterfaceMonitor dm = dmList.iterator().next();
+// assertNotNull(dm.listener);
+// assertSame(zk, dm.zookeeper);
+// assertEquals(Utils.getZooKeeperPath(String.class.getName()), dm.znode);
+//
+// assertEquals("Precondition failed", 1, fc.watchers.size());
+// fc.addingService(sr2);
+// assertEquals(2, fc.watchers.size());
+//
+// assertTrue(fc.watchers.containsKey(dst));
+// assertTrue(fc.watchers.containsKey(dst2));
+// assertEquals(dmList, fc.watchers.get(dst));
+// List<InterfaceMonitor> dmList2 = fc.watchers.get(dst2);
+// assertEquals(2, dmList2.size());
+//
+// Set<String> actual = new HashSet<String>();
+// for (InterfaceMonitor im : dmList2) {
+// actual.add(im.znode);
+// }
+// Set<String> expected = new HashSet<String>(Arrays.asList(
+// Utils.getZooKeeperPath(Integer.class.getName()),
+// Utils.getZooKeeperPath(Comparable.class.getName())));
+// assertEquals(expected, actual);
+//
+// EasyMock.verify(zk);
+//
+// // ---------------------------------------------------------------
+// // Test the modifiedService APIs
+// // ---------------------------------------------------------------
+// EasyMock.reset(zk);
+// zkExpectExists(zk, List.class.getName());
+// EasyMock.replay(zk);
+//
+// EasyMock.reset(sr);
+// EasyMock.expect(sr.getProperty(DiscoveredServiceTracker.INTERFACE_MATCH_CRITERIA))
+// .andReturn(Collections.singleton(List.class.getName()));
+// EasyMock.expect(sr.getProperty(DiscoveredServiceTracker.FILTER_MATCH_CRITERIA))
+// .andReturn(null);
+// EasyMock.replay(sr);
+//
+// assertEquals("Precondition failed", 2, fc.watchers.size());
+// fc.modifiedService(sr, dst);
+// assertEquals("Precondition failed", 2, fc.watchers.size());
+//
+// assertTrue(fc.watchers.containsKey(dst));
+// assertTrue(fc.watchers.containsKey(dst2));
+// assertEquals(dmList2, fc.watchers.get(dst2));
+// List<InterfaceMonitor> dmList3 = fc.watchers.get(dst);
+// assertEquals(1, dmList3.size());
+// assertEquals(Utils.getZooKeeperPath(List.class.getName()), dmList3.iterator().next().znode);
+//
+// EasyMock.verify(zk);
+//
+// // ---------------------------------------------------------------
+// // Test the removedService APIs
+// // ---------------------------------------------------------------
+// EasyMock.reset(zk);
+// EasyMock.replay(zk);
+//
+// assertEquals("Precondition failed", 2, fc.watchers.size());
+// fc.removedService(sr2, dst2);
+// assertEquals("Precondition failed", 1, fc.watchers.size());
+//
+// assertEquals(dmList3, fc.watchers.get(dst));
+// assertNull(fc.watchers.get(dst2));
+//
+// EasyMock.verify(zk);
+// }
+//
+// public void testAddingServiceFilter() {
+// DiscoveredServiceTracker dst = new DiscoveredServiceTracker() {
+// public void serviceChanged(DiscoveredServiceNotification dsn) {}
+// };
+//
+// ServiceReference sr = EasyMock.createMock(ServiceReference.class);
+// EasyMock.expect(sr.getProperty(DiscoveredServiceTracker.INTERFACE_MATCH_CRITERIA))
+// .andReturn(null);
+// Set<String> stringFilter = Collections.singleton("(objectClass=java.lang.String)");
+// EasyMock.expect(sr.getProperty(DiscoveredServiceTracker.FILTER_MATCH_CRITERIA))
+// .andReturn(stringFilter);
+// EasyMock.replay(sr);
+//
+// DiscoveredServiceTracker dst2 = new DiscoveredServiceTracker() {
+// public void serviceChanged(DiscoveredServiceNotification dsn) {}
+// };
+//
+// ServiceReference sr2 = EasyMock.createMock(ServiceReference.class);
+// EasyMock.expect(sr2.getProperty(DiscoveredServiceTracker.INTERFACE_MATCH_CRITERIA))
+// .andReturn(null);
+// List<String> combinedFilter =
+// Arrays.asList("(objectClass=java.lang.Integer)", "(objectClass=java.lang.Comparable)");
+// EasyMock.expect(sr2.getProperty(DiscoveredServiceTracker.FILTER_MATCH_CRITERIA))
+// .andReturn(combinedFilter);
+// EasyMock.replay(sr2);
+//
+// BundleContext bc = EasyMock.createMock(BundleContext.class);
+// EasyMock.expect(bc.getService(sr)).andReturn(dst);
+// EasyMock.expect(bc.getService(sr2)).andReturn(dst2);
+// EasyMock.replay(bc);
+//
+// ZooKeeper zk = EasyMock.createMock(ZooKeeper.class);
+// zkExpectExists(zk, String.class.getName());
+// zkExpectExists(zk, Integer.class.getName());
+// zkExpectExists(zk, Comparable.class.getName());
+// EasyMock.expectLastCall();
+// EasyMock.replay(zk);
+//
+// FindInZooKeeperCustomizer fc = new FindInZooKeeperCustomizer(bc, zk);
+//
+// // ---------------------------------------------------------------
+// // Test the addingService APIs
+// // ---------------------------------------------------------------
+//
+// assertEquals("Precondition failed", 0, fc.watchers.size());
+// fc.addingService(sr);
+// assertEquals(1, fc.watchers.size());
+//
+// DiscoveredServiceTracker key = fc.watchers.keySet().iterator().next();
+// assertSame(dst, key);
+// List<InterfaceMonitor> dmList = fc.watchers.get(key);
+// assertEquals(1, dmList.size());
+// InterfaceMonitor dm = dmList.iterator().next();
+// assertNotNull(dm.listener);
+// assertSame(zk, dm.zookeeper);
+// assertEquals(Utils.getZooKeeperPath(String.class.getName()), dm.znode);
+//
+// assertEquals("Precondition failed", 1, fc.watchers.size());
+// fc.addingService(sr2);
+// assertEquals(2, fc.watchers.size());
+//
+// assertTrue(fc.watchers.containsKey(dst));
+// assertTrue(fc.watchers.containsKey(dst2));
+// assertEquals(dmList, fc.watchers.get(dst));
+// List<InterfaceMonitor> dmList2 = fc.watchers.get(dst2);
+// assertEquals(2, dmList2.size());
+// Set<String> actual = new HashSet<String>();
+// for (InterfaceMonitor im : dmList2) {
+// actual.add(im.znode);
+// }
+// Set<String> expected = new HashSet<String>(Arrays.asList(
+// Utils.getZooKeeperPath(Integer.class.getName()),
+// Utils.getZooKeeperPath(Comparable.class.getName())));
+// assertEquals(expected, actual);
+//
+// EasyMock.verify(zk);
+//
+// // ---------------------------------------------------------------
+// // Test the modifiedService APIs
+// // ---------------------------------------------------------------
+// EasyMock.reset(zk);
+// zkExpectExists(zk, List.class.getName());
+// EasyMock.replay(zk);
+//
+// EasyMock.reset(sr);
+// Set<String> listFilter = Collections.singleton("(objectClass=java.util.List)");
+// EasyMock.expect(sr.getProperty(DiscoveredServiceTracker.INTERFACE_MATCH_CRITERIA))
+// .andReturn(null);
+// EasyMock.expect(sr.getProperty(DiscoveredServiceTracker.FILTER_MATCH_CRITERIA))
+// .andReturn(listFilter);
+// EasyMock.replay(sr);
+//
+// assertEquals("Precondition failed", 2, fc.watchers.size());
+// fc.modifiedService(sr, dst);
+// assertEquals("Precondition failed", 2, fc.watchers.size());
+//
+// assertTrue(fc.watchers.containsKey(dst));
+// assertTrue(fc.watchers.containsKey(dst2));
+// assertEquals(dmList2, fc.watchers.get(dst2));
+// List<InterfaceMonitor> dmList3 = fc.watchers.get(dst);
+// assertEquals(1, dmList3.size());
+// assertEquals(Utils.getZooKeeperPath(List.class.getName()), dmList3.iterator().next().znode);
+//
+// EasyMock.verify(zk);
+//
+// // ---------------------------------------------------------------
+// // Test the removedService APIs
+// // ---------------------------------------------------------------
+// EasyMock.reset(zk);
+// EasyMock.replay(zk);
+//
+// assertEquals("Precondition failed", 2, fc.watchers.size());
+// fc.removedService(sr2, dst2);
+// assertEquals("Precondition failed", 1, fc.watchers.size());
+//
+// assertEquals(dmList3, fc.watchers.get(dst));
+// assertNull(fc.watchers.get(dst2));
+//
+// EasyMock.verify(zk);
+// }
+//
+// public void testGetInterfacesFromFilter() {
+// testGetInterfacesFromFilter("objectClass=org.apache_2.Some$FunnyClass",
+// "org.apache_2.Some$FunnyClass");
+// testGetInterfacesFromFilter("(&(a=b)(objectClass = org.acme.Q)",
+// "org.acme.Q");
+// testGetInterfacesFromFilter("(&(objectClassIdentifier=b)(objectClass = org.acme.Q)",
+// "org.acme.Q");
+// testGetInterfacesFromFilter("(|(OBJECTCLASS= X )(objectclass = Y)",
+// "X", "Y");
+// testGetInterfacesFromFilter(new String[] {"(objectClass=X)", "(objectClass=Y)"},
+// "X", "Y");
+// }
+//
+// private void testGetInterfacesFromFilter(String filter, String ... interfaces) {
+// testGetInterfacesFromFilter(new String[] {filter}, interfaces);
+// }
+//
+// private void testGetInterfacesFromFilter(String[] filters, String ... interfaces) {
+// FindInZooKeeperCustomizer.getInterfacesFromFilter(Arrays.asList(filters));
+// }
+//
+// private void zkExpectExists(ZooKeeper zk, String className) {
+// zk.exists(EasyMock.eq(Utils.getZooKeeperPath(className)),
+// (Watcher) EasyMock.anyObject(),
+// (StatCallback) EasyMock.anyObject(), EasyMock.isNull());
+// EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+// public Object answer() throws Throwable {
+// assertEquals(EasyMock.getCurrentArguments()[1],
+// EasyMock.getCurrentArguments()[2]);
+// return null;
+// }
+// });
+// }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/InterfaceDataMonitorListenerImplTest.java b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/InterfaceDataMonitorListenerImplTest.java
new file mode 100644
index 0000000..53b6139
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/InterfaceDataMonitorListenerImplTest.java
@@ -0,0 +1,183 @@
+/**
+ * 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.discovery.zookeeper;
+
+import junit.framework.TestCase;
+
+public class InterfaceDataMonitorListenerImplTest extends TestCase {
+
+ public void testDUMMY() {
+ assertTrue(true);
+ }
+
+// public void testChange() throws Exception {
+// final List<DiscoveredServiceNotification> dsnCallbacks = new ArrayList<DiscoveredServiceNotification>();
+// DiscoveredServiceTracker dst = new DiscoveredServiceTracker() {
+// public void serviceChanged(DiscoveredServiceNotification dsn) {
+// dsnCallbacks.add(dsn);
+// }
+// };
+//
+// //----------------------------------------------------------------
+// // Test DiscoveredServiceNotification.AVAILABLE
+// //----------------------------------------------------------------
+// Properties initial = new Properties();
+// initial.put("a", "b");
+// initial.put(ServicePublication.ENDPOINT_LOCATION, "http://somehost:12345/some/context");
+// ByteArrayOutputStream propBytes = new ByteArrayOutputStream();
+// initial.store(propBytes, "");
+//
+// ZooKeeper zk = EasyMock.createMock(ZooKeeper.class);
+// EasyMock.expect(zk.getChildren(Utils.getZooKeeperPath(String.class.getName()), false))
+// .andReturn(Arrays.asList("x#y#z"));
+// EasyMock.expect(zk.getData(Utils.getZooKeeperPath(String.class.getName()) + "/x#y#z", false, null))
+// .andReturn(propBytes.toByteArray());
+// EasyMock.replay(zk);
+//
+// InterfaceDataMonitorListenerImpl dml = new InterfaceDataMonitorListenerImpl(zk, String.class.getName(), dst);
+//
+// assertEquals("Precondition failed", 0, dsnCallbacks.size());
+// dml.change();
+// assertEquals(1, dsnCallbacks.size());
+// DiscoveredServiceNotification dsn = dsnCallbacks.iterator().next();
+// assertEquals(Collections.singleton(String.class.getName()), dsn.getInterfaces());
+// assertEquals(DiscoveredServiceNotification.AVAILABLE, dsn.getType());
+// assertEquals(0, dsn.getFilters().size());
+// ServiceEndpointDescription sed = dsn.getServiceEndpointDescription();
+// assertEquals(Collections.singleton(String.class.getName()), sed.getProvidedInterfaces());
+//
+// Properties expected = new Properties();
+// expected.load(new ByteArrayInputStream(propBytes.toByteArray()));
+// expected.put("service.exported.configs", "org.apache.cxf.ws");
+// expected.put("org.apache.cxf.ws.address", "http://somehost:12345/some/context");
+//
+// assertEquals(expected, sed.getProperties());
+// EasyMock.verify(zk);
+//
+// // Again with the same data
+// EasyMock.reset(zk);
+// EasyMock.expect(zk.getChildren(Utils.getZooKeeperPath(String.class.getName()), false))
+// .andReturn(Arrays.asList("x#y#z"));
+// EasyMock.expect(zk.getData(Utils.getZooKeeperPath(String.class.getName()) + "/x#y#z", false, null))
+// .andReturn(propBytes.toByteArray());
+// EasyMock.replay(zk);
+//
+// dsnCallbacks.clear();
+// assertEquals("Precondition failed", 0, dsnCallbacks.size());
+// dml.change();
+// assertEquals(0, dsnCallbacks.size());
+//
+// EasyMock.verify(zk);
+// //----------------------------------------------------------------
+// // Test DiscoveredServiceNotification.MODIFIED
+// //----------------------------------------------------------------
+// Properties modified = new Properties();
+// modified.put("c", "d");
+// modified.put(ServicePublication.ENDPOINT_LOCATION, "http://somehost:999/some/context");
+// modified.put("service.exported.configs", "org.apache.cxf.rs");
+// ByteArrayOutputStream modBytes = new ByteArrayOutputStream();
+// modified.store(modBytes, "");
+//
+// EasyMock.reset(zk);
+// EasyMock.expect(zk.getChildren(Utils.getZooKeeperPath(String.class.getName()), false))
+// .andReturn(Arrays.asList("x#y#z"));
+// EasyMock.expect(zk.getData(Utils.getZooKeeperPath(String.class.getName()) + "/x#y#z", false, null))
+// .andReturn(modBytes.toByteArray());
+// EasyMock.replay(zk);
+//
+// dsnCallbacks.clear();
+// assertEquals("Precondition failed", 0, dsnCallbacks.size());
+// dml.change();
+// assertEquals(1, dsnCallbacks.size());
+// DiscoveredServiceNotification dsn2 = dsnCallbacks.iterator().next();
+// assertEquals(Collections.singleton(String.class.getName()), dsn2.getInterfaces());
+// assertEquals(DiscoveredServiceNotification.MODIFIED, dsn2.getType());
+// assertEquals(0, dsn2.getFilters().size());
+// ServiceEndpointDescription sed2 = dsn2.getServiceEndpointDescription();
+// assertEquals(Collections.singleton(String.class.getName()), sed2.getProvidedInterfaces());
+// assertEquals(modified, sed2.getProperties());
+//
+// EasyMock.verify(zk);
+//
+// //----------------------------------------------------------------
+// // Test DiscoveredServiceNotification.MODIFIED2
+// //----------------------------------------------------------------
+// Properties modified2 = new Properties();
+// modified2.put("c", "d2");
+// modified2.put(ServicePublication.ENDPOINT_LOCATION, "http://somehost:112/some/context");
+// modified2.put("service.exported.configs", "org.apache.cxf.ws");
+// modified2.put("org.apache.cxf.ws.address", "http://somewhereelse/123");
+// ByteArrayOutputStream modBytes2 = new ByteArrayOutputStream();
+// modified2.store(modBytes2, "");
+//
+// EasyMock.reset(zk);
+// EasyMock.expect(zk.getChildren(Utils.getZooKeeperPath(String.class.getName()), false))
+// .andReturn(Arrays.asList("x#y#z"));
+// EasyMock.expect(zk.getData(Utils.getZooKeeperPath(String.class.getName()) + "/x#y#z", false, null))
+// .andReturn(modBytes2.toByteArray());
+// EasyMock.replay(zk);
+//
+// dsnCallbacks.clear();
+// assertEquals("Precondition failed", 0, dsnCallbacks.size());
+// dml.change();
+// assertEquals(1, dsnCallbacks.size());
+// DiscoveredServiceNotification dsn3 = dsnCallbacks.iterator().next();
+// assertEquals(Collections.singleton(String.class.getName()), dsn3.getInterfaces());
+// assertEquals(DiscoveredServiceNotification.MODIFIED, dsn3.getType());
+// assertEquals(0, dsn3.getFilters().size());
+// ServiceEndpointDescription sed3 = dsn3.getServiceEndpointDescription();
+// assertEquals(Collections.singleton(String.class.getName()), sed3.getProvidedInterfaces());
+// assertEquals(modified2, sed3.getProperties());
+//
+// EasyMock.verify(zk);
+// //----------------------------------------------------------------
+// // Test DiscoveredServiceNotification.UNAVAILABLE
+// //----------------------------------------------------------------
+// EasyMock.reset(zk);
+// EasyMock.expect(zk.getChildren(Utils.getZooKeeperPath(String.class.getName()), false))
+// .andReturn(Collections.<String>emptyList());
+// EasyMock.replay(zk);
+//
+// dsnCallbacks.clear();
+// assertEquals("Precondition failed", 0, dsnCallbacks.size());
+// dml.change();
+// assertEquals(1, dsnCallbacks.size());
+// DiscoveredServiceNotification dsn4 = dsnCallbacks.iterator().next();
+// assertEquals(Collections.singleton(String.class.getName()), dsn4.getInterfaces());
+// assertEquals(DiscoveredServiceNotification.UNAVAILABLE, dsn4.getType());
+// assertEquals(0, dsn4.getFilters().size());
+// ServiceEndpointDescription sed4 = dsn4.getServiceEndpointDescription();
+// assertEquals(Collections.singleton(String.class.getName()), sed4.getProvidedInterfaces());
+// assertEquals(modified2, sed4.getProperties());
+//
+// EasyMock.verify(zk);
+//
+// // Try the same again...
+// EasyMock.reset(zk);
+// EasyMock.expect(zk.getChildren(Utils.getZooKeeperPath(String.class.getName()), false))
+// .andReturn(Collections.<String>emptyList());
+// EasyMock.replay(zk);
+//
+// dsnCallbacks.clear();
+// assertEquals("Precondition failed", 0, dsnCallbacks.size());
+// dml.change();
+// assertEquals("Should not receive a callback again...", 0, dsnCallbacks.size());
+// EasyMock.verify(zk);
+// }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerFactoryTest.java b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerFactoryTest.java
new file mode 100644
index 0000000..e63b805
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerFactoryTest.java
@@ -0,0 +1,100 @@
+/**
+ * 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.discovery.zookeeper.publish;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.apache.zookeeper.ZooKeeper;
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+
+public class PublishingEndpointListenerFactoryTest extends TestCase {
+
+ @SuppressWarnings("unchecked")
+ public void testScope() {
+ IMocksControl c = EasyMock.createNiceControl();
+
+ BundleContext ctx = c.createMock(BundleContext.class);
+ ZooKeeper zk = c.createMock(ZooKeeper.class);
+ @SuppressWarnings("rawtypes")
+ ServiceRegistration sreg = c.createMock(ServiceRegistration.class);
+
+ PublishingEndpointListenerFactory eplf = new PublishingEndpointListenerFactory(zk, ctx);
+
+ EasyMock.expect(ctx.registerService(EasyMock.eq(EndpointListener.class.getName()), EasyMock.eq(eplf),
+ (Dictionary<String, String>)EasyMock.anyObject())).andReturn(sreg).once();
+
+ EasyMock.expect(ctx.getProperty(EasyMock.eq("org.osgi.framework.uuid"))).andReturn("myUUID").anyTimes();
+
+ c.replay();
+ eplf.start();
+ c.verify();
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testServiceFactory() {
+ IMocksControl c = EasyMock.createNiceControl();
+
+ BundleContext ctx = c.createMock(BundleContext.class);
+ ZooKeeper zk = c.createMock(ZooKeeper.class);
+ @SuppressWarnings("rawtypes")
+ ServiceRegistration sreg = c.createMock(ServiceRegistration.class);
+
+ PublishingEndpointListenerFactory eplf = new PublishingEndpointListenerFactory(zk, ctx);
+
+ EasyMock.expect(ctx.registerService(EasyMock.eq(EndpointListener.class.getName()), EasyMock.eq(eplf),
+ (Dictionary<String, String>)EasyMock.anyObject())).andReturn(sreg).once();
+
+ EasyMock.expect(ctx.getProperty(EasyMock.eq("org.osgi.framework.uuid"))).andReturn("myUUID").anyTimes();
+
+ PublishingEndpointListener eli = c.createMock(PublishingEndpointListener.class);
+ eli.close();
+ EasyMock.expectLastCall().once();
+
+ c.replay();
+ eplf.start();
+
+ PublishingEndpointListener service = eplf.getService(null, null);
+ assertNotNull(service);
+ assertTrue(service instanceof EndpointListener);
+
+ List<PublishingEndpointListener> listeners = eplf.getListeners();
+ assertEquals(1, listeners.size());
+ assertEquals(service, listeners.get(0));
+
+ eplf.ungetService(null, null, service);
+ listeners = eplf.getListeners();
+ assertEquals(0, listeners.size());
+
+ eplf.ungetService(null, null, eli); // no call to close
+ listeners.add(eli);
+ eplf.ungetService(null, null, eli); // call to close
+ listeners = eplf.getListeners();
+ assertEquals(0, listeners.size());
+
+ c.verify();
+ }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerTest.java b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerTest.java
new file mode 100644
index 0000000..c61fc9d
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/publish/PublishingEndpointListenerTest.java
@@ -0,0 +1,200 @@
+/**
+ * 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.discovery.zookeeper.publish;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.cxf.dosgi.endpointdesc.EndpointDescriptionParser;
+import org.apache.cxf.dosgi.endpointdesc.PropertiesMapper;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.easymock.IAnswer;
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+import org.osgi.xmlns.rsa.v1_0.EndpointDescriptionType;
+import org.osgi.xmlns.rsa.v1_0.PropertyType;
+
+public class PublishingEndpointListenerTest extends TestCase {
+
+ 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();
+
+ 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);
+
+ eli.endpointAdded(endpoint, null);
+ eli.endpointAdded(endpoint, null); // should do nothing
+
+ eli.endpointRemoved(endpoint, null);
+ eli.endpointRemoved(endpoint, null); // should do nothing
+
+ c.verify();
+ }
+
+ 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();
+ 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();
+ 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);
+
+ Map<String, Object> expectedProps = new HashMap<String, Object>(props);
+ expectedProps.put("endpoint.id", "http://localhost:9876/test/appended");
+ expectedProps.put("foo", "bar");
+ expectedProps.put("service.imported", "true");
+
+ final ZooKeeper zk = EasyMock.createNiceMock(ZooKeeper.class);
+ String expectedFullPath = "/osgi/service_registry/org/foo/myClass/some.machine#9876##test";
+
+ List<PropertyType> props2 = new PropertiesMapper().fromProps(expectedProps);
+ 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("");
+ EasyMock.replay(zk);
+
+ PublishingEndpointListener eli = new PublishingEndpointListener(zk, ctx);
+
+ List<EndpointDescription> endpoints = getEndpoints(eli);
+ assertEquals("Precondition", 0, endpoints.size());
+ eli.endpointAdded(endpoint, null);
+ assertEquals(1, endpoints.size());
+
+ 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();
+
+ 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);
+
+ eli.endpointAdded(endpoint, null);
+
+ eli.close(); // should result in zk.delete(...)
+
+ c.verify();
+ }
+
+ public void testGetKey() throws Exception {
+ assertEquals("somehost#9090##org#example#TestEndpoint",
+ PublishingEndpointListener.getKey("http://somehost:9090/org/example/TestEndpoint"));
+ }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorManagerTest.java b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorManagerTest.java
new file mode 100644
index 0000000..7d6f67f
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorManagerTest.java
@@ -0,0 +1,139 @@
+/**
+ * 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.discovery.zookeeper.subscribe;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.zookeeper.ZooKeeper;
+import org.easymock.IAnswer;
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+
+import static org.junit.Assert.assertEquals;
+
+public class InterfaceMonitorManagerTest {
+
+ @Test
+ public void testEndpointListenerTrackerCustomizer() {
+ IMocksControl c = EasyMock.createNiceControl();
+
+ BundleContext ctx = c.createMock(BundleContext.class);
+ ZooKeeper zk = c.createMock(ZooKeeper.class);
+
+ @SuppressWarnings("unchecked")
+ ServiceReference<EndpointListener> sref = c.createMock(ServiceReference.class);
+ @SuppressWarnings("unchecked")
+ ServiceReference<EndpointListener> sref2 = c.createMock(ServiceReference.class);
+
+ final Map<String, ?> p = new HashMap<String, Object>();
+
+ EasyMock.expect(sref.getPropertyKeys()).andAnswer(new IAnswer<String[]>() {
+ public String[] answer() throws Throwable {
+ return p.keySet().toArray(new String[p.size()]);
+ }
+ }).anyTimes();
+
+ EasyMock.expect(sref.getProperty((String)EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ String key = (String)(EasyMock.getCurrentArguments()[0]);
+ return p.get(key);
+ }
+ }).anyTimes();
+
+ EasyMock.expect(sref2.getPropertyKeys()).andAnswer(new IAnswer<String[]>() {
+ public String[] answer() throws Throwable {
+ return p.keySet().toArray(new String[p.size()]);
+ }
+ }).anyTimes();
+
+ EasyMock.expect(sref2.getProperty((String)EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ String key = (String)(EasyMock.getCurrentArguments()[0]);
+ return p.get(key);
+ }
+ }).anyTimes();
+
+ final List<IMocksControl> controls = new ArrayList<IMocksControl>();
+
+ InterfaceMonitorManager eltc = new InterfaceMonitorManager(ctx, zk);
+
+ c.replay();
+
+ // sref has no scope -> nothing should happen
+
+ assertEquals(0, eltc.getEndpointListenerScopes().size());
+ assertEquals(0, eltc.getInterests().size());
+
+ //p.put(EndpointListener.ENDPOINT_LISTENER_SCOPE, );
+
+ eltc.addInterest(sref, "(objectClass=mine)", "mine");
+
+ assertEquals(1, eltc.getEndpointListenerScopes().size());
+ assertEquals(1, eltc.getEndpointListenerScopes().get(sref).size());
+ assertEquals("(objectClass=mine)", eltc.getEndpointListenerScopes().get(sref).get(0));
+ assertEquals(1, eltc.getInterests().size());
+
+ eltc.addInterest(sref, "(objectClass=mine)", "mine");
+
+ assertEquals(1, eltc.getEndpointListenerScopes().size());
+ assertEquals(1, eltc.getEndpointListenerScopes().get(sref).size());
+ assertEquals("(objectClass=mine)", eltc.getEndpointListenerScopes().get(sref).get(0));
+ assertEquals(1, eltc.getInterests().size());
+
+ eltc.addInterest(sref2, "(objectClass=mine)", "mine");
+
+ assertEquals(2, eltc.getEndpointListenerScopes().size());
+ assertEquals(1, eltc.getEndpointListenerScopes().get(sref).size());
+ assertEquals(1, eltc.getEndpointListenerScopes().get(sref2).size());
+ assertEquals("(objectClass=mine)", eltc.getEndpointListenerScopes().get(sref).get(0));
+ assertEquals("(objectClass=mine)", eltc.getEndpointListenerScopes().get(sref2).get(0));
+ assertEquals(1, eltc.getInterests().size());
+
+ eltc.removeInterest(sref);
+
+ assertEquals(1, eltc.getEndpointListenerScopes().size());
+ assertEquals(1, eltc.getEndpointListenerScopes().get(sref2).size());
+ assertEquals("(objectClass=mine)", eltc.getEndpointListenerScopes().get(sref2).get(0));
+ assertEquals(1, eltc.getInterests().size());
+
+ eltc.removeInterest(sref);
+
+ assertEquals(1, eltc.getEndpointListenerScopes().size());
+ assertEquals(1, eltc.getEndpointListenerScopes().get(sref2).size());
+ assertEquals("(objectClass=mine)", eltc.getEndpointListenerScopes().get(sref2).get(0));
+ assertEquals(1, eltc.getInterests().size());
+
+ eltc.removeInterest(sref2);
+
+ assertEquals(0, eltc.getEndpointListenerScopes().size());
+ assertEquals(0, eltc.getInterests().size());
+
+ c.verify();
+ for (IMocksControl control : controls) {
+ control.verify();
+ }
+ }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorTest.java b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorTest.java
new file mode 100644
index 0000000..67afb16
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/subscribe/InterfaceMonitorTest.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.cxf.dosgi.discovery.zookeeper.subscribe;
+
+import java.util.Collections;
+
+import junit.framework.TestCase;
+
+import org.apache.cxf.dosgi.discovery.zookeeper.util.Utils;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher.Event.EventType;
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.data.Stat;
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+
+public class InterfaceMonitorTest extends TestCase {
+
+ public void testInterfaceMonitor() throws KeeperException, InterruptedException {
+ IMocksControl c = EasyMock.createControl();
+
+ ZooKeeper zk = c.createMock(ZooKeeper.class);
+ expect(zk.getState()).andReturn(ZooKeeper.States.CONNECTED).anyTimes();
+
+ String scope = "(myProp=test)";
+ String interf = "es.schaaf.test";
+ String node = Utils.getZooKeeperPath(interf);
+
+ EndpointListener endpointListener = c.createMock(EndpointListener.class);
+ InterfaceMonitor im = new InterfaceMonitor(zk, interf, endpointListener, scope);
+ zk.exists(eq(node), eq(im), eq(im), EasyMock.anyObject());
+ EasyMock.expectLastCall().once();
+
+ expect(zk.exists(eq(node), eq(false))).andReturn(new Stat()).anyTimes();
+ expect(zk.getChildren(eq(node), eq(false))).andReturn(Collections.<String> emptyList()).once();
+ expect(zk.getChildren(eq(node), eq(im))).andReturn(Collections.<String> emptyList()).once();
+
+ c.replay();
+ im.start();
+ // simulate a zk callback
+ WatchedEvent we = new WatchedEvent(EventType.NodeCreated, KeeperState.SyncConnected, node);
+ im.process(we);
+ c.verify();
+ }
+}
diff --git a/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/util/UtilsTest.java b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/util/UtilsTest.java
new file mode 100644
index 0000000..08c830c
--- /dev/null
+++ b/trunk/discovery/distributed/cxf-discovery/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/util/UtilsTest.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.discovery.zookeeper.util;
+
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+
+public class UtilsTest extends TestCase {
+
+ public void testGetZooKeeperPath() {
+ assertEquals(Utils.PATH_PREFIX + '/' + "org/example/Test",
+ Utils.getZooKeeperPath("org.example.Test"));
+
+ // used for the recursive discovery
+ assertEquals(Utils.PATH_PREFIX, Utils.getZooKeeperPath(null));
+ assertEquals(Utils.PATH_PREFIX, Utils.getZooKeeperPath(""));
+ }
+
+ public void testGetStringPlusProperty() {
+ String[] out = Utils.getStringPlusProperty("MyString");
+ assertEquals(1, out.length);
+ assertEquals("MyString", out[0]);
+
+ out = Utils.getStringPlusProperty(new String[]{"MyString"});
+ assertEquals(1, out.length);
+ assertEquals("MyString", out[0]);
+
+ out = Utils.getStringPlusProperty(Arrays.asList("MyString"));
+ assertEquals(1, out.length);
+ assertEquals("MyString", out[0]);
+
+ out = Utils.getStringPlusProperty(Arrays.asList(1));
+ assertEquals(0, out.length);
+
+ out = Utils.getStringPlusProperty(new Object());
+ assertEquals(0, out.length);
+
+ out = Utils.getStringPlusProperty(null);
+ assertEquals(0, out.length);
+ }
+
+ public void testRemoveEmpty() {
+ String[] out = Utils.removeEmpty(new String[0]);
+ assertEquals(0, out.length);
+
+ out = Utils.removeEmpty(new String[]{null});
+ assertEquals(0, out.length);
+
+ out = Utils.removeEmpty(new String[]{""});
+ assertEquals(0, out.length);
+
+ out = Utils.removeEmpty(new String[]{"hi"});
+ assertEquals(1, out.length);
+ assertEquals("hi", out[0]);
+
+ out = Utils.removeEmpty(new String[]{"", "hi", null});
+ assertEquals(1, out.length);
+ assertEquals("hi", out[0]);
+
+ out = Utils.removeEmpty(new String[]{"hi", null, "", ""});
+ assertEquals(1, out.length);
+ assertEquals("hi", out[0]);
+
+ out = Utils.removeEmpty(new String[]{"", "hi", null, "", "", "bye", null});
+ assertEquals(2, out.length);
+ assertEquals("hi", out[0]);
+ assertEquals("bye", out[1]);
+ }
+
+ public void testGetScopes() {
+ IMocksControl c = EasyMock.createNiceControl();
+
+ String[] scopes = new String[]{"myScope=test", ""};
+
+ @SuppressWarnings("unchecked")
+ ServiceReference<EndpointListener> sref = c.createMock(ServiceReference.class);
+ EasyMock.expect(sref.getProperty(EasyMock.eq(EndpointListener.ENDPOINT_LISTENER_SCOPE)))
+ .andReturn(scopes).anyTimes();
+
+ c.replay();
+
+ String[] ret = Utils.getScopes(sref);
+
+ c.verify();
+ assertEquals(1, ret.length);
+ assertEquals(scopes[0], ret[0]);
+ }
+}
diff --git a/trunk/discovery/distributed/pom.xml b/trunk/discovery/distributed/pom.xml
new file mode 100644
index 0000000..61f06f0
--- /dev/null
+++ b/trunk/discovery/distributed/pom.xml
@@ -0,0 +1,42 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-discovery-distributed-parent</artifactId>
+ <version>1.6.0</version>
+ <packaging>pom</packaging>
+ <name>Distributed OSGI Distributed Discovery Parent</name>
+ <url>http://cxf.apache.org</url>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <modules>
+ <module>zookeeper-server</module>
+ <module>zookeeper-server-config</module>
+ <module>cxf-discovery</module>
+ </modules>
+</project>
diff --git a/trunk/discovery/distributed/zookeeper-server-config/pom.xml b/trunk/discovery/distributed/zookeeper-server-config/pom.xml
new file mode 100644
index 0000000..0b3fbfc
--- /dev/null
+++ b/trunk/discovery/distributed/zookeeper-server-config/pom.xml
@@ -0,0 +1,76 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-discovery-distributed-zookeeper-server-config</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Distributed Discovery ZooKeeper Server Control Configuration Bundle</name>
+ <description>This bundle provides some default OSGi Configuration Admin data and is only useful for use in the OSGi CT suite</description>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>../../..</topDirectoryLocation>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>ZooKeeper server configuration bundle</Bundle-Name>
+ <Bundle-Description>Configures the ZooKeeper server in an OSGi Framework</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Activator>org.apache.cxf.dosgi.discovery.zookeeper.server.config.Activator</Bundle-Activator>
+ <Import-Package>*</Import-Package>
+ <Private-Package>org.apache.cxf.dosgi.discovery.zookeeper.server.config</Private-Package>
+ <Export-Package />
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/discovery/distributed/zookeeper-server-config/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/config/Activator.java b/trunk/discovery/distributed/zookeeper-server-config/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/config/Activator.java
new file mode 100644
index 0000000..cbe0e79
--- /dev/null
+++ b/trunk/discovery/distributed/zookeeper-server-config/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/config/Activator.java
@@ -0,0 +1,113 @@
+/**
+ * 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.discovery.zookeeper.server.config;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+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 ServiceTracker st;
+
+ public void start(BundleContext context) throws Exception {
+ synchronized (Activator.class) {
+ // Only one thread gets to set the port number
+ if (System.getProperty(ZOOKEEPER_PORT) == null) {
+ String port = getFreePort();
+ System.setProperty(ZOOKEEPER_PORT, port);
+ LOG.info("Global ZooKeeper port: {}", port);
+ }
+ }
+
+ st = new ServiceTracker(context, ConfigurationAdmin.class.getName(), null) {
+ @Override
+ public Object addingService(ServiceReference reference) {
+ Object svc = super.addingService(reference);
+ if (svc instanceof ConfigurationAdmin) {
+ try {
+ ConfigurationAdmin cadmin = (ConfigurationAdmin) svc;
+ Configuration cfg = cadmin.getConfiguration(PID, null);
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ String zp = System.getProperty(ZOOKEEPER_PORT);
+ props.put("clientPort", zp);
+ cfg.update(props);
+ LOG.debug("Set ZooKeeper client port to {}", zp);
+ } catch (IOException e) {
+ LOG.error("Failed to configure ZooKeeper server!", e);
+ }
+ }
+ return svc;
+ }
+ };
+ st.open();
+
+ // The following section is done synchronously otherwise it doesn't happen in time for the CT
+ ServiceReference[] refs = context.getServiceReferences(ManagedService.class.getName(),
+ "(service.pid=org.apache.cxf.dosgi.discovery.zookeeper)");
+ if (refs == null || refs.length == 0) {
+ throw new RuntimeException("This bundle must be started after the bundle with the ZooKeeper "
+ + "Discovery Managed Service was started.");
+ }
+
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put("zookeeper.host", "127.0.0.1");
+ props.put("zookeeper.port", System.getProperty(ZOOKEEPER_PORT));
+
+ ManagedService ms = (ManagedService) context.getService(refs[0]);
+ try {
+ ms.updated(props);
+ } finally {
+ if (ms != null) {
+ context.ungetService(refs[0]);
+ }
+ }
+ LOG.debug("Passed the zookeeper.host property to the ZooKeeper Client managed service.");
+ }
+
+ private String getFreePort() {
+ try {
+ ServerSocket ss = new ServerSocket(0);
+ String port = "" + ss.getLocalPort();
+ ss.close();
+ return port;
+ } catch (IOException e) {
+ LOG.error("Failed to find a free port!", e);
+ return null;
+ }
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ st.close();
+ }
+}
diff --git a/trunk/discovery/distributed/zookeeper-server/pom.xml b/trunk/discovery/distributed/zookeeper-server/pom.xml
new file mode 100644
index 0000000..a37706b
--- /dev/null
+++ b/trunk/discovery/distributed/zookeeper-server/pom.xml
@@ -0,0 +1,101 @@
+<?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>
+ <artifactId>cxf-dosgi-ri-discovery-distributed-zookeeper-server</artifactId>
+ <packaging>bundle</packaging>
+ <name>CXF DOSGi ZooKeeper Server Control Bundle</name>
+ <description>Runs the ZooKeeper server</description>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>../../..</topDirectoryLocation>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </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>
+ </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>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Activator>org.apache.cxf.dosgi.discovery.zookeeper.server.Activator</Bundle-Activator>
+ <Export-Package>
+ !*
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/discovery/distributed/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/Activator.java b/trunk/discovery/distributed/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/Activator.java
new file mode 100644
index 0000000..6adf700
--- /dev/null
+++ b/trunk/discovery/distributed/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/Activator.java
@@ -0,0 +1,44 @@
+/**
+ * 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.discovery.zookeeper.server;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+public class Activator implements BundleActivator {
+
+ ZookeeperStarter zkStarter;
+
+ 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");
+ context.registerService(org.osgi.service.cm.ManagedService.class.getName(), zkStarter, props);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ if (zkStarter != null) {
+ zkStarter.shutdown();
+ }
+ }
+}
diff --git a/trunk/discovery/distributed/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/ZookeeperStarter.java b/trunk/discovery/distributed/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/ZookeeperStarter.java
new file mode 100644
index 0000000..4bf772a
--- /dev/null
+++ b/trunk/discovery/distributed/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/ZookeeperStarter.java
@@ -0,0 +1,156 @@
+/**
+ * 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.discovery.zookeeper.server;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Dictionary;
+
+import org.apache.cxf.dosgi.discovery.zookeeper.server.util.Utils;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.zookeeper.server.ServerConfig;
+import org.apache.zookeeper.server.ZooKeeperServerMain;
+import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
+import org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException;
+import org.apache.zookeeper.server.quorum.QuorumPeerMain;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationException;
+
+public class ZookeeperStarter implements org.osgi.service.cm.ManagedService {
+
+ private static final Logger LOG = Logger.getLogger(ZookeeperStarter.class); //NOPMD - using log4j here
+
+ protected ZookeeperServer main;
+ private final BundleContext bundleContext;
+ private Thread zkMainThread;
+
+ public ZookeeperStarter(BundleContext ctx) {
+ bundleContext = ctx;
+ }
+
+ synchronized void shutdown() {
+ if (main != null) {
+ LOG.info("Shutting down ZooKeeper server");
+ try {
+ main.shutdown();
+ if (zkMainThread != null) {
+ zkMainThread.join();
+ }
+ } catch (Throwable e) {
+ LOG.log(Level.ERROR, e.getMessage(), e);
+ }
+ main = null;
+ zkMainThread = null;
+ }
+ }
+
+ private void setDefaults(Dictionary<String, String> dict) throws IOException {
+ Utils.removeEmptyValues(dict); // to avoid NumberFormatExceptions
+ Utils.setDefault(dict, "tickTime", "2000");
+ Utils.setDefault(dict, "initLimit", "10");
+ Utils.setDefault(dict, "syncLimit", "5");
+ Utils.setDefault(dict, "clientPort", "2181");
+ Utils.setDefault(dict, "dataDir", new File(bundleContext.getDataFile(""), "zkdata").getCanonicalPath());
+ }
+
+ @SuppressWarnings("unchecked")
+ public synchronized void updated(Dictionary<String, ?> dict) throws ConfigurationException {
+ shutdown();
+ if (dict == null) {
+ return;
+ }
+ try {
+ setDefaults((Dictionary<String, String>)dict);
+ QuorumPeerConfig config = parseConfig(dict);
+ startFromConfig(config);
+ LOG.info("Applied configuration update: " + dict);
+ } catch (Exception th) {
+ LOG.error("Problem applying configuration update: " + dict, th);
+ }
+ }
+
+ private QuorumPeerConfig parseConfig(Dictionary<String, ?> dict) throws IOException, ConfigException {
+ QuorumPeerConfig config = new QuorumPeerConfig();
+ config.parseProperties(Utils.toProperties(dict));
+ return config;
+ }
+
+ protected void startFromConfig(final QuorumPeerConfig config) {
+ int numServers = config.getServers().size();
+ main = numServers > 1 ? new MyQuorumPeerMain(config) : new MyZooKeeperServerMain(config);
+ zkMainThread = new Thread(new Runnable() {
+ public void run() {
+ try {
+ main.startup();
+ } catch (Throwable e) {
+ LOG.error("Problem running ZooKeeper server.", e);
+ }
+ }
+ });
+ zkMainThread.start();
+ }
+
+ interface ZookeeperServer {
+ void startup() throws IOException;
+ void shutdown();
+ }
+
+ static class MyQuorumPeerMain extends QuorumPeerMain implements ZookeeperServer {
+
+ private QuorumPeerConfig config;
+
+ public MyQuorumPeerMain(QuorumPeerConfig config) {
+ this.config = config;
+ }
+
+ public void startup() throws IOException {
+ runFromConfig(config);
+ }
+
+ public void shutdown() {
+ if (null != quorumPeer) {
+ quorumPeer.shutdown();
+ }
+ }
+ }
+
+ static class MyZooKeeperServerMain extends ZooKeeperServerMain implements ZookeeperServer {
+
+ private QuorumPeerConfig config;
+
+ public MyZooKeeperServerMain(QuorumPeerConfig config) {
+ this.config = config;
+ }
+
+ public void startup() throws IOException {
+ ServerConfig serverConfig = new ServerConfig();
+ serverConfig.readFrom(config);
+ runFromConfig(serverConfig);
+ }
+
+ public void shutdown() {
+ try {
+ super.shutdown();
+ } catch (Exception e) {
+ LOG.error("Error shutting down ZooKeeper", e);
+ }
+ }
+ }
+}
diff --git a/trunk/discovery/distributed/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/util/Utils.java b/trunk/discovery/distributed/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/util/Utils.java
new file mode 100644
index 0000000..7359199
--- /dev/null
+++ b/trunk/discovery/distributed/zookeeper-server/src/main/java/org/apache/cxf/dosgi/discovery/zookeeper/server/util/Utils.java
@@ -0,0 +1,86 @@
+/**
+ * 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.discovery.zookeeper.server.util;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * General purpose utility methods.
+ */
+public final class Utils {
+
+ private Utils() {
+ // prevent instantiation
+ }
+
+ /**
+ * Remove entries whose values are empty from the given dictionary.
+ *
+ * @param dict a dictionary
+ */
+ public static void removeEmptyValues(Dictionary<String, ?> dict) {
+ List<String> keysToRemove = new ArrayList<String>();
+ Enumeration<String> keys = dict.keys();
+ while (keys.hasMoreElements()) {
+ String key = keys.nextElement();
+ Object value = dict.get(key);
+ if (value instanceof String && "".equals(value)) {
+ keysToRemove.add(key);
+ }
+ }
+ for (String key : keysToRemove) {
+ dict.remove(key);
+ }
+ }
+
+ /**
+ * Puts the given key-value pair in the given dictionary if the key does not
+ * already exist in it or if its existing value is null.
+ *
+ * @param dict a dictionary
+ * @param key the key
+ * @param value the default value to set
+ */
+ public static void setDefault(Dictionary<String, String> dict, String key, String value) {
+ if (dict.get(key) == null) {
+ dict.put(key, value);
+ }
+ }
+
+ /**
+ * Converts a Dictionary into a Properties instance.
+ *
+ * @param dict a dictionary
+ * @param <K> the key type
+ * @param <V> the value type
+ * @return the properties
+ */
+ public static <K, V> Properties toProperties(Dictionary<K, V> dict) {
+ Properties props = new Properties();
+ for (Enumeration<K> e = dict.keys(); e.hasMoreElements();) {
+ K key = e.nextElement();
+ props.put(key, dict.get(key));
+ }
+ return props;
+ }
+}
diff --git a/trunk/discovery/distributed/zookeeper-server/src/main/resources/OSGI-INF/metatype/zookeeper.xml b/trunk/discovery/distributed/zookeeper-server/src/main/resources/OSGI-INF/metatype/zookeeper.xml
new file mode 100644
index 0000000..efd9403
--- /dev/null
+++ b/trunk/discovery/distributed/zookeeper-server/src/main/resources/OSGI-INF/metatype/zookeeper.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+<MetaData xmlns="http://www.osgi.org/xmlns/metadata/v1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+ http://www.osgi.org/xmlns/metadata/v1.0.0 http://www.osgi.org/xmlns/metatype/v1.1.0/metatype.xsd
+ ">
+ <OCD description="" name="Zookeeper server config" id="org.apache.cxf.dosgi.discovery.zookeeper.server">
+ <AD id="clientPort" required="false" type="String" default="2181" description=""/>
+ <AD id="tickTime" required="false" type="String" default="2000" description=""/>
+ <AD id="initLimit" required="false" type="String" default="10" description=""/>
+ <AD id="syncLimit" required="false" type="String" default="5" description=""/>
+ </OCD>
+ <Designate pid="org.apache.cxf.dosgi.discovery.zookeeper.server">
+ <Object ocdref="org.apache.cxf.dosgi.discovery.zookeeper.server"/>
+ </Designate>
+</MetaData>
\ No newline at end of file
diff --git a/trunk/discovery/distributed/zookeeper-server/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/server/ZookeeperStarterTest.java b/trunk/discovery/distributed/zookeeper-server/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/server/ZookeeperStarterTest.java
new file mode 100644
index 0000000..17ca117
--- /dev/null
+++ b/trunk/discovery/distributed/zookeeper-server/src/test/java/org/apache/cxf/dosgi/discovery/zookeeper/server/ZookeeperStarterTest.java
@@ -0,0 +1,81 @@
+/**
+ * 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.discovery.zookeeper.server;
+
+import java.io.File;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import junit.framework.TestCase;
+
+import org.apache.cxf.dosgi.discovery.zookeeper.server.ZookeeperStarter.MyZooKeeperServerMain;
+import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+import org.osgi.framework.BundleContext;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.classextension.EasyMock.replay;
+import static org.easymock.classextension.EasyMock.verify;
+
+public class ZookeeperStarterTest extends TestCase {
+
+ public void testUpdateConfig() throws Exception {
+ final File tempDir = new File("target");
+ IMocksControl control = EasyMock.createControl();
+ BundleContext bc = control.createMock(BundleContext.class);
+ expect(bc.getDataFile("")).andReturn(tempDir);
+ final MyZooKeeperServerMain mockServer = control.createMock(MyZooKeeperServerMain.class);
+ control.replay();
+
+ ZookeeperStarter starter = new ZookeeperStarter(bc) {
+ @Override
+ protected void startFromConfig(QuorumPeerConfig config) {
+ assertEquals(1234, config.getClientPortAddress().getPort());
+ assertTrue(config.getDataDir().contains(tempDir + File.separator + "zkdata"));
+ assertEquals(2000, config.getTickTime());
+ assertEquals(10, config.getInitLimit());
+ assertEquals(5, config.getSyncLimit());
+ this.main = mockServer;
+ }
+ };
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put("clientPort", "1234");
+ starter.updated(props);
+ assertNotNull(starter.main);
+
+ control.verify();
+ }
+
+ public void testRemoveConfiguration() throws Exception {
+ BundleContext bc = EasyMock.createMock(BundleContext.class);
+ MyZooKeeperServerMain zkServer = EasyMock.createMock(MyZooKeeperServerMain.class);
+ zkServer.shutdown();
+ EasyMock.expectLastCall();
+
+ replay(zkServer);
+
+ ZookeeperStarter starter = new ZookeeperStarter(bc);
+ starter.main = zkServer;
+ starter.updated(null);
+
+ verify(zkServer);
+ assertNull("main should be null", starter.main);
+ }
+}
diff --git a/trunk/discovery/local/pom.xml b/trunk/discovery/local/pom.xml
new file mode 100644
index 0000000..e96bbc4
--- /dev/null
+++ b/trunk/discovery/local/pom.xml
@@ -0,0 +1,96 @@
+<?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>
+ <artifactId>cxf-dosgi-ri-discovery-local</artifactId>
+ <packaging>bundle</packaging>
+ <name>CXF Local Discovery Service Bundle</name>
+ <description>The CXF Local Discovery Service Implementation</description>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>../..</topDirectoryLocation>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>xmlunit</groupId>
+ <artifactId>xmlunit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.jvnet.jaxb2.maven2</groupId>
+ <artifactId>maven-jaxb2-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Activator>org.apache.cxf.dosgi.discovery.local.internal.Activator</Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/internal/Activator.java b/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/internal/Activator.java
new file mode 100644
index 0000000..918f009
--- /dev/null
+++ b/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/internal/Activator.java
@@ -0,0 +1,41 @@
+/**
+ * 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.discovery.local.internal;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Activator implements BundleActivator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Activator.class);
+
+ LocalDiscovery localDiscovery;
+
+ public synchronized void start(BundleContext context) {
+ localDiscovery = new LocalDiscovery(context);
+ LOG.info("Local D-OSGi service discovery started");
+ }
+
+ public synchronized void stop(BundleContext context) {
+ localDiscovery.shutDown();
+ LOG.info("Local D-OSGi service discovery stopped");
+ }
+}
diff --git a/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/internal/LocalDiscovery.java b/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/internal/LocalDiscovery.java
new file mode 100644
index 0000000..ebfaefe
--- /dev/null
+++ b/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/internal/LocalDiscovery.java
@@ -0,0 +1,242 @@
+/**
+ * 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.discovery.local.internal;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.cxf.dosgi.discovery.local.util.Utils;
+import org.apache.cxf.dosgi.endpointdesc.EndpointDescriptionBundleParser;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.osgi.util.tracker.ServiceTracker;
+
+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 =
+ new ConcurrentHashMap<EndpointDescription, Bundle>();
+ Map<EndpointListener, Collection<String>> listenerToFilters =
+ new HashMap<EndpointListener, Collection<String>>();
+ Map<String, Collection<EndpointListener>> filterToListeners =
+ new HashMap<String, Collection<EndpointListener>>();
+ final BundleContext bundleContext;
+
+ EndpointDescriptionBundleParser bundleParser;
+ ServiceTracker<EndpointListener, EndpointListener> listenerTracker;
+
+ public LocalDiscovery(BundleContext bc) {
+ this.bundleParser = new EndpointDescriptionBundleParser();
+ bundleContext = bc;
+
+ listenerTracker = new ServiceTracker<EndpointListener, EndpointListener>(bundleContext,
+ EndpointListener.class, null) {
+
+ @Override
+ public EndpointListener addingService(ServiceReference<EndpointListener> reference) {
+ EndpointListener svc = super.addingService(reference);
+ addListener(reference, svc);
+ return svc;
+ }
+
+ @Override
+ public void modifiedService(ServiceReference<EndpointListener> reference, EndpointListener service) {
+ super.modifiedService(reference, service);
+ removeListener(service);
+
+ // This may cause duplicate registrations of remote services,
+ // but that's fine and should be filtered out on another level.
+ // See Remote Service Admin spec section 122.6.3
+ addListener(reference, service);
+ }
+
+ @Override
+ public void removedService(ServiceReference<EndpointListener> reference, EndpointListener service) {
+ super.removedService(reference, service);
+ removeListener(service);
+ }
+ };
+ listenerTracker.open();
+
+ bundleContext.addBundleListener(this);
+ processExistingBundles();
+ }
+
+ private void processExistingBundles() {
+ Bundle[] bundles = bundleContext.getBundles();
+ if (bundles == null) {
+ return;
+ }
+
+ for (Bundle b : bundles) {
+ if (b.getState() == Bundle.ACTIVE) {
+ findDeclaredRemoteServices(b);
+ }
+ }
+ }
+
+ void addListener(ServiceReference<EndpointListener> endpointListenerRef, EndpointListener endpointListener) {
+ List<String> filters = Utils.getStringPlusProperty(endpointListenerRef,
+ EndpointListener.ENDPOINT_LISTENER_SCOPE);
+ if (filters.isEmpty()) {
+ return;
+ }
+
+ synchronized (listenerToFilters) {
+ listenerToFilters.put(endpointListener, filters);
+ for (String filter : filters) {
+ Collection<EndpointListener> listeners = filterToListeners.get(filter);
+ if (listeners == null) {
+ listeners = new ArrayList<EndpointListener>();
+ filterToListeners.put(filter, listeners);
+ }
+ listeners.add(endpointListener);
+ }
+ }
+
+ triggerCallbacks(filters, endpointListener);
+ }
+
+ /**
+ * If the tracker was removed or the scope was changed this doesn't require
+ * additional callbacks on the tracker. Its the responsibility of the tracker
+ * itself to clean up any orphans. See Remote Service Admin spec 122.6.3
+ * @param endpointListener
+ */
+ void removeListener(EndpointListener endpointListener) {
+ synchronized (listenerToFilters) {
+ Collection<String> filters = listenerToFilters.remove(endpointListener);
+ if (filters == null) {
+ return;
+ }
+
+ for (String filter : filters) {
+ Collection<EndpointListener> listeners = filterToListeners.get(filter);
+ if (listeners != null) {
+ listeners.remove(endpointListener);
+ if (listeners.isEmpty()) {
+ filterToListeners.remove(filter);
+ }
+ }
+ }
+ }
+ }
+
+ private Map<String, Collection<EndpointListener>> getMatchingListeners(EndpointDescription endpoint) {
+ // return a copy of matched filters/listeners so that caller doesn't need to hold locks while triggering events
+ Map<String, Collection<EndpointListener>> matched = new HashMap<String, Collection<EndpointListener>>();
+ synchronized (listenerToFilters) {
+ for (Entry<String, Collection<EndpointListener>> entry : filterToListeners.entrySet()) {
+ String filter = entry.getKey();
+ if (Utils.matchFilter(bundleContext, filter, endpoint)) {
+ matched.put(filter, new ArrayList<EndpointListener>(entry.getValue()));
+ }
+ }
+ }
+ return matched;
+ }
+
+ public void shutDown() {
+ bundleContext.removeBundleListener(this);
+ listenerTracker.close();
+ }
+
+ // BundleListener method
+ public void bundleChanged(BundleEvent be) {
+ switch (be.getType()) {
+ case BundleEvent.STARTED:
+ findDeclaredRemoteServices(be.getBundle());
+ break;
+ case BundleEvent.STOPPED:
+ removeServicesDeclaredInBundle(be.getBundle());
+ break;
+ default:
+ }
+ }
+
+ private void findDeclaredRemoteServices(Bundle bundle) {
+ List<EndpointDescription> endpoints = bundleParser.getAllEndpointDescriptions(bundle);
+ for (EndpointDescription endpoint : endpoints) {
+ endpointDescriptions.put(endpoint, bundle);
+ addedEndpointDescription(endpoint);
+ }
+ }
+
+ private void removeServicesDeclaredInBundle(Bundle bundle) {
+ for (Iterator<Entry<EndpointDescription, Bundle>> i = endpointDescriptions.entrySet().iterator();
+ i.hasNext();) {
+ Entry<EndpointDescription, Bundle> entry = i.next();
+ if (bundle.equals(entry.getValue())) {
+ removedEndpointDescription(entry.getKey());
+ i.remove();
+ }
+ }
+ }
+
+ private void addedEndpointDescription(EndpointDescription endpoint) {
+ triggerCallbacks(endpoint, true);
+ }
+
+ private void removedEndpointDescription(EndpointDescription endpoint) {
+ triggerCallbacks(endpoint, false);
+ }
+
+ private void triggerCallbacks(EndpointDescription endpoint, boolean added) {
+ for (Map.Entry<String, Collection<EndpointListener>> entry : getMatchingListeners(endpoint).entrySet()) {
+ String filter = entry.getKey();
+ for (EndpointListener listener : entry.getValue()) {
+ triggerCallbacks(listener, filter, endpoint, added);
+ }
+ }
+ }
+
+ private void triggerCallbacks(EndpointListener endpointListener, String filter,
+ EndpointDescription endpoint, boolean added) {
+ if (!Utils.matchFilter(bundleContext, filter, endpoint)) {
+ return;
+ }
+
+ if (added) {
+ endpointListener.endpointAdded(endpoint, filter);
+ } else {
+ endpointListener.endpointRemoved(endpoint, filter);
+ }
+ }
+
+ private void triggerCallbacks(Collection<String> filters, EndpointListener endpointListener) {
+ for (String filter : filters) {
+ for (EndpointDescription endpoint : endpointDescriptions.keySet()) {
+ triggerCallbacks(endpointListener, filter, endpoint, true);
+ }
+ }
+ }
+
+}
diff --git a/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/util/Utils.java b/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/util/Utils.java
new file mode 100644
index 0000000..990ae9f
--- /dev/null
+++ b/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/discovery/local/util/Utils.java
@@ -0,0 +1,125 @@
+/**
+ * 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.discovery.local.util;
+
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class Utils {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
+
+ private Utils() {
+ // prevent instantiation
+ }
+
+ public static List<String> getStringPlusProperty(ServiceReference<?> sr, String key) {
+ Object value = sr.getProperty(key);
+ if (value == null) {
+ return Collections.emptyList();
+ }
+
+ if (value instanceof String) {
+ return Collections.singletonList((String) value);
+ }
+
+ if (value instanceof String[]) {
+ String[] values = (String[]) value;
+ List<String> result = new ArrayList<String>(values.length);
+ for (String v : values) {
+ if (v != null) {
+ result.add(v);
+ }
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ if (value instanceof Collection<?>) {
+ Collection<?> values = (Collection<?>) value;
+ List<String> result = new ArrayList<String>(values.size());
+ for (Object v : values) {
+ if (v instanceof String) {
+ result.add((String) v);
+ }
+ }
+ return Collections.unmodifiableList(result);
+ }
+
+ return Collections.emptyList();
+ }
+
+ public static boolean matchFilter(BundleContext bctx, String filter, EndpointDescription endpoint) {
+ if (filter == null) {
+ return false;
+ }
+
+ try {
+ Filter f = bctx.createFilter(filter);
+ Dictionary<String, Object> dict = new Hashtable<String, Object>(endpoint.getProperties());
+ return f.match(dict);
+ } catch (Exception e) {
+ LOG.error("Problem creating a Filter from " + filter, e);
+ return false;
+ }
+ }
+
+ public static String normXML(String s) {
+ String s2 = stripComment(s);
+ String s3 = stripProlog(s2);
+ try {
+ TransformerFactory transFactory = TransformerFactory.newInstance();
+ Transformer transformer = transFactory.newTransformer();
+ StringWriter buffer = new StringWriter();
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ transformer.transform(new StreamSource(new StringReader(s3)), new StreamResult(buffer));
+ return buffer.toString();
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+ private static String stripComment(String s) {
+ return Pattern.compile("<!--(.*?)-->", Pattern.DOTALL).matcher(s).replaceAll("");
+ }
+
+ private static String stripProlog(String s) {
+ return s.replaceAll("<\\?(.*?)\\?>", "");
+ }
+}
diff --git a/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/endpointdesc/EndpointDescriptionBundleParser.java b/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/endpointdesc/EndpointDescriptionBundleParser.java
new file mode 100644
index 0000000..edeaeac
--- /dev/null
+++ b/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/endpointdesc/EndpointDescriptionBundleParser.java
@@ -0,0 +1,104 @@
+/**
+ * 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.endpointdesc;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.Bundle;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.xmlns.rsa.v1_0.EndpointDescriptionType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class EndpointDescriptionBundleParser {
+ private static final Logger LOG = LoggerFactory.getLogger(EndpointDescriptionBundleParser.class);
+
+ private static final String REMOTE_SERVICES_HEADER_NAME = "Remote-Service";
+ private static final String REMOTE_SERVICES_DIRECTORY = "OSGI-INF/remote-service/";
+
+ private EndpointDescriptionParser parser;
+
+ public EndpointDescriptionBundleParser() {
+ parser = new EndpointDescriptionParser();
+ }
+
+ public List<EndpointDescription> getAllEndpointDescriptions(Bundle b) {
+ List<EndpointDescriptionType> elements = getAllDescriptionElements(b);
+
+ List<EndpointDescription> endpoints = new ArrayList<EndpointDescription>(elements.size());
+ for (EndpointDescriptionType epd : elements) {
+ Map<String, Object> props = new PropertiesMapper().toProps(epd.getProperty());
+ endpoints.add(new EndpointDescription(props));
+ }
+ return endpoints;
+ }
+
+ List<EndpointDescriptionType> getAllDescriptionElements(Bundle b) {
+ Enumeration<URL> urls = getEndpointDescriptionURLs(b);
+ List<EndpointDescriptionType> elements = new ArrayList<EndpointDescriptionType>();
+ while (urls.hasMoreElements()) {
+ URL resourceURL = (URL) urls.nextElement();
+ try {
+ elements.addAll(parser.getEndpointDescriptions(resourceURL.openStream()));
+ } catch (Exception ex) {
+ LOG.warn("Problem parsing: " + resourceURL, ex);
+ }
+ }
+ return elements;
+ }
+
+ Enumeration<URL> getEndpointDescriptionURLs(Bundle b) {
+ String origDir = getRemoteServicesDir(b);
+
+ // Split origDir into dir and file pattern
+ String filePattern = "*.xml";
+ String dir;
+ if (origDir.endsWith("/")) {
+ dir = origDir.substring(0, origDir.length() - 1);
+ } else {
+ int idx = origDir.lastIndexOf('/');
+ if (idx >= 0 & origDir.length() > idx) {
+ filePattern = origDir.substring(idx + 1);
+ dir = origDir.substring(0, idx);
+ } else {
+ filePattern = origDir;
+ dir = "";
+ }
+ }
+
+ Enumeration<URL> urls = b.findEntries(dir, filePattern, false);
+ return (urls == null) ? Collections.enumeration(new ArrayList<URL>()) : urls;
+ }
+
+ private static String getRemoteServicesDir(Bundle b) {
+ Dictionary<?, ?> headers = b.getHeaders();
+ Object header = null;
+ if (headers != null) {
+ header = headers.get(REMOTE_SERVICES_HEADER_NAME);
+ }
+ return (header == null) ? REMOTE_SERVICES_DIRECTORY : header.toString();
+ }
+
+}
diff --git a/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/endpointdesc/EndpointDescriptionParser.java b/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/endpointdesc/EndpointDescriptionParser.java
new file mode 100644
index 0000000..fb502c8
--- /dev/null
+++ b/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/endpointdesc/EndpointDescriptionParser.java
@@ -0,0 +1,90 @@
+/**
+ * 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.endpointdesc;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.namespace.QName;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
+import org.osgi.xmlns.rsa.v1_0.EndpointDescriptionType;
+import org.osgi.xmlns.rsa.v1_0.EndpointDescriptionsType;
+
+public class EndpointDescriptionParser {
+ private JAXBContext jaxbContext;
+
+ public EndpointDescriptionParser() {
+ try {
+ jaxbContext = JAXBContext.newInstance(EndpointDescriptionsType.class.getPackage().getName(),
+ this.getClass().getClassLoader());
+ } catch (JAXBException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ public List<EndpointDescriptionType> getEndpointDescriptions(InputStream is) {
+ try {
+ Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+ Source source = new StreamSource(is);
+ JAXBElement<EndpointDescriptionsType> jaxb = unmarshaller.unmarshal(source, EndpointDescriptionsType.class);
+ EndpointDescriptionsType decorations = jaxb.getValue();
+ return decorations.getEndpointDescription();
+ } catch (Exception ex) {
+ throw new RuntimeException(ex.getMessage(), ex);
+ }
+ }
+
+ public void writeTo(EndpointDescriptionsType endpointDescriptions, OutputStream os) {
+ try {
+ Marshaller marshaller = jaxbContext.createMarshaller();
+ marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
+ QName name = new QName("http://www.osgi.org/xmlns/rsa/v1.0.0", "endpoint-descriptions");
+ JAXBElement<EndpointDescriptionsType> el =
+ new JAXBElement<EndpointDescriptionsType>(name, EndpointDescriptionsType.class,
+ endpointDescriptions);
+ marshaller.marshal(el, os);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex.getMessage(), ex);
+ } finally {
+ try {
+ os.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+
+ public byte[] getData(EndpointDescriptionType endpointDescription) {
+ EndpointDescriptionsType endpointDescriptions = new EndpointDescriptionsType();
+ endpointDescriptions.getEndpointDescription().add(endpointDescription);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ writeTo(endpointDescriptions, bos);
+ return bos.toByteArray();
+ }
+}
diff --git a/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/endpointdesc/PropertiesMapper.java b/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/endpointdesc/PropertiesMapper.java
new file mode 100644
index 0000000..a7ed445
--- /dev/null
+++ b/trunk/discovery/local/src/main/java/org/apache/cxf/dosgi/endpointdesc/PropertiesMapper.java
@@ -0,0 +1,345 @@
+/**
+ * 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.endpointdesc;
+
+import java.io.Serializable;
+import java.io.StringWriter;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.w3c.dom.Node;
+import org.osgi.xmlns.rsa.v1_0.ArrayType;
+import org.osgi.xmlns.rsa.v1_0.ObjectFactory;
+import org.osgi.xmlns.rsa.v1_0.PropertyType;
+import org.osgi.xmlns.rsa.v1_0.ValueType;
+import org.osgi.xmlns.rsa.v1_0.XmlType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PropertiesMapper {
+ private static final Logger LOG = LoggerFactory.getLogger(PropertiesMapper.class);
+
+ public Map<String, Object> toProps(List<PropertyType> properties) {
+ Map<String, Object> map = new HashMap<String, Object>();
+ for (PropertyType prop : properties) {
+ map.put(prop.getName(), getValue(prop));
+ }
+ return map;
+ }
+
+ private Object getValue(PropertyType prop) {
+ Object value = null;
+ String type = getTypeName(prop);
+ Object content = getFirstNonText(prop.getContent());
+ if (content instanceof JAXBElement<?>) {
+ JAXBElement<?> el = (JAXBElement<?>)content;
+ if (el.getDeclaredType() == ArrayType.class) {
+ String elName = el.getName().getLocalPart();
+ ArrayType inValue = (ArrayType)el.getValue();
+ if ("array".equals(elName)) {
+ value = getArray(inValue, type);
+ } else if ("set".equals(elName)) {
+ value = handleCollection(inValue, new HashSet<Object>(), type);
+ } else if ("list".equals(elName)) {
+ value = handleCollection(inValue, new ArrayList<Object>(), type);
+ }
+ } else if (el.getDeclaredType() == XmlType.class) {
+ value = readXML((XmlType)el.getValue(), type);
+ }
+ } else {
+ if (prop.getValue() != null) {
+ value = instantiate(type, prop.getValue());
+ } else {
+ if (prop.getContent().size() > 0) {
+ value = instantiate(type, prop.getContent().get(0).toString());
+ }
+ }
+ }
+ return value;
+ }
+
+ private Object getFirstNonText(List<Serializable> contentList) {
+ for (Object content : contentList) {
+ if (content instanceof JAXBElement<?>) {
+ return content;
+ }
+ }
+ return null;
+ }
+
+ private static String getTypeName(PropertyType prop) {
+ String type = prop.getValueType();
+ return type == null ? "String" : type;
+ }
+
+ private Object getArray(ArrayType arrayEl, String type) {
+ List<ValueType> values = arrayEl.getValue();
+ Class<?> cls = null;
+ if ("long".equals(type)) {
+ cls = long.class;
+ } else if ("double".equals(type)) {
+ cls = double.class;
+ } else if ("float".equals(type)) {
+ cls = float.class;
+ } else if ("int".equals(type)) {
+ cls = int.class;
+ } else if ("byte".equals(type)) {
+ cls = byte.class;
+ } else if ("boolean".equals(type)) {
+ cls = boolean.class;
+ } else if ("short".equals(type)) {
+ cls = short.class;
+ }
+
+ try {
+ if (cls == null) {
+ cls = ClassLoader.getSystemClassLoader().loadClass("java.lang." + type);
+ }
+ Object array = Array.newInstance(cls, values.size());
+
+ for (int i = 0; i < values.size(); i++) {
+ Object val = getValue(values.get(i), type);
+ Array.set(array, i, val);
+ }
+
+ return array;
+ } catch (Exception e) {
+ LOG.warn("Could not create array for Endpoint Description", e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private Collection handleCollection(ArrayType el, Collection value, String type) {
+ List<ValueType> values = el.getValue();
+ for (ValueType val : values) {
+ Object obj = getValue(val, type);
+ value.add(obj);
+ }
+ return value;
+ }
+
+ private Object getValue(ValueType value, String type) {
+ if (value.getContent().size() == 1 && value.getContent().get(0) instanceof String) {
+ return handleValue((String)value.getContent().get(0), type);
+ }
+ JAXBElement<?> valueContent = (JAXBElement<?>)getFirstNonText(value.getContent());
+ if (valueContent.getDeclaredType() == XmlType.class) {
+ return readXML((XmlType)valueContent.getValue(), type);
+ }
+ return "";
+ }
+
+ private String readXML(XmlType el, String type) {
+ if (el == null) {
+ return null;
+ }
+ if (!"String".equals(type)) {
+ LOG.warn("Embedded XML must be of type String, found: " + type);
+ return null;
+ }
+ Node xmlContent = (Node)el.getAny();
+ xmlContent.normalize();
+ try {
+ TransformerFactory transFactory = TransformerFactory.newInstance();
+ Transformer transformer = transFactory.newTransformer();
+ StringWriter buffer = new StringWriter();
+ transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ transformer.transform(new DOMSource(xmlContent), new StreamResult(buffer));
+ return buffer.toString();
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+ private static Object handleValue(String val, String type) {
+ return instantiate(type, val);
+ }
+
+ private static Object instantiate(String type, String value) {
+ if ("String".equals(type)) {
+ return value;
+ }
+
+ value = value.trim();
+ String boxedType = null;
+ if ("long".equals(type)) {
+ boxedType = "Long";
+ } else if ("double".equals(type)) {
+ boxedType = "Double";
+ } else if ("float".equals(type)) {
+ boxedType = "Float";
+ } else if ("int".equals(type)) {
+ boxedType = "Integer";
+ } else if ("byte".equals(type)) {
+ boxedType = "Byte";
+ } else if ("char".equals(type)) {
+ boxedType = "Character";
+ } else if ("boolean".equals(type)) {
+ boxedType = "Boolean";
+ } else if ("short".equals(type)) {
+ boxedType = "Short";
+ }
+
+ if (boxedType == null) {
+ boxedType = type;
+ }
+ String javaType = "java.lang." + boxedType;
+
+ try {
+ if ("Character".equals(boxedType)) {
+ return new Character(value.charAt(0));
+ } else {
+ Class<?> cls = ClassLoader.getSystemClassLoader().loadClass(javaType);
+ Constructor<?> ctor = cls.getConstructor(String.class);
+ return ctor.newInstance(value);
+ }
+ } catch (Exception e) {
+ LOG.warn("Could not create Endpoint Property of type " + type + " and value " + value);
+ return null;
+ }
+ }
+
+ public List<PropertyType> fromProps(Map<String, Object> m) {
+ List<PropertyType> props = new ArrayList<PropertyType>();
+ for (Map.Entry<String, Object> entry : m.entrySet()) {
+ String key = entry.getKey();
+ Object val = entry.getValue();
+
+ PropertyType propEl = new PropertyType();
+ propEl.setName(key);
+ ObjectFactory factory = new ObjectFactory();
+ if (val.getClass().isArray()) {
+ ArrayType arrayEl = new ArrayType();
+ propEl.getContent().add(factory.createArray(arrayEl));
+ for (Object o : normalizeArray(val)) {
+ setValueType(propEl, o);
+ ValueType valueType = new ValueType();
+ valueType.getContent().add(o.toString());
+ arrayEl.getValue().add(valueType);
+ }
+ } else if (val instanceof List) {
+ ArrayType listEl = new ArrayType();
+ propEl.getContent().add(factory.createList(listEl));
+ handleCollectionValue((Collection<?>) val, propEl, listEl);
+ } else if (val instanceof Set) {
+ ArrayType setEl = new ArrayType();
+ propEl.getContent().add(factory.createSet(setEl));
+ handleCollectionValue((Collection<?>) val, propEl, setEl);
+ } else if (val instanceof String
+ || val instanceof Character
+ || val instanceof Boolean
+ || val instanceof Byte) {
+ setValueType(propEl, val);
+ propEl.setValue(val.toString());
+ } else if (val instanceof Long
+ || val instanceof Double
+ || val instanceof Float
+ || val instanceof Integer
+ || val instanceof Short) {
+ // various numbers.. maybe "val instanceof Number"?
+ setValueType(propEl, val);
+ propEl.setValue(val.toString());
+ } else {
+ // Don't add this property as the value type is not supported
+ continue;
+ }
+ props.add(propEl);
+ }
+ return props;
+ }
+
+ private static Object[] normalizeArray(Object val) {
+ List<Object> l = new ArrayList<Object>();
+ if (val instanceof int[]) {
+ int[] ia = (int[]) val;
+ for (int i : ia) {
+ l.add(i);
+ }
+ } else if (val instanceof long[]) {
+ long[] la = (long[]) val;
+ for (long i : la) {
+ l.add(i);
+ }
+ } else if (val instanceof float[]) {
+ float[] fa = (float[]) val;
+ for (float f : fa) {
+ l.add(f);
+ }
+ } else if (val instanceof byte[]) {
+ byte[] ba = (byte[]) val;
+ for (byte b : ba) {
+ l.add(b);
+ }
+ } else if (val instanceof boolean[]) {
+ boolean[] ba = (boolean[]) val;
+ for (boolean b : ba) {
+ l.add(b);
+ }
+ } else if (val instanceof short[]) {
+ short[] sa = (short[]) val;
+ for (short s : sa) {
+ l.add(s);
+ }
+ } else if (val instanceof char[]) {
+ char[] ca = (char[]) val;
+ for (char c : ca) {
+ l.add(c);
+ }
+ } else {
+ return (Object[]) val;
+ }
+ return l.toArray();
+ }
+
+ private static void handleCollectionValue(Collection<?> val, PropertyType propEl, ArrayType listEl) {
+ for (Object o : val) {
+ setValueType(propEl, o);
+ ValueType valueType = new ValueType();
+ valueType.getContent().add(o.toString());
+ listEl.getValue().add(valueType);
+ }
+ }
+
+ private static void setValueType(PropertyType propEl, Object val) {
+ if (val instanceof String) {
+ return;
+ }
+
+ String dataType = val.getClass().getName();
+ if (dataType.startsWith("java.lang.")) {
+ dataType = dataType.substring("java.lang.".length());
+ }
+ propEl.setValueType(dataType);
+ }
+}
diff --git a/trunk/discovery/local/src/main/resources/endpoint-description.xsd b/trunk/discovery/local/src/main/resources/endpoint-description.xsd
new file mode 100644
index 0000000..b3254c1
--- /dev/null
+++ b/trunk/discovery/local/src/main/resources/endpoint-description.xsd
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.osgi.org/xmlns/rsa/v1.0.0" targetNamespace="http://www.osgi.org/xmlns/rsa/v1.0.0">
+ <element name="endpoint-descriptions"
+ type="tns:EndpointDescriptionsType">
+ </element>
+
+ <complexType name="EndpointDescriptionsType">
+ <sequence>
+ <element maxOccurs="unbounded" minOccurs="0"
+ ref="tns:endpoint-description">
+ </element>
+ </sequence>
+ </complexType>
+
+ <complexType name="EndpointDescriptionType">
+ <sequence>
+ <element ref="tns:property" maxOccurs="unbounded" minOccurs="0"></element>
+ </sequence>
+ </complexType>
+
+ <complexType name="PropertyType" mixed="true">
+ <choice>
+ <element ref="tns:array"></element>
+ <element ref="tns:list"></element>
+ <element ref="tns:set"></element>
+ <element ref="tns:xml"></element>
+ </choice>
+ <attribute name="name" type="string"></attribute>
+ <attribute name="value-type" type="string"></attribute>
+ <attribute name="value" type="string"></attribute>
+ </complexType>
+
+ <complexType name="ArrayType">
+ <sequence>
+ <element maxOccurs="unbounded" minOccurs="0"
+ ref="tns:value">
+ </element>
+ </sequence>
+ </complexType>
+ <element name="endpoint-description"
+ type="tns:EndpointDescriptionType">
+ </element>
+ <element name="property" type="tns:PropertyType"></element>
+ <element name="array" type="tns:ArrayType"></element>
+ <element name="list" type="tns:ArrayType"></element>
+ <element name="set" type="tns:ArrayType"></element>
+
+ <complexType name="XmlType">
+ <sequence>
+ <any></any>
+ </sequence>
+ </complexType>
+ <element name="value" type="tns:ValueType"></element>
+ <element name="xml" type="tns:XmlType"></element>
+
+ <complexType name="ValueType" mixed="true">
+ <sequence>
+ <element ref="tns:xml"></element>
+ </sequence>
+ </complexType>
+</schema>
\ No newline at end of file
diff --git a/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/discovery/local/internal/ActivatorTest.java b/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/discovery/local/internal/ActivatorTest.java
new file mode 100644
index 0000000..a0bdc87
--- /dev/null
+++ b/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/discovery/local/internal/ActivatorTest.java
@@ -0,0 +1,52 @@
+/**
+ * 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.discovery.local.internal;
+
+import junit.framework.TestCase;
+
+import org.easymock.IAnswer;
+import org.easymock.classextension.EasyMock;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+
+public class ActivatorTest extends TestCase {
+
+ public void testActivator() throws Exception {
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.createFilter((String) EasyMock.anyObject())).andAnswer(new IAnswer<Filter>() {
+ public Filter answer() throws Throwable {
+ return FrameworkUtil.createFilter((String) EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(bc);
+
+ Activator a = new Activator();
+ a.start(bc);
+ assertNotNull(a.localDiscovery);
+
+ a.localDiscovery = EasyMock.createMock(LocalDiscovery.class);
+ a.localDiscovery.shutDown();
+ EasyMock.expectLastCall();
+ EasyMock.replay(a.localDiscovery);
+ a.stop(bc);
+
+ EasyMock.verify(a.localDiscovery);
+ }
+}
diff --git a/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/discovery/local/internal/LocalDiscoveryTest.java b/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/discovery/local/internal/LocalDiscoveryTest.java
new file mode 100644
index 0000000..ccbaca7
--- /dev/null
+++ b/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/discovery/local/internal/LocalDiscoveryTest.java
@@ -0,0 +1,409 @@
+/**
+ * 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.discovery.local.internal;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+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;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+
+public class LocalDiscoveryTest extends TestCase {
+
+ public void testLocalDiscovery() throws Exception {
+ Filter filter = EasyMock.createMock(Filter.class);
+ EasyMock.replay(filter);
+
+ BundleContext bc = EasyMock.createMock(BundleContext.class);
+ EasyMock.expect(bc.createFilter("(objectClass=org.osgi.service.remoteserviceadmin.EndpointListener)"))
+ .andReturn(filter);
+ bc.addServiceListener((ServiceListener) EasyMock.anyObject(),
+ EasyMock.eq("(objectClass=org.osgi.service.remoteserviceadmin.EndpointListener)"));
+ EasyMock.expectLastCall();
+ EasyMock.expect(bc.getServiceReferences("org.osgi.service.remoteserviceadmin.EndpointListener", null))
+ .andReturn(null);
+ bc.addBundleListener((BundleListener) EasyMock.anyObject());
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ assertEquals(LocalDiscovery.class, EasyMock.getCurrentArguments()[0].getClass());
+ return null;
+ }
+ });
+ EasyMock.expect(bc.getBundles()).andReturn(null);
+ EasyMock.replay(bc);
+
+ LocalDiscovery ld = new LocalDiscovery(bc);
+ assertSame(bc, ld.bundleContext);
+ assertNotNull(ld.listenerTracker);
+
+ EasyMock.verify(bc);
+
+ EasyMock.reset(bc);
+ bc.removeBundleListener(ld);
+ EasyMock.expectLastCall();
+ bc.removeServiceListener((ServiceListener) EasyMock.anyObject());
+ EasyMock.expectLastCall();
+ EasyMock.replay(bc);
+
+ ld.shutDown();
+ EasyMock.verify(bc);
+ }
+
+ public void testPreExistingBundles() throws Exception {
+ Filter filter = EasyMock.createMock(Filter.class);
+ EasyMock.replay(filter);
+
+ BundleContext bc = EasyMock.createMock(BundleContext.class);
+ EasyMock.expect(bc.createFilter("(objectClass=org.osgi.service.remoteserviceadmin.EndpointListener)"))
+ .andReturn(filter);
+ bc.addServiceListener((ServiceListener) EasyMock.anyObject(),
+ EasyMock.eq("(objectClass=org.osgi.service.remoteserviceadmin.EndpointListener)"));
+ EasyMock.expectLastCall();
+ EasyMock.expect(bc.getServiceReferences("org.osgi.service.remoteserviceadmin.EndpointListener", null))
+ .andReturn(null);
+ bc.addBundleListener((BundleListener) EasyMock.anyObject());
+ EasyMock.expectLastCall();
+
+ Bundle b1 = EasyMock.createMock(Bundle.class);
+ EasyMock.expect(b1.getState()).andReturn(Bundle.RESOLVED);
+ EasyMock.replay(b1);
+ Bundle b2 = EasyMock.createMock(Bundle.class);
+ EasyMock.expect(b2.getState()).andReturn(Bundle.ACTIVE);
+ Dictionary<String, String> headers = new Hashtable<String, String>();
+ headers.put("Remote-Service", "OSGI-INF/remote-service/");
+ EasyMock.expect(b2.getHeaders()).andReturn(headers);
+
+ URL rs3URL = getClass().getResource("/ed3.xml");
+ URL rs4URL = getClass().getResource("/ed4.xml");
+ List<URL> urls = Arrays.asList(rs3URL, rs4URL);
+ EasyMock.expect(b2.findEntries("OSGI-INF/remote-service", "*.xml", false))
+ .andReturn(Collections.enumeration(urls));
+ EasyMock.replay(b2);
+
+ EasyMock.expect(bc.getBundles()).andReturn(new Bundle[] {b1, b2});
+ EasyMock.replay(bc);
+
+ LocalDiscovery ld = new LocalDiscovery(bc);
+
+ assertEquals(3, ld.endpointDescriptions.size());
+ Set<String> expected = new HashSet<String>(
+ Arrays.asList("http://somewhere:12345", "http://somewhere:1", "http://somewhere"));
+ Set<String> actual = new HashSet<String>();
+ for (Map.Entry<EndpointDescription, Bundle> entry : ld.endpointDescriptions.entrySet()) {
+ assertSame(b2, entry.getValue());
+ actual.add(entry.getKey().getId());
+ }
+ assertEquals(expected, actual);
+ }
+
+ public void testBundleChanged() throws Exception {
+ LocalDiscovery ld = getLocalDiscovery();
+
+ Bundle bundle = EasyMock.createMock(Bundle.class);
+ EasyMock.expect(bundle.getSymbolicName()).andReturn("testing.bundle").anyTimes();
+ EasyMock.expect(bundle.getState()).andReturn(Bundle.ACTIVE);
+ Dictionary<String, String> headers = new Hashtable<String, String>();
+ headers.put("Remote-Service", "OSGI-INF/rsa/");
+ EasyMock.expect(bundle.getHeaders()).andReturn(headers);
+ EasyMock.expect(bundle.findEntries("OSGI-INF/rsa", "*.xml", false))
+ .andReturn(Collections.enumeration(
+ Collections.singleton(getClass().getResource("/ed3.xml"))));
+ EasyMock.replay(bundle);
+
+ BundleEvent be0 = new BundleEvent(BundleEvent.INSTALLED, bundle);
+ ld.bundleChanged(be0);
+ assertEquals(0, ld.endpointDescriptions.size());
+
+ // Create an EndpointListener
+ final Map<String, Object> props = new Hashtable<String, Object>();
+ props.put(EndpointListener.ENDPOINT_LISTENER_SCOPE, "(objectClass=*)");
+ @SuppressWarnings("unchecked")
+ ServiceReference<EndpointListener> sr = EasyMock.createMock(ServiceReference.class);
+ EasyMock.expect(sr.getPropertyKeys()).andReturn(props.keySet().toArray(new String[] {})).anyTimes();
+ EasyMock.expect(sr.getProperty((String) EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ return props.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(sr);
+
+ EndpointListener endpointListener = EasyMock.createMock(EndpointListener.class);
+ endpointListener.endpointAdded((EndpointDescription) EasyMock.anyObject(), EasyMock.eq("(objectClass=*)"));
+ EasyMock.expectLastCall();
+ EasyMock.replay(endpointListener);
+ ld.addListener(sr, endpointListener);
+
+ // Start the bundle
+ BundleEvent be = new BundleEvent(BundleEvent.STARTED, bundle);
+ ld.bundleChanged(be);
+ assertEquals(1, ld.endpointDescriptions.size());
+ EndpointDescription endpoint = ld.endpointDescriptions.keySet().iterator().next();
+ assertEquals("http://somewhere:12345", endpoint.getId());
+ assertSame(bundle, ld.endpointDescriptions.get(endpoint));
+
+ EasyMock.verify(endpointListener);
+
+ // Stop the bundle
+ EasyMock.reset(endpointListener);
+ endpointListener.endpointRemoved((EndpointDescription) EasyMock.anyObject(), EasyMock.eq("(objectClass=*)"));
+ EasyMock.expectLastCall();
+ EasyMock.replay(endpointListener);
+
+ BundleEvent be1 = new BundleEvent(BundleEvent.STOPPED, bundle);
+ ld.bundleChanged(be1);
+ assertEquals(0, ld.endpointDescriptions.size());
+
+ EasyMock.verify(endpointListener);
+ }
+
+ public void testEndpointListenerService() throws Exception {
+ LocalDiscovery ld = getLocalDiscovery();
+
+ Bundle bundle = EasyMock.createMock(Bundle.class);
+ EasyMock.expect(bundle.getState()).andReturn(Bundle.ACTIVE);
+ Dictionary<String, String> headers = new Hashtable<String, String>();
+ headers.put("Remote-Service", "OSGI-INF/rsa/ed4.xml");
+ EasyMock.expect(bundle.getHeaders()).andReturn(headers);
+ EasyMock.expect(bundle.findEntries("OSGI-INF/rsa", "ed4.xml", false))
+ .andReturn(Collections.enumeration(
+ Collections.singleton(getClass().getResource("/ed4.xml"))));
+ EasyMock.replay(bundle);
+
+ BundleEvent be = new BundleEvent(BundleEvent.STARTED, bundle);
+ ld.bundleChanged(be);
+ assertEquals(2, ld.endpointDescriptions.size());
+
+ final Map<String, Object> props = new Hashtable<String, Object>();
+ props.put(EndpointListener.ENDPOINT_LISTENER_SCOPE, new String[] {"(objectClass=org.example.ClassA)"});
+ @SuppressWarnings("unchecked")
+ ServiceReference<EndpointListener> sr = EasyMock.createMock(ServiceReference.class);
+ EasyMock.expect(sr.getPropertyKeys()).andReturn(props.keySet().toArray(new String[] {})).anyTimes();
+ EasyMock.expect(sr.getProperty((String) EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ return props.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+
+ EasyMock.replay(sr);
+
+ EasyMock.reset(ld.bundleContext);
+ EndpointListener el = EasyMock.createMock(EndpointListener.class);
+ EasyMock.expect(ld.bundleContext.getService(sr)).andReturn(el);
+ EasyMock.expect(ld.bundleContext.ungetService(sr)).andReturn(true);
+ EasyMock.expect(ld.bundleContext.createFilter((String) EasyMock.anyObject())).andAnswer(new IAnswer<Filter>() {
+ public Filter answer() throws Throwable {
+ return FrameworkUtil.createFilter((String) EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(ld.bundleContext);
+
+ el.endpointAdded((EndpointDescription) EasyMock.anyObject(),
+ EasyMock.eq("(objectClass=org.example.ClassA)"));
+ EasyMock.expectLastCall();
+ EasyMock.replay(el);
+
+ // Add the EndpointListener Service
+ assertEquals("Precondition failed", 0, ld.listenerToFilters.size());
+ assertEquals("Precondition failed", 0, ld.filterToListeners.size());
+ assertSame(el, ld.listenerTracker.addingService(sr));
+
+ assertEquals(1, ld.listenerToFilters.size());
+ assertEquals(Collections.singletonList("(objectClass=org.example.ClassA)"), ld.listenerToFilters.get(el));
+ assertEquals(1, ld.filterToListeners.size());
+ assertEquals(Collections.singletonList(el), ld.filterToListeners.get("(objectClass=org.example.ClassA)"));
+
+ EasyMock.verify(el);
+
+ // Modify the EndpointListener Service
+ // no need to reset the mock for this...
+ props.put(EndpointListener.ENDPOINT_LISTENER_SCOPE,
+ "(|(objectClass=org.example.ClassA)(objectClass=org.example.ClassB))");
+
+ EasyMock.reset(el);
+ final Set<String> actualEndpoints = new HashSet<String>();
+ el.endpointAdded((EndpointDescription) EasyMock.anyObject(),
+ EasyMock.eq("(|(objectClass=org.example.ClassA)(objectClass=org.example.ClassB))"));
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ EndpointDescription endpoint = (EndpointDescription) EasyMock.getCurrentArguments()[0];
+ actualEndpoints.addAll(endpoint.getInterfaces());
+ return null;
+ }
+ }).times(2);
+ EasyMock.replay(el);
+
+ ld.listenerTracker.modifiedService(sr, el);
+ assertEquals(1, ld.listenerToFilters.size());
+ assertEquals(Arrays.asList("(|(objectClass=org.example.ClassA)(objectClass=org.example.ClassB))"),
+ ld.listenerToFilters.get(el));
+ assertEquals(1, ld.filterToListeners.size());
+ assertEquals(Collections.singletonList(el),
+ ld.filterToListeners.get("(|(objectClass=org.example.ClassA)(objectClass=org.example.ClassB))"));
+
+ EasyMock.verify(el);
+ Set<String> expectedEndpoints = new HashSet<String>(Arrays.asList("org.example.ClassA", "org.example.ClassB"));
+ assertEquals(expectedEndpoints, actualEndpoints);
+
+ // Remove the EndpointListener Service
+ ld.listenerTracker.removedService(sr, el);
+ assertEquals(0, ld.listenerToFilters.size());
+ assertEquals(0, ld.filterToListeners.size());
+ }
+
+ public void testRegisterTracker() throws Exception {
+ LocalDiscovery ld = getLocalDiscovery();
+
+ final Map<String, Object> props = new Hashtable<String, Object>();
+ props.put(EndpointListener.ENDPOINT_LISTENER_SCOPE, "(objectClass=Aaaa)");
+ @SuppressWarnings("unchecked")
+ ServiceReference<EndpointListener> sr = EasyMock.createMock(ServiceReference.class);
+ EasyMock.expect(sr.getPropertyKeys()).andReturn(props.keySet().toArray(new String[] {})).anyTimes();
+ EasyMock.expect(sr.getProperty((String) EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ return props.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(sr);
+
+ EndpointListener endpointListener = EasyMock.createMock(EndpointListener.class);
+ EasyMock.replay(endpointListener);
+
+ assertEquals("Precondition failed", 0, ld.listenerToFilters.size());
+ assertEquals("Precondition failed", 0, ld.filterToListeners.size());
+ ld.addListener(sr, endpointListener);
+
+ assertEquals(1, ld.listenerToFilters.size());
+ assertEquals(Collections.singletonList("(objectClass=Aaaa)"), ld.listenerToFilters.get(endpointListener));
+ assertEquals(1, ld.filterToListeners.size());
+ assertEquals(Collections.singletonList(endpointListener), ld.filterToListeners.get("(objectClass=Aaaa)"));
+
+ // Add another one with the same scope filter
+ @SuppressWarnings("unchecked")
+ ServiceReference<EndpointListener> sr2 = EasyMock.createMock(ServiceReference.class);
+ EasyMock.expect(sr2.getPropertyKeys()).andReturn(props.keySet().toArray(new String[] {})).anyTimes();
+ EasyMock.expect(sr2.getProperty((String) EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ return props.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(sr2);
+
+ EndpointListener endpointListener2 = EasyMock.createMock(EndpointListener.class);
+ EasyMock.replay(endpointListener2);
+ ld.addListener(sr2, endpointListener2);
+
+ assertEquals(2, ld.listenerToFilters.size());
+ assertEquals(Collections.singletonList("(objectClass=Aaaa)"), ld.listenerToFilters.get(endpointListener));
+ assertEquals(Collections.singletonList("(objectClass=Aaaa)"), ld.listenerToFilters.get(endpointListener2));
+
+ assertEquals(1, ld.filterToListeners.size());
+ List<EndpointListener> endpointListeners12 = Arrays.asList(endpointListener, endpointListener2);
+ assertEquals(endpointListeners12, ld.filterToListeners.get("(objectClass=Aaaa)"));
+
+ // Add another listener with a multi-value scope
+ final Map<String, Object> props2 = new Hashtable<String, Object>();
+ props2.put(EndpointListener.ENDPOINT_LISTENER_SCOPE, Arrays.asList("(objectClass=X)", "(objectClass=Y)"));
+ @SuppressWarnings("unchecked")
+ ServiceReference<EndpointListener> sr3 = EasyMock.createMock(ServiceReference.class);
+ EasyMock.expect(sr3.getPropertyKeys()).andReturn(props2.keySet().toArray(new String[] {})).anyTimes();
+ EasyMock.expect(sr3.getProperty((String) EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ return props2.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(sr3);
+
+ EndpointListener endpointListener3 = EasyMock.createMock(EndpointListener.class);
+ EasyMock.replay(endpointListener3);
+ ld.addListener(sr3, endpointListener3);
+
+ assertEquals(3, ld.listenerToFilters.size());
+ assertEquals(Collections.singletonList("(objectClass=Aaaa)"), ld.listenerToFilters.get(endpointListener));
+ assertEquals(Collections.singletonList("(objectClass=Aaaa)"), ld.listenerToFilters.get(endpointListener2));
+ assertEquals(Arrays.asList("(objectClass=X)", "(objectClass=Y)"), ld.listenerToFilters.get(endpointListener3));
+
+ assertEquals(3, ld.filterToListeners.size());
+ assertEquals(endpointListeners12, ld.filterToListeners.get("(objectClass=Aaaa)"));
+ assertEquals(Collections.singletonList(endpointListener3), ld.filterToListeners.get("(objectClass=X)"));
+ assertEquals(Collections.singletonList(endpointListener3), ld.filterToListeners.get("(objectClass=Y)"));
+ }
+
+ public void testClearTracker() throws Exception {
+ LocalDiscovery ld = getLocalDiscovery();
+
+ EndpointListener endpointListener = EasyMock.createMock(EndpointListener.class);
+ ld.listenerToFilters.put(endpointListener,
+ new ArrayList<String>(Arrays.asList("(a=b)", "(objectClass=foo.bar.Bheuaark)")));
+ ld.filterToListeners.put("(a=b)", new ArrayList<EndpointListener>(Arrays.asList(endpointListener)));
+ ld.filterToListeners.put("(objectClass=foo.bar.Bheuaark)",
+ new ArrayList<EndpointListener>(Arrays.asList(endpointListener)));
+
+ assertEquals(1, ld.listenerToFilters.size());
+ assertEquals(2, ld.filterToListeners.size());
+ assertEquals(1, ld.filterToListeners.values().iterator().next().size());
+ ld.removeListener(EasyMock.createMock(EndpointListener.class));
+ assertEquals(1, ld.listenerToFilters.size());
+ assertEquals(2, ld.filterToListeners.size());
+ assertEquals(1, ld.filterToListeners.values().iterator().next().size());
+ ld.removeListener(endpointListener);
+ assertEquals(0, ld.listenerToFilters.size());
+ assertEquals(0, ld.filterToListeners.size());
+ }
+
+ private LocalDiscovery getLocalDiscovery() throws InvalidSyntaxException {
+ BundleContext bc = EasyMock.createMock(BundleContext.class);
+ EasyMock.expect(bc.createFilter((String) EasyMock.anyObject())).andAnswer(new IAnswer<Filter>() {
+ public Filter answer() throws Throwable {
+ return FrameworkUtil.createFilter((String) EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ bc.addServiceListener((ServiceListener) EasyMock.anyObject(),
+ EasyMock.eq("(objectClass=org.osgi.service.remoteserviceadmin.EndpointListener)"));
+ EasyMock.expectLastCall();
+ EasyMock.expect(bc.getServiceReferences("org.osgi.service.remoteserviceadmin.EndpointListener", null))
+ .andReturn(null);
+ bc.addBundleListener((BundleListener) EasyMock.anyObject());
+ EasyMock.expectLastCall();
+ EasyMock.expect(bc.getBundles()).andReturn(null);
+ EasyMock.replay(bc);
+
+ return new LocalDiscovery(bc);
+ }
+}
diff --git a/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/endpointdesc/EndpointDescriptionBundleParserTest.java b/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/endpointdesc/EndpointDescriptionBundleParserTest.java
new file mode 100644
index 0000000..4884c50
--- /dev/null
+++ b/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/endpointdesc/EndpointDescriptionBundleParserTest.java
@@ -0,0 +1,138 @@
+/**
+ * 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.endpointdesc;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.apache.cxf.dosgi.discovery.local.util.Utils;
+import org.easymock.EasyMock;
+import org.osgi.framework.Bundle;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+
+public class EndpointDescriptionBundleParserTest extends TestCase {
+
+ private Bundle createBundleContaining(URL ed1URL) {
+ Bundle b = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(b.findEntries(
+ EasyMock.eq("OSGI-INF/remote-service"),
+ EasyMock.eq("*.xml"), EasyMock.anyBoolean())).andReturn(
+ Collections.enumeration(Arrays.asList(ed1URL))).anyTimes();
+ EasyMock.replay(b);
+ return b;
+ }
+
+ public void testAllEndpoints1() {
+ URL ed1URL = getClass().getResource("/ed1.xml");
+
+ Bundle b = createBundleContaining(ed1URL);
+
+ List<EndpointDescription> endpoints = new EndpointDescriptionBundleParser().getAllEndpointDescriptions(b);
+ assertEquals(4, endpoints.size());
+ EndpointDescription endpoint0 = endpoints.get(0);
+ assertEquals("http://somewhere:12345", endpoint0.getId());
+ assertEquals(Arrays.asList("SomeService"), endpoint0.getInterfaces());
+ assertEquals(Arrays.asList("confidentiality"),
+ endpoint0.getProperties().get("osgi.remote.requires.intents"));
+ assertEquals("testValue", endpoint0.getProperties().get("testKey"));
+
+ EndpointDescription endpoint1 = endpoints.get(1);
+ assertEquals("myScheme://somewhere:12345", endpoint1.getId());
+ assertEquals(Arrays.asList("SomeOtherService", "WithSomeSecondInterface"), endpoint1.getInterfaces());
+
+ EndpointDescription endpoint2 = endpoints.get(2);
+ assertEquals("http://somewhere", endpoint2.getId());
+ assertEquals(Arrays.asList("SomeOtherService", "WithSomeSecondInterface"), endpoint2.getInterfaces());
+
+ EndpointDescription endpoint3 = endpoints.get(3);
+ assertEquals("http://somewhere:1/2/3/4?5", endpoint3.getId());
+ assertEquals(Arrays.asList("SomeOtherService", "WithSomeSecondInterface"), endpoint3.getInterfaces());
+ }
+
+ public void testAllEndpoints2() throws Exception {
+ URL ed2URL = getClass().getResource("/ed2.xml");
+
+ Bundle b = createBundleContaining(ed2URL);
+
+ List<EndpointDescription> endpoints = new EndpointDescriptionBundleParser().getAllEndpointDescriptions(b);
+ assertEquals(2, endpoints.size());
+ EndpointDescription endpoint0 = endpoints.get(0);
+ assertEquals("foo:bar", endpoint0.getId());
+ assertEquals(Arrays.asList("com.acme.HelloService"), endpoint0.getInterfaces());
+ assertEquals(Arrays.asList("SOAP"), endpoint0.getIntents());
+ // changed from exported to imported
+ assertEquals("org.apache.cxf.ws", endpoint0.getProperties().get("service.imported.configs"));
+
+ EndpointDescription endpoint1 = endpoints.get(1);
+ Map<String, Object> props = endpoint1.getProperties();
+ assertEquals(Arrays.asList("com.acme.HelloService", "some.other.Service"), endpoint1.getInterfaces());
+ assertEquals("org.apache.cxf.ws", props.get("service.imported.configs"));
+ // exports should have been removed
+ assertNull(props.get("service.exported.configs"));
+
+ assertEquals(Utils.normXML("<other:t1 xmlns:other='http://www.acme.org/xmlns/other/v1.0.0' "
+ + "xmlns='http://www.acme.org/xmlns/other/v1.0.0'><foo type='bar'>haha</foo>\n"
+ + " </other:t1>"),
+ Utils.normXML((String) props.get("someXML")));
+ assertEquals(Long.MAX_VALUE, props.get("long"));
+ assertEquals(-1L, props.get("long2"));
+ assertEquals(Double.MAX_VALUE, props.get("double"));
+ assertEquals(1.0d, props.get("Double2"));
+ assertEquals(42.24f, props.get("float"));
+ assertEquals(1.0f, props.get("Float2"));
+ assertEquals(17, props.get("int"));
+ assertEquals(42, props.get("Integer2"));
+ assertEquals((byte) 127, props.get("byte"));
+ assertEquals((byte) -128, props.get("Byte2"));
+ assertEquals(Boolean.TRUE, props.get("boolean"));
+ assertEquals(Boolean.TRUE, props.get("Boolean2"));
+ assertEquals((short) 99, props.get("short"));
+ assertEquals((short) -99, props.get("Short2"));
+ assertEquals('@', props.get("char"));
+ assertEquals('X', props.get("Character2"));
+
+ int[] intArray = (int[]) props.get("int-array");
+ assertTrue(Arrays.equals(new int[] {1, 2}, intArray));
+
+ Integer[] integerArray = (Integer[]) props.get("Integer-array");
+ assertTrue(Arrays.equals(new Integer[] {2, 1}, integerArray));
+
+ assertEquals(Arrays.asList(true, false), props.get("bool-list"));
+ assertEquals(new HashSet<Object>(), props.get("long-set"));
+ Set<String> stringSet = new HashSet<String>();
+ stringSet.add("Hello there");
+ stringSet.add("How are you?");
+ assertEquals(stringSet, props.get("string-set"));
+ assertEquals("Hello", props.get("other1").toString().trim());
+
+ List<?> l = (List<?>) props.get("other2");
+ assertEquals(1, l.size());
+ assertEquals(Utils.normXML("<other:t2 xmlns:other='http://www.acme.org/xmlns/other/v1.0.0' "
+ + "xmlns='http://www.osgi.org/xmlns/rsa/v1.0.0'/>"),
+ Utils.normXML((String) l.get(0)));
+ }
+
+}
diff --git a/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/endpointdesc/EndpointDescriptionParserTest.java b/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/endpointdesc/EndpointDescriptionParserTest.java
new file mode 100644
index 0000000..97e4b45
--- /dev/null
+++ b/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/endpointdesc/EndpointDescriptionParserTest.java
@@ -0,0 +1,49 @@
+/**
+ * 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.endpointdesc;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.List;
+
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.xmlns.rsa.v1_0.EndpointDescriptionType;
+
+public class EndpointDescriptionParserTest {
+
+ @Test
+ public void testNoRemoteServicesXMLFiles() {
+ Bundle b = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.replay(b);
+
+ List<EndpointDescriptionType> rsElements = new EndpointDescriptionBundleParser().getAllDescriptionElements(b);
+ Assert.assertEquals(0, rsElements.size());
+ }
+
+ @Test
+ public void testEndpointDescriptionsFromURL() throws IOException {
+ URL ed1URL = getClass().getResource("/ed1.xml");
+ List<EndpointDescriptionType> edElements = new EndpointDescriptionParser().
+ getEndpointDescriptions(ed1URL.openStream());
+ Assert.assertEquals(4, edElements.size());
+ }
+}
diff --git a/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/endpointdesc/PropertiesMapperTest.java b/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/endpointdesc/PropertiesMapperTest.java
new file mode 100644
index 0000000..bc21198
--- /dev/null
+++ b/trunk/discovery/local/src/test/java/org/apache/cxf/dosgi/endpointdesc/PropertiesMapperTest.java
@@ -0,0 +1,97 @@
+/**
+ * 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.endpointdesc;
+
+import java.io.ByteArrayInputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.xml.sax.InputSource;
+
+import org.custommonkey.xmlunit.XMLAssert;
+import org.junit.Test;
+import org.osgi.xmlns.rsa.v1_0.EndpointDescriptionType;
+import org.osgi.xmlns.rsa.v1_0.PropertyType;
+
+public class PropertiesMapperTest {
+ private static final String LF = "\n";
+
+ @Test
+ public void testCreateXML() throws Exception {
+ Map<String, Object> m = new LinkedHashMap<String, Object>();
+ m.put("service.imported.configs", "org.apache.cxf.ws");
+ m.put("endpoint.id", "foo:bar");
+ m.put("objectClass", new String[] {"com.acme.HelloService", "some.other.Service"});
+ m.put("SomeObject", new Object());
+ m.put("long", 9223372036854775807L);
+ m.put("Long2", -1L);
+ m.put("double", 1.7976931348623157E308);
+ m.put("Double2", 1.0d);
+ m.put("float", 42.24f);
+ m.put("Float2", 1.0f);
+ m.put("int", 17);
+ m.put("Integer2", 42);
+ m.put("byte", (byte) 127);
+ m.put("Byte2", (byte) -128);
+ m.put("boolean", true);
+ m.put("Boolean2", false);
+ m.put("short", (short) 99);
+ m.put("Short2", (short) -99);
+ m.put("char", '@');
+ m.put("Character2", 'X');
+
+ List<Boolean> boolList = new ArrayList<Boolean>();
+ boolList.add(true);
+ boolList.add(false);
+ m.put("bool-list", boolList);
+ m.put("empty-set", new HashSet<Object>());
+
+ Set<String> stringSet = new LinkedHashSet<String>();
+ stringSet.add("Hello there");
+ stringSet.add("How are you?");
+ m.put("string-set", stringSet);
+
+ int[] intArray = new int[] {1, 2};
+ m.put("int-array", intArray);
+
+ String xml = "<xml>" + LF
+ + "<t1 xmlns=\"http://www.acme.org/xmlns/other/v1.0.0\">" + LF
+ + "<foo type='bar'>haha</foo>" + LF
+ + "</t1>" + LF
+ + "</xml>";
+ m.put("someXML", xml);
+
+ List<PropertyType> props = new PropertiesMapper().fromProps(m);
+ EndpointDescriptionType epd = new EndpointDescriptionType();
+ epd.getProperty().addAll(props);
+ byte[] epData = new EndpointDescriptionParser().getData(epd);
+
+ URL edURL = getClass().getResource("/ed2-generated.xml");
+ InputSource expectedXml = new InputSource(edURL.openStream());
+ InputSource actualXml = new InputSource(new ByteArrayInputStream(epData));
+ XMLAssert.assertXMLEqual(expectedXml, actualXml);
+ }
+
+}
diff --git a/trunk/discovery/local/src/test/resources/ed1.xml b/trunk/discovery/local/src/test/resources/ed1.xml
new file mode 100644
index 0000000..95b1994
--- /dev/null
+++ b/trunk/discovery/local/src/test/resources/ed1.xml
@@ -0,0 +1,72 @@
+<?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.
+-->
+
+<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0"
+ xmlns:other="http://www.acme.org/xmlns/other/v1.0.0">
+ <endpoint-description>
+ <property name="objectClass">
+ <array>
+ <value>SomeService</value>
+ </array>
+ </property>
+ <property name="osgi.remote.requires.intents">
+ <list>
+ <value>confidentiality</value>
+ </list>
+ </property>
+ <property name="testKey" value="testValue"/>
+ <property name="endpoint.id">http://somewhere:12345</property>
+ <property name="service.imported.configs" value="org.apache.cxf.ws"/>
+ </endpoint-description>
+
+ <endpoint-description>
+ <property name="objectClass">
+ <array>
+ <value>SomeOtherService</value>
+ <value>WithSomeSecondInterface</value>
+ </array>
+ </property>
+ <property name="endpoint.id" value-type="String" value="myScheme://somewhere:12345" />
+ <property name="service.imported.configs" value="org.apache.cxf.ws"/>
+ </endpoint-description>
+
+ <endpoint-description>
+ <property name="objectClass" value-type="String">
+ <array>
+ <value>SomeOtherService</value>
+ <value>WithSomeSecondInterface</value>
+ </array>
+ </property>
+ <property name="endpoint.id" value="http://somewhere" />
+ <property name="service.imported.configs" value="org.apache.cxf.ws"/>
+ </endpoint-description>
+
+ <endpoint-description>
+ <property name="objectClass">
+ <array>
+ <value>SomeOtherService</value>
+ <value>WithSomeSecondInterface</value>
+ </array>
+ </property>
+ <property name="endpoint.id" value-type="String">http://somewhere:1/2/3/4?5</property>
+ <property name="service.imported.configs" value="org.apache.cxf.ws"/>
+ </endpoint-description>
+</endpoint-descriptions>
diff --git a/trunk/discovery/local/src/test/resources/ed2-generated.xml b/trunk/discovery/local/src/test/resources/ed2-generated.xml
new file mode 100644
index 0000000..5e2a5ef
--- /dev/null
+++ b/trunk/discovery/local/src/test/resources/ed2-generated.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0">
+ <endpoint-description>
+ <property name="service.imported.configs" value="org.apache.cxf.ws" />
+ <property name="endpoint.id" value="foo:bar" />
+ <property name="objectClass">
+ <array>
+ <value>com.acme.HelloService</value>
+ <value>some.other.Service</value>
+ </array>
+ </property>
+ <property name="long" value-type="Long" value="9223372036854775807" />
+ <property name="Long2" value-type="Long" value="-1" />
+ <property name="double" value-type="Double"
+ value="1.7976931348623157E308" />
+ <property name="Double2" value-type="Double" value="1.0" />
+ <property name="float" value-type="Float" value="42.24" />
+ <property name="Float2" value-type="Float" value="1.0" />
+ <property name="int" value-type="Integer" value="17" />
+ <property name="Integer2" value-type="Integer" value="42" />
+ <property name="byte" value-type="Byte" value="127" />
+ <property name="Byte2" value-type="Byte" value="-128" />
+ <property name="boolean" value-type="Boolean" value="true" />
+ <property name="Boolean2" value-type="Boolean" value="false" />
+ <property name="short" value-type="Short" value="99" />
+ <property name="Short2" value-type="Short" value="-99" />
+ <property name="char" value-type="Character" value="@" />
+ <property name="Character2" value-type="Character"
+ value="X" />
+ <property name="bool-list" value-type="Boolean">
+ <list>
+ <value>true</value>
+ <value>false</value>
+ </list>
+ </property>
+ <property name="empty-set">
+ <set />
+ </property>
+ <property name="string-set">
+ <set>
+ <value>Hello there</value>
+ <value>How are you?</value>
+ </set>
+ </property>
+ <property name="int-array" value-type="Integer">
+ <array>
+ <value>1</value>
+ <value>2</value>
+ </array>
+ </property>
+ <property name="someXML"
+ value="<xml>
<t1 xmlns="http://www.acme.org/xmlns/other/v1.0.0">
<foo type='bar'>haha</foo>
</t1>
</xml>" />
+ </endpoint-description>
+</endpoint-descriptions>
diff --git a/trunk/discovery/local/src/test/resources/ed2.xml b/trunk/discovery/local/src/test/resources/ed2.xml
new file mode 100644
index 0000000..e3c42b9
--- /dev/null
+++ b/trunk/discovery/local/src/test/resources/ed2.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0"
+ xmlns:other="http://www.acme.org/xmlns/other/v1.0.0">
+ <endpoint-description>
+ <property name="objectClass">
+ <array>
+ <value>com.acme.HelloService</value>
+ </array>
+ </property>
+ <property name="service.intents">SOAP</property>
+ <property name="service.imported.configs" value="org.apache.cxf.ws"/>
+ <property name="endpoint.id">foo:bar</property>
+ </endpoint-description>
+ <endpoint-description>
+ <property name="service.imported.configs" value="org.apache.cxf.ws"/>
+ <property name="endpoint.id">foo:bar</property>
+ <property name="objectClass" value-type="String">
+ <array>
+ <value>com.acme.HelloService</value>
+ <value>some.other.Service</value>
+ </array>
+ </property>
+
+ <property name="someXML" value-type="String">
+ <!-- Literal XML to be parsed into the String -->
+ <xml>
+ <other:t1 xmlns="http://www.acme.org/xmlns/other/v1.0.0">
+ <foo type="bar">haha</foo>
+ </other:t1>
+ </xml>
+ </property>
+
+ <property name="long" value-type="long">9223372036854775807</property>
+ <property name="Long2" value-type="Long" value="-1"/>
+ <property name="double" value-type="double">1.7976931348623157E308</property>
+ <property name="Double2" value-type="Double">1.0</property>
+ <property name="float" value-type="float">42.24</property>
+ <property name="Float2" value-type="Float" value="1.0"/>
+ <property name="int" value-type="int">17</property>
+ <property name="Integer2" value-type="Integer" value="42"/>
+ <property name="byte" value-type="byte">127</property>
+ <property name="Byte2" value-type="Byte" value="-128"/>
+ <property name="boolean" value-type="boolean">true</property>
+ <property name="Boolean2" value-type="Boolean" value="true"/>
+ <property name="short" value-type="short">99</property>
+ <property name="Short2" value-type="Short" value="-99"/>
+ <property name="char" value-type="char">@</property>
+ <property name="Character2" value-type="Character" value="X"/>
+
+ <property name="bool-list" value-type="boolean">
+ <list>
+ <value>true</value>
+ <value>false</value>
+ </list>
+ </property>
+ <property name="long-set" value-type="long">
+ <set/> <!-- empty set -->
+ </property>
+ <property name="string-set">
+ <set>
+ <value>Hello there</value>
+ <value>How are you?</value>
+ </set>
+ </property>
+ <property name="int-array" value-type="int">
+ <array>
+ <value>1</value>
+ <value>2</value>
+ </array>
+ </property>
+ <property name="Integer-array" value-type="Integer">
+ <array>
+ <value>2</value>
+ <value>1</value>
+ </array>
+ </property>
+ <property name="service.exported.configs">
+ org.apache.cxf.ws
+ </property>
+
+ <property name="other1">
+ Hello
+ <other:t1/>
+ <!-- the above tag is a custom extension -->
+ </property>
+ <property name="other2">
+ <list>
+ <value>
+ <!-- A value specified as literal XML -->
+ <xml>
+ <other:t2/>
+ </xml>
+ </value>
+ </list>
+ <!-- This is a custom extension -->
+ <other:t1/>
+ </property>
+ </endpoint-description>
+</endpoint-descriptions>
diff --git a/trunk/discovery/local/src/test/resources/ed3.xml b/trunk/discovery/local/src/test/resources/ed3.xml
new file mode 100644
index 0000000..7d4d681
--- /dev/null
+++ b/trunk/discovery/local/src/test/resources/ed3.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0">
+ <endpoint-description>
+ <property name="objectClass">
+ <array>
+ <value>SomeService</value>
+ </array>
+ </property>
+ <property name="endpoint.id">http://somewhere:12345</property>
+ <property name="service.imported.configs" value="org.apache.cxf.ws"/>
+ </endpoint-description>
+</endpoint-descriptions>
diff --git a/trunk/discovery/local/src/test/resources/ed4.xml b/trunk/discovery/local/src/test/resources/ed4.xml
new file mode 100644
index 0000000..cef5d18
--- /dev/null
+++ b/trunk/discovery/local/src/test/resources/ed4.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+
+<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0"
+ xmlns:other="http://www.acme.org/xmlns/other/v1.0.0">
+ <endpoint-description>
+ <property name="objectClass">
+ <array>
+ <value>org.example.ClassA</value>
+ </array>
+ </property>
+ <property name="endpoint.id">http://somewhere</property>
+ <property name="service.imported.configs" value="org.apache.cxf.ws"/>
+ </endpoint-description>
+
+ <endpoint-description>
+ <property name="objectClass">
+ <array>
+ <value>org.example.ClassB</value>
+ </array>
+ </property>
+ <property name="endpoint.id">http://somewhere:1</property>
+ <property name="service.imported.configs" value="org.apache.cxf.ws"/>
+ </endpoint-description>
+</endpoint-descriptions>
diff --git a/trunk/discovery/local/src/test/resources/sd.xml b/trunk/discovery/local/src/test/resources/sd.xml
new file mode 100644
index 0000000..c476116
--- /dev/null
+++ b/trunk/discovery/local/src/test/resources/sd.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
+ <service-description>
+ <provide interface="org.apache.cxf.dosgi.samples.greeter.GreeterService" />
+ <property name="service.exported.interfaces">*</property>
+ <property name="service.exported.configs">org.apache.cxf.ws</property>
+ <property name="org.apache.cxf.ws.address">http://localhost:9090/greeter</property>
+ </service-description>
+</service-descriptions>
diff --git a/trunk/discovery/local/src/test/resources/sd2.xml b/trunk/discovery/local/src/test/resources/sd2.xml
new file mode 100644
index 0000000..5a156ea
--- /dev/null
+++ b/trunk/discovery/local/src/test/resources/sd2.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
+ <service-description>
+ <provide interface="org.example.SomeService" />
+ <property name="service.intents">confidentiality</property>
+ </service-description>
+ <service-description>
+ <provide interface="SomeOtherService" />
+ <provide interface="WithSomeSecondInterface" />
+ <property name="blah">5</property>
+ </service-description>
+</service-descriptions>
diff --git a/trunk/discovery/pom.xml b/trunk/discovery/pom.xml
new file mode 100644
index 0000000..659afd1
--- /dev/null
+++ b/trunk/discovery/pom.xml
@@ -0,0 +1,41 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-discovery</artifactId>
+ <packaging>pom</packaging>
+ <name>Distributed OSGI Discovery Service Modules</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+
+ <modules>
+ <module>local</module>
+ <module>distributed</module>
+ </modules>
+
+</project>
diff --git a/trunk/distribution/features/pom.xml b/trunk/distribution/features/pom.xml
new file mode 100644
index 0000000..aa02f51
--- /dev/null
+++ b/trunk/distribution/features/pom.xml
@@ -0,0 +1,90 @@
+<?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>
+ <artifactId>cxf-dosgi</artifactId>
+ <packaging>pom</packaging>
+ <name>CXF DOSGI Karaf Features</name>
+ <url>http://cxf.apache.org</url>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-distribution-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <dosgi.version>${project.version}</dosgi.version>
+ <topDirectoryLocation>../..</topDirectoryLocation>
+ </properties>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>${project.basedir}/src/main/resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>**/features.xml</include>
+ <include>**/*.cfg</include>
+ <include>**/*.properties</include>
+ </includes>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>filter</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>resources</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-artifacts</id>
+ <phase>package</phase>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>target/classes/features.xml</file>
+ <type>xml</type>
+ <classifier>features</classifier>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/distribution/features/src/main/resources/features.xml b/trunk/distribution/features/src/main/resources/features.xml
new file mode 100644
index 0000000..66eda10
--- /dev/null
+++ b/trunk/distribution/features/src/main/resources/features.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.0.0" name="cxf-dosgi-${project.version}">
+ <repository>mvn:org.apache.cxf.karaf/apache-cxf/${cxf.version}/xml/features</repository>
+
+ <feature name="cxf-dosgi-base" description="base libs needed outside karaf" version="${project.version}" resolver="(obr)">
+ <bundle start-level="8">mvn:org.ops4j.base/ops4j-base-lang/1.4.0</bundle>
+ <bundle start-level="8">mvn:org.ops4j.pax.logging/pax-logging-api/1.7.0</bundle>
+ <bundle start-level="8">mvn:org.ops4j.pax.logging/pax-logging-service/1.7.0</bundle>
+ <bundle start-level="10">mvn:org.apache.felix/org.apache.felix.configadmin/1.6.0</bundle>
+ <bundle start-level="11">mvn:org.apache.felix/org.apache.felix.fileinstall/3.2.6</bundle>
+ <bundle start-level="20">mvn:org.apache.aries/org.apache.aries.util/1.1.0</bundle>
+ <bundle start-level="20">mvn:org.apache.aries.proxy/org.apache.aries.proxy.api/1.0.0</bundle>
+ <bundle start-level="20">mvn:org.apache.aries.proxy/org.apache.aries.proxy.impl/1.0.1</bundle>
+ <bundle start-level="20">mvn:org.apache.aries.blueprint/org.apache.aries.blueprint.api/1.0.0</bundle>
+ <bundle start-level="20">mvn:org.apache.aries.blueprint/org.apache.aries.blueprint.core/1.1.0</bundle>
+ <bundle start-level="20">mvn:org.apache.aries.blueprint/org.apache.aries.blueprint.cm/1.0.1</bundle>
+ </feature>
+
+ <feature name="cxf-dosgi-core" version="${project.version}">
+ <feature>cxf-specs</feature>
+ <feature>cxf-jaxws</feature>
+ <feature>cxf-jaxrs</feature>
+ <feature>cxf-databinding-aegis</feature>
+ <feature>cxf-http-jetty</feature>
+ <feature>cxf-http</feature>
+ <bundle start-level="8">mvn:${project.groupId}/cxf-dosgi-ri-osgi-api/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/cxf-dosgi-ri-topology-manager/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/cxf-dosgi-ri-dsw-cxf/${project.version}</bundle>
+ </feature>
+
+ <feature name="cxf-dosgi-discovery-local" version="${project.version}">
+ <feature>cxf-dosgi-core</feature>
+ <bundle>mvn:${project.groupId}/cxf-dosgi-ri-discovery-local/${project.version}</bundle>
+ </feature>
+
+ <feature name="cxf-dosgi-discovery-distributed" version="${project.version}">
+ <feature>cxf-dosgi-core</feature>
+ <bundle>mvn:org.apache.zookeeper/zookeeper/${zookeeper.version}</bundle>
+ <bundle>mvn:${project.groupId}/cxf-dosgi-ri-discovery-local/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/cxf-dosgi-ri-discovery-distributed/${project.version}</bundle>
+ </feature>
+
+ <feature name="cxf-dosgi-zookeeper-server">
+ <bundle>mvn:org.apache.zookeeper/zookeeper/${zookeeper.version}</bundle>
+ <bundle>mvn:${project.groupId}/cxf-dosgi-ri-discovery-distributed-zookeeper-server/${project.version}</bundle>
+ </feature>
+</features>
diff --git a/trunk/distribution/multi-bundle/pom.xml b/trunk/distribution/multi-bundle/pom.xml
new file mode 100644
index 0000000..a76a1dc
--- /dev/null
+++ b/trunk/distribution/multi-bundle/pom.xml
@@ -0,0 +1,121 @@
+<?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>
+ <artifactId>cxf-dosgi-ri-multibundle-distribution</artifactId>
+ <name>Distributed OSGI Multi-Bundle Distribution</name>
+ <url>http://cxf.apache.org</url>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-distribution-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <dosgi.version>${project.version}</dosgi.version>
+ <topDirectoryLocation>../..</topDirectoryLocation>
+ </properties>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.karaf.tooling</groupId>
+ <artifactId>karaf-maven-plugin</artifactId>
+ <version>3.0.0</version>
+
+ <executions>
+ <execution>
+ <id>features-add-to-repository</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>features-export-meta-data</goal>
+ <goal>features-add-to-repository</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>mvn:org.apache.karaf.assemblies.features/standard/2.3.3/xml/features</descriptor>
+ <descriptor>mvn:org.apache.cxf.dosgi/cxf-dosgi/${project.version}/xml/features</descriptor>
+ </descriptors>
+ <features>
+ <feature>cxf-dosgi-base</feature>
+ <feature>cxf-dosgi-discovery-distributed</feature>
+ <feature>cxf-dosgi-zookeeper-server</feature>
+ </features>
+ <repository>target/distribution_bundles</repository>
+ <resolveDefinedRepositoriesRecursively>true</resolveDefinedRepositoriesRecursively>
+ <flatRepoLayout>true</flatRepoLayout>
+ <mergedFeature>true</mergedFeature>
+ <karafVersion>2.3.2</karafVersion>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <configuration>
+ <target>
+ <xslt processor="trax" in="target/features.xml" out="target/filtered-features.xml" style="src/main/xsl/filter_features.xslt" />
+ <xslt processor="trax" in="target/filtered-features.xml" out="target/distribution_configs/felix.config.properties.append" style="src/main/xsl/felix_distro_config.xslt" />
+ <xslt processor="trax" in="target/filtered-features.xml" out="target/distribution_configs/config.ini" style="src/main/xsl/equinox_distro_config.xslt" />
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>./src/main/assembly/assembly.xml</descriptor>
+ </descriptors>
+ </configuration>
+
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/distribution/multi-bundle/src/main/appended-resources/META-INF/LICENSE.vm b/trunk/distribution/multi-bundle/src/main/appended-resources/META-INF/LICENSE.vm
new file mode 100644
index 0000000..8569572
--- /dev/null
+++ b/trunk/distribution/multi-bundle/src/main/appended-resources/META-INF/LICENSE.vm
@@ -0,0 +1,39 @@
+##
+## 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.
+##
+## $Date: 2008-03-09 23:17:06 -0700 (Sun, 09 Mar 2008) $ $Rev: 635446 $
+##
+
+
+The $projectName
+includes a number of components and libraries with separate
+copyright notices and license terms. Your use of those components are
+subject to the terms and conditions of the following licenses.
+#set ( $apacheTxt = "The Apache Software License, Version 2.0" )
+
+#foreach ( $project in $projects )
+#foreach ( $license in $project.licenses)
+#if ( ! ($apacheTxt == $license.name) )
+
+$project.name #if ($project.url)($project.url)#end $project.artifact
+ License: $license.name #if ($license.url) ($license.url)#end
+
+#end
+#end
+#end
+
diff --git a/trunk/distribution/multi-bundle/src/main/appended-resources/META-INF/NOTICE b/trunk/distribution/multi-bundle/src/main/appended-resources/META-INF/NOTICE
new file mode 100644
index 0000000..f380729
--- /dev/null
+++ b/trunk/distribution/multi-bundle/src/main/appended-resources/META-INF/NOTICE
@@ -0,0 +1,42 @@
+This product contains interfaces and specifications Copyright The OSGi Alliance.
+
+
+This product also includes schemas and specification developed by:
+ - the W3C consortium (http://www.w3c.org)
+ (http://www.w3.org/XML/1998/namespace)
+
+
+This product also includes WS-* schemas developed by International
+ Business Machines Corporation, Microsoft Corporation, BEA Systems,
+ TIBCO Software, SAP AG, Sonic Software, and VeriSign
+ (http://schemas.xmlsoap.org/wsdl/2003-02-11.xsd)
+ (http://schemas.xmlsoap.org/ws/2004/08/addressing/)
+ (http://schemas.xmlsoap.org/wsdl/http)
+ (http://schemas.xmlsoap.org/ws/2005/02/rm/wsrm.xsd)
+ (http://www.w3.org/2005/08/addressing/ws-addr.xsd)
+
+The product contains code (StaxBuilder.java) that is
+ Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
+ All rights reserved.
+
+Java classes (source and binary) under org.apache.cxf.jaxws.javaee
+are generated from schema available here:
+(http://java.sun.com/xml/ns/javaee/javaee_5.xsd)
+
+
+Portions of the included XmlSchema library are Copyright 2006 International Business Machines Corp.
+
+
+Portions of the included xml-apis library were originally based on the following:
+ - software copyright (c) 1999, IBM Corporation., http://www.ibm.com.
+ - software copyright (c) 1999, Sun Microsystems., http://www.sun.com.
+ - software copyright (c) 2000 World Wide Web Consortium, http://www.w3.org
+
+
+Portions of the included xmlbeans library were originally based on the following:
+ - software copyright (c) 2000-2003, BEA Systems, <http://www.bea.com/>.
+
+This product includes xmlunit
+Copyright (c) 2001-2007, Jeff Martin, Tim Bacon
+All rights reserved.
+
diff --git a/trunk/distribution/multi-bundle/src/main/assembly/assembly.xml b/trunk/distribution/multi-bundle/src/main/assembly/assembly.xml
new file mode 100644
index 0000000..2844317
--- /dev/null
+++ b/trunk/distribution/multi-bundle/src/main/assembly/assembly.xml
@@ -0,0 +1,54 @@
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/component-1.1.2.xsd">
+<!--
+ 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.
+-->
+ <id>dir</id>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ <format>dir</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <fileSets>
+ <fileSet>
+ <directory>target/distribution_bundles</directory>
+ <outputDirectory>apache-cxf-dosgi-ri-${dosgi.version}/dosgi_bundles</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>target/distribution_configs</directory>
+ <outputDirectory>apache-cxf-dosgi-ri-${dosgi.version}/conf</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>target/maven-shared-archive-resources/META-INF</directory>
+ <excludes>
+ <exclude>DEPENDENCIES</exclude>
+ </excludes>
+ <outputDirectory>apache-cxf-dosgi-ri-${dosgi.version}</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/release</directory>
+ <includes>
+ <include>README</include>
+ <include>release_notes.txt</include>
+ </includes>
+ <outputDirectory>apache-cxf-dosgi-ri-${dosgi.version}</outputDirectory>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/trunk/distribution/multi-bundle/src/main/release/README b/trunk/distribution/multi-bundle/src/main/release/README
new file mode 100644
index 0000000..40d7b0d
--- /dev/null
+++ b/trunk/distribution/multi-bundle/src/main/release/README
@@ -0,0 +1,63 @@
+Welcome to the Apache CXF Distributed OSGi DSW Reference Implementation
+=======================================================================
+
+
+The dOSGi subproject of Apache CXF provides the Reference Implementation
+of the Remote Services Specification version 1.0, Chapter 13 in the OSGi
+Compendium Specification, and OSGi Remote Service Admin Specification
+version 1.0, Chapter 122 in the OSGi Enterprise Specification[1].
+
+
+This release is provided for your convenience in three different
+distribution formats:
+
+1. Multi-bundle distro: cxf-dosgi-ri-multibundle-distribution-1.2.{tar.gaz|zip}
+
+Contains the dOSGi implementation and all 3rd party dependencies as
+separate bundles within an archive. The current distribution is an
+instance of this type. The easiest way to get started is to explode
+this archive into your Felix or Equinox installation tree. Then
+append the contents of the felix.config.properties.append or
+equinox.config.ini.append snippets found in the conf directory to
+your felix.config.properties or equinox.config.ini. This will cause
+all the required bundles to be loaded on start-up. The bundles required
+for the Zookeeper-based Discovery Service implementation are also provided
+in this distribution, and similarly may be automatically loaded by
+appending the felix.discovery.config.properties.append or
+equinox.discovery.config.ini.append snippets as appropriate.
+
+
+2. Single-bundle distro: cxf-dosgi-ri-singlebundle-distribution-1.2.jar
+
+Contains the dOSGi implementation and all 3rd party dependencies wrapped
+in a single OSGi bundle so as to allow direct installation in your favourite
+OSGi container in one fell swoop.
+
+
+3. Source distro: cxf-dosgi-ri-source-distribution-1.2.{tar.gz|zip}
+
+Contains the entire source tree for dOSGi.
+
+
+The best starting point for using dOSGi is the Getting Started Guide[2].
+
+Also note the very detailed walk-through of the greeter demo[3].
+
+If you need more help, or want to provide any feedback, please feel free
+to drop us a note on the CXF dev or users list[4].
+
+If you trip over any problems with dOSGi, don't hesitate to submit an issue
+to the CXF JIRA[5] with the component set to "Distributed-OSGi".
+
+
+Thanks for using dOSGi!
+
+Regards,
+The CXF dOSGi team.
+
+
+[1] see chapter 122 http://www.osgi.org/Download/Release4V42
+[2] http://cxf.apache.org/distributed-osgi.html#DistributedOSGi-GettingStarted
+[3] http://cxf.apache.org/distributed-osgi-greeter-demo-walkthrough.htm
+[4] http://cxf.apache.org/mailing-lists.html
+[5] https://issues.apache.org/jira/browse/CXF
diff --git a/trunk/distribution/multi-bundle/src/main/release/release_notes.txt b/trunk/distribution/multi-bundle/src/main/release/release_notes.txt
new file mode 100644
index 0000000..889952e
--- /dev/null
+++ b/trunk/distribution/multi-bundle/src/main/release/release_notes.txt
@@ -0,0 +1,295 @@
+
+Release Notes - CXF Distributed OSGi - Version 1.6.0
+====================================================
+
+** Bug
+ * [DOSGI-11] - dOSGi creates new databinding instance instead of using a spring-loaded databinding if available
+ * [DOSGI-196] - Greeter demo does not work in standalone Felix
+ * [DOSGI-198] - Imported service is gone after client bundle is restarted
+ * [DOSGI-206] - Parse errors for xml files in discovery local and dsw
+ * [DOSGI-207] - Update karaf maven plugin to release version
+ * [DOSGI-208] - OSGi compendium bundle installed by feature causes problems
+ * [DOSGI-210] - Service registration and memory leaks
+
+** Improvement
+ * [DOSGI-201] - Create DSOGi distro from karaf feature file
+ * [DOSGI-202] - Make DOSGi independent of jdom
+ * [DOSGI-203] - Upgrade to cxf 2.7.6
+ * [DOSGI-204] - Update osgi spec version to 4.3.1 and felix to 4.2.1
+ * [DOSGI-205] - Upgrade to pax exam 3.2
+
+** Task
+ * [DOSGI-212] - Update CXF version to 2.7.8
+
+
+Release Notes - CXF Distributed OSGi - Version 1.5.0
+====================================================
+
+** Bug
+ * [DOSGI-158] - NPE on shutdown of DOSGi service
+ * [DOSGI-160] - RemoteServiceAdmin shuts itself down during startup
+ * [DOSGI-161] - services sometimes don't get exported
+ * [DOSGI-162] - Compilation errors when using OSGi core
+ * 4.3.0/4.3.1/5.0.0
+ * [DOSGI-164] - NullPointerException on export
+ * [DOSGI-165] - exported service is not properly closed and cannot be
+ * restarted
+ * [DOSGI-166] - List can not be used to register CXF providers with DSW
+ * [DOSGI-168] - RemoteServiceAdminCore service parameters handling bugs
+ * [DOSGI-172] - o.a.c.d.discovery.zookeeper package classes are not
+ * properly synchronized
+ * [DOSGI-173] - unregistering an exported service does not remove it
+ * from zookeeper (and remote clients)
+ * [DOSGI-174] - synchronization issues and resource leaks in
+ * TopologyManagerImport and related classes
+ * [DOSGI-175] - TopologyManagerImport's reference counter doesn't count
+ * [DOSGI-176] - zookeeper discovery sending multiple duplicate endpoint
+ * notifications
+ * [DOSGI-177] - stopped services still appear as available to clients
+ * [DOSGI-180] - CXF service does not disappear if exporting bundle is
+ * stopped
+ * [DOSGI-188] - services aren't re-imported after RemoteServiceAdmin
+ * restart
+ * [DOSGI-190] - NodeExistsException and missing endpoint after ZooKeeper
+ * is restarted
+ * [DOSGI-191] - ZooKeeperDiscovery instance reconnects to ZooKeeper
+ * after bundle is stopped
+ * [DOSGI-192] - Upgrade to zookeeper 3.3.2 to fix bug with zk event
+ * thread shutdown
+ * [DOSGI-195] - Exceptions in tests: ClassCastException on
+ * SpringBusFactory
+
+** Improvement
+ * [DOSGI-167] - Upgrade Aries to the 1.x version for distro
+ * [DOSGI-170] - Remove single bundle distro
+ * [DOSGI-181] - Reactivate or delete old systests
+ * [DOSGI-184] - Split Endpoint repository from TopologyManagerExport
+ * [DOSGI-193] - Split discovery.zookeeper package into subpackages
+
+
+Release Notes - CXF Distributed OSGi - Version 1.4.0
+====================================================
+
+** Bug
+ * [DOSGI-10] - Spring schema handling (intents) doesn't work properly
+ * for the single-bundle case
+ * [DOSGI-18] - RESTful Proxies can not be created in multibundle DOSGI
+ * distributions
+ * [DOSGI-63] - The discovery can be used be used before the connection
+ * to the server is completely established
+ * [DOSGI-69] - CXF-DOSGi requires internet access when reading XML
+ * [DOSGI-90] - Do not use/assume that endpoint.id is an address
+ * [DOSGI-92] - Exception : Applying intent: SOAP via binding config
+ * [DOSGI-109] - NullPointerException in ToloplogyManager during bundle
+ * stop
+ * [DOSGI-110] - Unable to export multiple services
+ * [DOSGI-111] - DOSGi bundle attempts to load WSDL using wrong bundle in
+ * WSDL-first configuration
+ * [DOSGI-113] - Integration with pax-logging not possible
+ * [DOSGI-114] - RemoteServiceAdmin is not available warnings in DOSGi
+ * 1.3
+ * [DOSGI-116] - Build fails when downloading Zookeeper artifacts
+ * [DOSGI-119] - Single bundle activator fails to stop all the bundles if
+ * one of the activators throws an exception
+ * [DOSGI-120] - NullPointerException on export
+ * [DOSGI-121] - Fix logging: System.out prints, printStackTrace, verbose
+ * logs
+ * [DOSGI-123] - ZooKeeper registrations are not recreated on ZooKeeper
+ * server restart
+ * [DOSGI-125] - The dead lock in TopologyManagerImport
+ * [DOSGI-129] - NPE when stopping a bundle that exports a DOSGI service
+ * [DOSGI-135] - Switch logging api to slf4j
+ * [DOSGI-136] - Refactor zookeeper server and add metatype config
+ * [DOSGI-137] - Possible bug in TopologyManagerImport when checking if
+ * an Endpoint is already imported
+ * [DOSGI-142] - Upgrade CXF to 2.7.0
+ * [DOSGI-145] - Multiple services using HTTP Service and published from
+ * the same bundle do not work
+ * [DOSGI-150] - Update to Java 1.6
+ * [DOSGI-153] - Error starting greeter sample in karaf 2.3.0 in aegis
+ * setup: ExceptionInInitializerError ... failed to create an
+ * XPathFactory for the default object model:
+ * http://java.sun.com/jaxp/xpath/dom
+ * [DOSGI-154] - Problems with api packages since update to cxf 2.7.2
+
+** Improvement
+ * [DOSGI-70] - Reconnect automatically to Zookeeper after a connection
+ * loss / timeout
+ * [DOSGI-86] - Decouple DOSGi DSW from Spring DM.
+ * [DOSGI-126] - Allow to use the servlet transport with automatic
+ * discovery
+ * [DOSGI-127] - Default address for services should use the servlet
+ * transport
+ * [DOSGI-128] - Allow to use JAXWS/JAXB service without frontend and
+ * databinding properties
+ * [DOSGI-130] - Clean up unused code and fix warnings, use interfaces
+ * where possible in DSW
+ * [DOSGI-131] - Switch slf4j from springsource bundles to org.slf4j,
+ * update slf4j version
+ * [DOSGI-132] - Switch logging at runtime to pax logging
+ * [DOSGI-133] - Create a Karaf feature file for CXF DOSGi
+ * [DOSGI-134] - Refactoring of TopologyManager
+ * [DOSGI-138] - Refactoring of discovery distributed
+ * [DOSGI-139] - Refactor dsw-cxf to remove dep cycles
+ * [DOSGI-141] - ZooKeeper is not support cluster (ensemble) mode
+ * [DOSGI-143] - Remove all repos except for apache snapshot and central
+ * [DOSGI-146] - Allow to define intents as OSGi services
+ * [DOSGI-147] - Deprecate some Constants that are defined in the spec
+ * [DOSGI-148] - Refactoring of dsw ConfigTypeHandlers
+ * [DOSGI-149] - Optimize distributions and karaf feature
+
+** New Feature
+ * [DOSGI-115] - Use Spring DM and Eclipse Gemini Blueprint with DOSGi
+
+Apache CXF Distributed OSGi 1.3.1 Release Notes
+===============================================
+
+Fixes needed in order to pass the OSGi 4.3 Remote Service Admin TCK.
+* Fixed exports from Single Bundle Distro
+* Fixed deadlock
+* Fixed cleanup
+* Fixed ExportReferenceImpl.equals() and hashCode()
+* Fixed RemoteServiceAdminCore.exportService()
+
+Apache CXF Distributed OSGi 1.3 Release Notes
+=============================================
+
+The following modules have been removed from the destribution:
+
+* org.apache.cxf.dosgi:cxf-dosgi-ri-discovery-distributed-zookeeper-wrapper
+
+ Zookeeper 3.3.1 bundle is now available. See DOSGI-100 for more information.
+
+* org.apache.cxf.dosgi:cxf-dosgi-remote-service-admin-interfaces
+
+ org.osgi:org.osgi.enterprize:4.2.0 artifact is now available. See DOSGI-104 for more information.
+
+Many dependencies have been updated, including the update to CXF 2.5.1. See also DOSGI-96.
+
+Additionally, a number of bugs has been fixed including:
+
+DOSGI-108 service.exported.interfaces doesn't support comma-seperated String value
+DOSGI-107 Support for a 'wsdl' configuration type
+DOSGI-105 Update greeter_rest demo to use OSGI HttpService
+DOSGI-103 Improve multi-bundle distro configuration for Felix
+DOSGI-102 DOSGI RI can not map invocation exceptions to custom exceptions on the client side
+DOSGI-97 Automatically unregister HTTP servlets/resources if exported service goes down
+DOSGI-94 Enhancement to deal with registered services that might have been proxied
+DOSGI-91 DOSGI RS Proxies ignore ResponseExceptionMapper providers
+DOSGI-85 Unable to export services started after Topology manager
+DOSGI-82 Populate custom servicename, portname and targetnamespace for jax-ws
+DOSGI-79 RemoteServiceAdmin.getImportedEndpoints() returns collection of incorrect type
+DOSGI-77 NullPointerException from Distributed OSGI when bundle with wanted properties is started.
+DOSGI-76 EndpointListener.endpointRemoved
+DOSGI-75 CXF Distributed OSGi isn't using org.apache.cxf.common.logging.LogUtils
+
+
+Apache CXF Distributed OSGi 1.2 Release Notes
+=============================================
+
+In addition to providing the Reference Implementation to the OSGi Remote
+Services Specification, the CXF Distributed OSGi 1.2 release now also
+provides the Reference Implementation of the OSGi Remote Service Admin
+Specification version 1.0, Chapter 122 in the OSGi Enterprise
+Specification.
+
+To achieve compliance with the RSA specification a major refactoring has
+been done on the code base.
+
+For the new features applicable to the RSA specification, see chapter
+122 in the OSGi Enterprise Spec (http://www.osgi.org/Download/Release4V42).
+
+The following new features have been introduced:
+
+* org.apache.cxf.ws.port=[port number]
+ This configuration property can be used to change the default port
+ at which a remote service appears. When setting this property the
+ default context will still be used.
+
+* Servlet Filters (javax.servlet.Filter) can now be registered as OSGi
+ services with the "org.apache.cxf.httpservice.filter" boolean
+ property set to true and used to secure DOSGi server endpoints.
+ Endpoints can enforce the registration of the filters by setting an
+ "org.apache.cxf.httpservice.requirefilter" boolean property to true
+
+Additionally, a number of bugs has been fixed including:
+
+[DOSGI-13] - The CXF DOSGi implementation needs to be updated to support the
+ latest OSGi Remote Services Admin spec.
+[DOSGI-24] - org.apache.cxf.dosgi.dsw.ClassUtils#getInterfaceClass() method
+ should search through super class interfaces too
+[DOSGI-25] - FileNotFoundException when client-side proxy is being created
+ [META-INF/cxf/cxf.xml]
+[DOSGI-27] - Discovery problem when two dependent bundles export interfaces
+[DOSGI-28] - Consuming more than 1 service using Spring-DM doesn't work
+[DOSGI-29] - Exception when stopping DOSGi bundle
+[DOSGI-30] - ClassNotFoundException when exposing service.
+[DOSGI-31] - Distributed OSGi having a problem with a custom type method
+ argument
+[DOSGI-32] - The default amount of logging should be significantly reduced
+[DOSGI-33] - Exception when exposing remote service using DOSGi
+[DOSGI-34] - org.apache.servicemix.specs.locator-1.1.1.jar useless?
+[DOSGI-35] - ServicePublication.PROP_KEY_ENDPOINT_LOCATION is now a URI,
+ instead of a URL
+[DOSGI-37] - Fix the dependency on Equinox for the DOSGi system tests
+[DOSGI-38] - single-bundle distribution has incorrect Import-Package and
+ Export-Package declaration in the manifest
+[DOSGI-40] - Remoted service fails to register endpoint after framework is
+ restarted
+[DOSGI-41] - Remove simple-pojo demo (temporarily) as its currently not
+ supported
+[DOSGI-43] - ClassCastException with Declarative Services
+[DOSGI-44] - Existing OSGi Services are not remoted when CXF-DOSGi is started
+[DOSGI-50] - Need to automatically infer SOAP/HTTP transport intents if not
+ explicily set via osgi.remote.requires.intents
+[DOSGI-54] - RemoteServiceAdmin interfaces/classes out of sync with official
+ version
+[DOSGI-61] - The Zookeeper Discovery only supports primitive types as service
+ properties
+[DOSGI-62] - The DSW creates endpoints with localhost URLs
+[DOSGI-66] - The DSW only loads the intent map when certain spring bundles
+ are loaded and started upfront
+[DOSGI-67] - Enable filters on DOSGi endpoints
+[DOSGI-72] - DOSGI not working with HTTP Service
+[DOSGI-74] - Update CXF version to 2.2.9
+
+
+Apache CXF Distributed OSGi 1.1 Release Notes
+=============================================
+
+Specific issues, features, and improvements fixed in this version
+-----------------------------------------------------------------
+
+The Distributed OSGi 1.1 release provides the Reference Implementation
+of the Remote Services Specification version 1.0. Chapter 13 in the OSGi
+Compendium Specification (http://www.osgi.org/Download/Release4V42).
+
+New features in this release include:
+
+* A live Discovery System is now supported. The CXF-DOSGi implementation
+ makes use of Apache Zookeeper (http://hadoop.apache.org/zookeeper) as
+ the Discovery Server and provides client-side bundles for transparent
+ interaction with Zookeeper. See the Discovery Documentation pages
+ (http://cxf.apache.org/dosgi-discovery.html) for more details.
+
+* REST support for JAX-RS-based Remoted Services and Consumers through
+ the org.apache.cxf.rs configuration type.
+
+* Many user issues have been addressed. In addition the following bugs
+ have been fixed:
+
+[CXF-2182] - Exceptions when remoting pre-existing service
+[CXF-2337] - org.apache.cxf.dosgi.dsw.ClassUtils#getInterfaceClass() method
+ should search through super class interfaces too
+[CXF-2435] - Distributed OSGi having a problem with a custom type method
+ argument
+[CXF-2288] - Bundle cannot be restarted
+[CXF-2385] - Discovery doesn't fully translate 'localhost' into a proper
+ machine name
+[CXF-2200] - Consuming more than 1 service using Spring-DM doesn't work
+
+
+Known limitations :
+
+* Schema validation can not be done for JAX-RS-based endpoints which use
+ Aegis databinding
diff --git a/trunk/distribution/multi-bundle/src/main/xsl/equinox_distro_config.xslt b/trunk/distribution/multi-bundle/src/main/xsl/equinox_distro_config.xslt
new file mode 100644
index 0000000..d7db137
--- /dev/null
+++ b/trunk/distribution/multi-bundle/src/main/xsl/equinox_distro_config.xslt
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes"/>
+<xsl:template match="/">
+# equinox config
+org.ops4j.pax.web.session.timeout=30
+
+osgi.bundles=org.eclipse.osgi.services@start, \
+../plugins/org.eclipse.equinox.console_1.0.0.v20120522-1841.jar@start, \
+../plugins/org.apache.felix.gogo.shell_0.8.0.v201110170705.jar@start, \
+../plugins/org.apache.felix.gogo.command_0.8.0.v201108120515.jar@start, \
+../plugins/org.apache.felix.gogo.runtime_0.8.0.v201108120515.jar@start, \
+<xsl:for-each select="//bundle"><xsl:sort select="@start-level" data-type="number" order="ascending"/>../dosgi_bundles/<xsl:value-of select="@name"/>@start,\
+</xsl:for-each>
+ </xsl:template>
+</xsl:transform>
+
diff --git a/trunk/distribution/multi-bundle/src/main/xsl/felix_distro_config.xslt b/trunk/distribution/multi-bundle/src/main/xsl/felix_distro_config.xslt
new file mode 100644
index 0000000..eeb88b5
--- /dev/null
+++ b/trunk/distribution/multi-bundle/src/main/xsl/felix_distro_config.xslt
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:output method="text" version="1.0" encoding="UTF-8" indent="yes"/>
+ <xsl:template match="/">
+org.ops4j.pax.web.session.timeout=30
+org.osgi.framework.startlevel.beginning=200
+
+<xsl:for-each select="//bundle">
+<xsl:sort select="@start-level" data-type="number" order="ascending"/>
+<xsl:variable name="i" select="position() + count(//bundles/felix_deps) + 50"/>
+felix.auto.start.<xsl:value-of select="$i"/>=file:dosgi_bundles/<xsl:value-of select="@name"/>
+</xsl:for-each>
+
+org.osgi.framework.system.packages=org.osgi.framework; version=1.5.0, \
+ org.osgi.framework.launch; version=1.0.0, \
+ org.osgi.framework.hooks.service; version=1.0.0, \
+ org.osgi.framework.wiring; version=1.0.0 , \
+ org.osgi.service.packageadmin; version=1.2.0, \
+ org.osgi.service.startlevel; version=1.1.0, \
+ org.osgi.service.url; version=1.0.0, \
+ org.osgi.util.tracker; version=1.4.0, \
+ org.apache.karaf.jaas.boot; version=2.2.9, \
+ org.apache.karaf.version; version=2.2.9, \
+ javax.annotation.processing, \
+ javax.crypto, \
+ javax.crypto.spec, \
+ javax.imageio, \
+ javax.imageio.stream, \
+ javax.lang.model, \
+ javax.lang.model.element, \
+ javax.lang.model.type, \
+ javax.lang.model.util, \
+ javax.naming, \
+ javax.xml.bind.annotation, \
+ javax.xml.datatype, \
+ javax.xml.parsers, \
+ javax.xml.namespace, \
+ javax.xml.transform, \
+ javax.xml.transform.dom, \
+ javax.xml.transform.sax, \
+ javax.xml.transform.stream, \
+ javax.xml.validation, \
+ javax.xml.xpath, \
+ javax.management, \
+ javax.management.modelmbean, \
+ javax.management.remote, \
+ javax.naming.directory, \
+ javax.naming.spi, \
+ javax.net, \
+ javax.net.ssl, \
+ javax.security.auth, \
+ javax.security.auth.callback, \
+ javax.security.auth.login, \
+ javax.security.auth.spi, \
+ javax.security.auth.x500, \
+ javax.security.cert, \
+ javax.sql, \
+ javax.swing, \
+ javax.swing.border, \
+ javax.swing.tree, \
+ javax.tools, \
+ javax.transaction; javax.transaction.xa; partial=true; mandatory:=partial, \
+ javax.xml.transform.stax, \
+ javax.wsdl, \
+ javax.wsdl.extensions, \
+ org.ietf.jgss, \
+ org.xml.sax, \
+ org.xml.sax.ext, \
+ org.xml.sax.helpers, \
+ org.w3c.dom, \
+ org.w3c.dom.bootstrap, \
+ org.w3c.dom.ls
+
+ </xsl:template>
+</xsl:transform>
diff --git a/trunk/distribution/multi-bundle/src/main/xsl/filter_features.xslt b/trunk/distribution/multi-bundle/src/main/xsl/filter_features.xslt
new file mode 100644
index 0000000..3044a55
--- /dev/null
+++ b/trunk/distribution/multi-bundle/src/main/xsl/filter_features.xslt
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:transform version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:output method="xml" version="1.0" encoding="UTF-8"
+ indent="yes" />
+
+ <!-- Filter out undesired bundles -->
+ <xsl:template match="bundle[@artifactId='cxf-karaf-commands']"></xsl:template>
+
+ <!-- Copy the rest unachanged -->
+ <xsl:template match="@* | node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:transform>
diff --git a/trunk/distribution/pom.xml b/trunk/distribution/pom.xml
new file mode 100644
index 0000000..320fc8a
--- /dev/null
+++ b/trunk/distribution/pom.xml
@@ -0,0 +1,45 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-distribution-parent</artifactId>
+ <version>1.6.0</version>
+ <packaging>pom</packaging>
+ <name>Distributed OSGI Distribution Parent</name>
+ <url>http://cxf.apache.org</url>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ </properties>
+
+ <modules>
+ <module>features</module>
+ <module>multi-bundle</module>
+ <module>sources</module>
+ </modules>
+</project>
diff --git a/trunk/distribution/sources/pom.xml b/trunk/distribution/sources/pom.xml
new file mode 100644
index 0000000..8b82857
--- /dev/null
+++ b/trunk/distribution/sources/pom.xml
@@ -0,0 +1,66 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-source-distribution</artifactId>
+ <version>1.6.0</version>
+ <name>Distributed OSGI Source Distribution</name>
+ <url>http://cxf.apache.org</url>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-distribution-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <maven.test.skip>true</maven.test.skip>
+ <dosgi.version>${project.version}</dosgi.version>
+ <topDirectoryLocation>../..</topDirectoryLocation>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>distribution-package</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/src.xml</descriptor>
+ </descriptors>
+ <tarLongFileMode>gnu</tarLongFileMode>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/trunk/distribution/sources/src/main/assembly/src.xml b/trunk/distribution/sources/src/main/assembly/src.xml
new file mode 100644
index 0000000..493e414
--- /dev/null
+++ b/trunk/distribution/sources/src/main/assembly/src.xml
@@ -0,0 +1,68 @@
+<?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.
+-->
+<assembly>
+ <id>dist</id>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <formats>
+ <format>tar.gz</format>
+ <format>zip</format>
+ </formats>
+
+ <fileSets>
+ <fileSet>
+ <directory>../..</directory>
+ <outputDirectory>apache-cxf-dosgi-ri-${dosgi.version}</outputDirectory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ <excludes>
+ <exclude>**/target</exclude>
+ <exclude>**/target/**/*</exclude>
+ <exclude>**/build</exclude>
+ <exclude>**/build/**/*</exclude>
+ <exclude>**/.settings</exclude>
+ <exclude>**/.pmd</exclude>
+ <exclude>**/.checkstyle</exclude>
+ <exclude>**/.ruleset</exclude>
+ <exclude>**/pmd-eclipse.log</exclude>
+ <exclude>**/.classpath</exclude>
+ <exclude>**/.project</exclude>
+ <exclude>**/.wtpmodules</exclude>
+ <exclude>**/surefire*</exclude>
+ <exclude>**/cobertura.ser</exclude>
+ <exclude>**/velocity.log</exclude>
+ <exclude>**/var/journal</exclude>
+ <exclude>**/build.out*</exclude>
+ <exclude>**/*.swp</exclude>
+ <exclude>**/org.eclipse.jdt.core.prefs</exclude>
+ </excludes>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/release</directory>
+ <includes>
+ <include>LICENSE</include>
+ <include>NOTICE</include>
+ <include>README</include>
+ <include>release_notes.txt</include>
+ </includes>
+ <outputDirectory>apache-cxf-dosgi-ri-${dosgi.version}</outputDirectory>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/trunk/distribution/sources/src/main/release/LICENSE b/trunk/distribution/sources/src/main/release/LICENSE
new file mode 100644
index 0000000..172feda
--- /dev/null
+++ b/trunk/distribution/sources/src/main/release/LICENSE
@@ -0,0 +1,262 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
+
+
+The Apache CXF Distributed OSGi DSW Reference Implementation
+includes a number of components and libraries with separate
+copyright notices and license terms. Your use of those components are
+subject to the terms and conditions of the following licenses.
+
+
+AOP alliance (http://aopalliance.sourceforge.net) aopalliance:aopalliance:jar:1.0
+ License: Public Domain
+
+Unnamed - asm:asm:jar:2.2.3 (http://asm.objectweb.org/asm/asm) asm:asm:jar:2.2.3:compile
+ License: BSD (http://asm.objectweb.org/license.html)
+
+Legion of the Bouncy Castle Java Cryptography APIs (http://www.bouncycastle.org/java.html) bouncycastle:bcprov-jdk15:jar:140
+ License: Bouncy Castle License (http://www.bouncycastle.org/licence.html)
+
+Sun JAXB Reference Implementation Runtime com.sun.xml.bind:jaxb-impl:jar:2.1.9:compile
+ License: COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 (http://www.sun.com/cddl/cddl.html)
+
+Sun JAXB Reference Implementation Tools com.sun.xml.bind:jaxb-xjc:jar:2.1.9:compile
+ License: COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 (http://www.sun.com/cddl/cddl.html)
+
+Sun SAAJ Reference Implementation com.sun.xml.messaging.saaj:saaj-impl:jar:1.3.2:compile
+ License: COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 (http://www.sun.com/cddl/cddl.html)
+
+JavaBeans Activation Framework (JAF) (http://java.sun.com/products/javabeans/jaf/index.jsp) javax.activation:activation:jar:1.1
+ License: Common Development and Distribution License (CDDL) v1.0 (https://glassfish.dev.java.net/public/CDDLv1.0.html)
+
+Java Architecture for XML Binding (JAXB API) javax.xml.bind:jaxb-api:jar:2.1:compile
+ License: COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 (http://www.sun.com/cddl/cddl.html)
+
+Sun SAAJ API (http://java.sun.com/webservices/saaj/index.jsp/saaj-api) javax.xml.soap:saaj-api:jar:1.3:compile
+ License: COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 (http://www.sun.com/cddl/cddl.html)
+
+Streaming API for XML javax.xml.stream:stax-api:jar:1.0-2
+ License: GNU General Public Library (http://www.gnu.org/licenses/gpl.txt)
+
+Streaming API for XML javax.xml.stream:stax-api:jar:1.0-2
+ License: COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 (http://www.sun.com/cddl/cddl.html)
+
+Jetty Server (http://jetty.mortbay.org/project/modules/jetty) org.mortbay.jetty:jetty:jar:6.1.16
+ License: Apache License Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+
+Jetty Utilities (http://jetty.mortbay.org/project/jetty-util) org.mortbay.jetty:jetty-util:jar:6.1.16
+ License: Apache License Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+
+OPS4J Pax Web - Service (http://www.ops4j.org/projects/pax/web/pax-web-service) org.ops4j.pax.web:pax-web-service:bundle:0.5.1
+ License: ALv2 (http://www.apache.org/licenses/LICENSE-2.0.html)
+
+Unnamed - org.slf4j:slf4j-api:jar:1.5.6 (http://www.slf4j.org/slf4j-api) org.slf4j:slf4j-api:jar:1.5.6:runtime
+ License: MIT License (http://www.slf4j.org/license.html)
+
+Unnamed - org.slf4j:slf4j-jdk14:jar:1.5.6 (http://www.slf4j.org/slf4j-jdk14) org.slf4j:slf4j-jdk14:jar:1.5.6:runtime
+ License: MIT License (http://www.slf4j.org/license.html)
+
+WSDL4J (http://sf.net/projects/wsdl4j) wsdl4j:wsdl4j:jar:1.6.2
+ License: CPL (http://www.opensource.org/licenses/cpl1.0.txt)
+
+
diff --git a/trunk/distribution/sources/src/main/release/NOTICE b/trunk/distribution/sources/src/main/release/NOTICE
new file mode 100644
index 0000000..51527cd
--- /dev/null
+++ b/trunk/distribution/sources/src/main/release/NOTICE
@@ -0,0 +1,9 @@
+
+Apache CXF Distributed OSGi DSW Reference Implementation
+Copyright 2008-2012 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+This product contains interfaces and specifications Copyright The OSGi Alliance.
+
diff --git a/trunk/distribution/sources/src/main/release/README b/trunk/distribution/sources/src/main/release/README
new file mode 100644
index 0000000..f2f7ffe
--- /dev/null
+++ b/trunk/distribution/sources/src/main/release/README
@@ -0,0 +1,57 @@
+Welcome to the Apache CXF Distributed OSGi DSW Reference Implementation
+=======================================================================
+
+
+The dOSGi subproject of Apache CXF provides the Reference Implementation
+of the Remote Services Specification version 1.0, Chapter 13 in the OSGi
+Compendium Specification, and OSGi Remote Service Admin Specification
+version 1.0, Chapter 122 in the OSGi Enterprise Specification[1].
+
+
+This release is provided for your convenience in three different
+distribution formats:
+
+1. Multi-bundle distro: cxf-dosgi-ri-multibundle-distribution-1.3.{tar.gaz|zip}
+
+Contains the dOSGi implementation and all 3rd party dependencies as
+separate bundles within an archive. The current distribution is an
+instance of this type.
+
+
+2. Single-bundle distro: cxf-dosgi-ri-singlebundle-distribution-1.3.jar
+
+Contains the dOSGi implementation and all 3rd party dependencies wrapped
+in a single OSGi bundle so as to allow direct installation in your favourite
+OSGi container in one fell swoop.
+
+3. Source distro: cxf-dosgi-ri-source-distribution-1.3.{tar.gz|zip}
+
+Contains the entire source tree for dOSGi. The current distribution is
+an instance of this type. It may be built by running "mvn install" from
+the top level directory. Ensure you have maven installed, version 2.0.9
+or higher is recommended. Also a JDK of version 1.5.0_13 or higher is
+required.
+
+
+The best starting point for using dOSGi is the Getting Started Guide[2].
+
+Also note the very detailed walk-through of the greeter demo[3].
+
+If you need more help, or want to provide any feedback, please feel free
+to drop us a note on the CXF dev or users list[4].
+
+If you trip over any problems with dOSGi, don't hesitate to submit an issue
+to the CXF Distributed OSGI JIRA[5].
+
+
+Thanks for using dOSGi!
+
+Regards,
+The CXF dOSGi team.
+
+
+[1] see chapter 122 http://www.osgi.org/Download/Release4V42
+[2] http://cxf.apache.org/distributed-osgi.html#DistributedOSGi-GettingStarted
+[3] http://cxf.apache.org/distributed-osgi-greeter-demo-walkthrough.htm
+[4] http://cxf.apache.org/mailing-lists.html
+[5] https://issues.apache.org/jira/browse/DOSGI
diff --git a/trunk/distribution/sources/src/main/release/release_notes.txt b/trunk/distribution/sources/src/main/release/release_notes.txt
new file mode 100644
index 0000000..889952e
--- /dev/null
+++ b/trunk/distribution/sources/src/main/release/release_notes.txt
@@ -0,0 +1,295 @@
+
+Release Notes - CXF Distributed OSGi - Version 1.6.0
+====================================================
+
+** Bug
+ * [DOSGI-11] - dOSGi creates new databinding instance instead of using a spring-loaded databinding if available
+ * [DOSGI-196] - Greeter demo does not work in standalone Felix
+ * [DOSGI-198] - Imported service is gone after client bundle is restarted
+ * [DOSGI-206] - Parse errors for xml files in discovery local and dsw
+ * [DOSGI-207] - Update karaf maven plugin to release version
+ * [DOSGI-208] - OSGi compendium bundle installed by feature causes problems
+ * [DOSGI-210] - Service registration and memory leaks
+
+** Improvement
+ * [DOSGI-201] - Create DSOGi distro from karaf feature file
+ * [DOSGI-202] - Make DOSGi independent of jdom
+ * [DOSGI-203] - Upgrade to cxf 2.7.6
+ * [DOSGI-204] - Update osgi spec version to 4.3.1 and felix to 4.2.1
+ * [DOSGI-205] - Upgrade to pax exam 3.2
+
+** Task
+ * [DOSGI-212] - Update CXF version to 2.7.8
+
+
+Release Notes - CXF Distributed OSGi - Version 1.5.0
+====================================================
+
+** Bug
+ * [DOSGI-158] - NPE on shutdown of DOSGi service
+ * [DOSGI-160] - RemoteServiceAdmin shuts itself down during startup
+ * [DOSGI-161] - services sometimes don't get exported
+ * [DOSGI-162] - Compilation errors when using OSGi core
+ * 4.3.0/4.3.1/5.0.0
+ * [DOSGI-164] - NullPointerException on export
+ * [DOSGI-165] - exported service is not properly closed and cannot be
+ * restarted
+ * [DOSGI-166] - List can not be used to register CXF providers with DSW
+ * [DOSGI-168] - RemoteServiceAdminCore service parameters handling bugs
+ * [DOSGI-172] - o.a.c.d.discovery.zookeeper package classes are not
+ * properly synchronized
+ * [DOSGI-173] - unregistering an exported service does not remove it
+ * from zookeeper (and remote clients)
+ * [DOSGI-174] - synchronization issues and resource leaks in
+ * TopologyManagerImport and related classes
+ * [DOSGI-175] - TopologyManagerImport's reference counter doesn't count
+ * [DOSGI-176] - zookeeper discovery sending multiple duplicate endpoint
+ * notifications
+ * [DOSGI-177] - stopped services still appear as available to clients
+ * [DOSGI-180] - CXF service does not disappear if exporting bundle is
+ * stopped
+ * [DOSGI-188] - services aren't re-imported after RemoteServiceAdmin
+ * restart
+ * [DOSGI-190] - NodeExistsException and missing endpoint after ZooKeeper
+ * is restarted
+ * [DOSGI-191] - ZooKeeperDiscovery instance reconnects to ZooKeeper
+ * after bundle is stopped
+ * [DOSGI-192] - Upgrade to zookeeper 3.3.2 to fix bug with zk event
+ * thread shutdown
+ * [DOSGI-195] - Exceptions in tests: ClassCastException on
+ * SpringBusFactory
+
+** Improvement
+ * [DOSGI-167] - Upgrade Aries to the 1.x version for distro
+ * [DOSGI-170] - Remove single bundle distro
+ * [DOSGI-181] - Reactivate or delete old systests
+ * [DOSGI-184] - Split Endpoint repository from TopologyManagerExport
+ * [DOSGI-193] - Split discovery.zookeeper package into subpackages
+
+
+Release Notes - CXF Distributed OSGi - Version 1.4.0
+====================================================
+
+** Bug
+ * [DOSGI-10] - Spring schema handling (intents) doesn't work properly
+ * for the single-bundle case
+ * [DOSGI-18] - RESTful Proxies can not be created in multibundle DOSGI
+ * distributions
+ * [DOSGI-63] - The discovery can be used be used before the connection
+ * to the server is completely established
+ * [DOSGI-69] - CXF-DOSGi requires internet access when reading XML
+ * [DOSGI-90] - Do not use/assume that endpoint.id is an address
+ * [DOSGI-92] - Exception : Applying intent: SOAP via binding config
+ * [DOSGI-109] - NullPointerException in ToloplogyManager during bundle
+ * stop
+ * [DOSGI-110] - Unable to export multiple services
+ * [DOSGI-111] - DOSGi bundle attempts to load WSDL using wrong bundle in
+ * WSDL-first configuration
+ * [DOSGI-113] - Integration with pax-logging not possible
+ * [DOSGI-114] - RemoteServiceAdmin is not available warnings in DOSGi
+ * 1.3
+ * [DOSGI-116] - Build fails when downloading Zookeeper artifacts
+ * [DOSGI-119] - Single bundle activator fails to stop all the bundles if
+ * one of the activators throws an exception
+ * [DOSGI-120] - NullPointerException on export
+ * [DOSGI-121] - Fix logging: System.out prints, printStackTrace, verbose
+ * logs
+ * [DOSGI-123] - ZooKeeper registrations are not recreated on ZooKeeper
+ * server restart
+ * [DOSGI-125] - The dead lock in TopologyManagerImport
+ * [DOSGI-129] - NPE when stopping a bundle that exports a DOSGI service
+ * [DOSGI-135] - Switch logging api to slf4j
+ * [DOSGI-136] - Refactor zookeeper server and add metatype config
+ * [DOSGI-137] - Possible bug in TopologyManagerImport when checking if
+ * an Endpoint is already imported
+ * [DOSGI-142] - Upgrade CXF to 2.7.0
+ * [DOSGI-145] - Multiple services using HTTP Service and published from
+ * the same bundle do not work
+ * [DOSGI-150] - Update to Java 1.6
+ * [DOSGI-153] - Error starting greeter sample in karaf 2.3.0 in aegis
+ * setup: ExceptionInInitializerError ... failed to create an
+ * XPathFactory for the default object model:
+ * http://java.sun.com/jaxp/xpath/dom
+ * [DOSGI-154] - Problems with api packages since update to cxf 2.7.2
+
+** Improvement
+ * [DOSGI-70] - Reconnect automatically to Zookeeper after a connection
+ * loss / timeout
+ * [DOSGI-86] - Decouple DOSGi DSW from Spring DM.
+ * [DOSGI-126] - Allow to use the servlet transport with automatic
+ * discovery
+ * [DOSGI-127] - Default address for services should use the servlet
+ * transport
+ * [DOSGI-128] - Allow to use JAXWS/JAXB service without frontend and
+ * databinding properties
+ * [DOSGI-130] - Clean up unused code and fix warnings, use interfaces
+ * where possible in DSW
+ * [DOSGI-131] - Switch slf4j from springsource bundles to org.slf4j,
+ * update slf4j version
+ * [DOSGI-132] - Switch logging at runtime to pax logging
+ * [DOSGI-133] - Create a Karaf feature file for CXF DOSGi
+ * [DOSGI-134] - Refactoring of TopologyManager
+ * [DOSGI-138] - Refactoring of discovery distributed
+ * [DOSGI-139] - Refactor dsw-cxf to remove dep cycles
+ * [DOSGI-141] - ZooKeeper is not support cluster (ensemble) mode
+ * [DOSGI-143] - Remove all repos except for apache snapshot and central
+ * [DOSGI-146] - Allow to define intents as OSGi services
+ * [DOSGI-147] - Deprecate some Constants that are defined in the spec
+ * [DOSGI-148] - Refactoring of dsw ConfigTypeHandlers
+ * [DOSGI-149] - Optimize distributions and karaf feature
+
+** New Feature
+ * [DOSGI-115] - Use Spring DM and Eclipse Gemini Blueprint with DOSGi
+
+Apache CXF Distributed OSGi 1.3.1 Release Notes
+===============================================
+
+Fixes needed in order to pass the OSGi 4.3 Remote Service Admin TCK.
+* Fixed exports from Single Bundle Distro
+* Fixed deadlock
+* Fixed cleanup
+* Fixed ExportReferenceImpl.equals() and hashCode()
+* Fixed RemoteServiceAdminCore.exportService()
+
+Apache CXF Distributed OSGi 1.3 Release Notes
+=============================================
+
+The following modules have been removed from the destribution:
+
+* org.apache.cxf.dosgi:cxf-dosgi-ri-discovery-distributed-zookeeper-wrapper
+
+ Zookeeper 3.3.1 bundle is now available. See DOSGI-100 for more information.
+
+* org.apache.cxf.dosgi:cxf-dosgi-remote-service-admin-interfaces
+
+ org.osgi:org.osgi.enterprize:4.2.0 artifact is now available. See DOSGI-104 for more information.
+
+Many dependencies have been updated, including the update to CXF 2.5.1. See also DOSGI-96.
+
+Additionally, a number of bugs has been fixed including:
+
+DOSGI-108 service.exported.interfaces doesn't support comma-seperated String value
+DOSGI-107 Support for a 'wsdl' configuration type
+DOSGI-105 Update greeter_rest demo to use OSGI HttpService
+DOSGI-103 Improve multi-bundle distro configuration for Felix
+DOSGI-102 DOSGI RI can not map invocation exceptions to custom exceptions on the client side
+DOSGI-97 Automatically unregister HTTP servlets/resources if exported service goes down
+DOSGI-94 Enhancement to deal with registered services that might have been proxied
+DOSGI-91 DOSGI RS Proxies ignore ResponseExceptionMapper providers
+DOSGI-85 Unable to export services started after Topology manager
+DOSGI-82 Populate custom servicename, portname and targetnamespace for jax-ws
+DOSGI-79 RemoteServiceAdmin.getImportedEndpoints() returns collection of incorrect type
+DOSGI-77 NullPointerException from Distributed OSGI when bundle with wanted properties is started.
+DOSGI-76 EndpointListener.endpointRemoved
+DOSGI-75 CXF Distributed OSGi isn't using org.apache.cxf.common.logging.LogUtils
+
+
+Apache CXF Distributed OSGi 1.2 Release Notes
+=============================================
+
+In addition to providing the Reference Implementation to the OSGi Remote
+Services Specification, the CXF Distributed OSGi 1.2 release now also
+provides the Reference Implementation of the OSGi Remote Service Admin
+Specification version 1.0, Chapter 122 in the OSGi Enterprise
+Specification.
+
+To achieve compliance with the RSA specification a major refactoring has
+been done on the code base.
+
+For the new features applicable to the RSA specification, see chapter
+122 in the OSGi Enterprise Spec (http://www.osgi.org/Download/Release4V42).
+
+The following new features have been introduced:
+
+* org.apache.cxf.ws.port=[port number]
+ This configuration property can be used to change the default port
+ at which a remote service appears. When setting this property the
+ default context will still be used.
+
+* Servlet Filters (javax.servlet.Filter) can now be registered as OSGi
+ services with the "org.apache.cxf.httpservice.filter" boolean
+ property set to true and used to secure DOSGi server endpoints.
+ Endpoints can enforce the registration of the filters by setting an
+ "org.apache.cxf.httpservice.requirefilter" boolean property to true
+
+Additionally, a number of bugs has been fixed including:
+
+[DOSGI-13] - The CXF DOSGi implementation needs to be updated to support the
+ latest OSGi Remote Services Admin spec.
+[DOSGI-24] - org.apache.cxf.dosgi.dsw.ClassUtils#getInterfaceClass() method
+ should search through super class interfaces too
+[DOSGI-25] - FileNotFoundException when client-side proxy is being created
+ [META-INF/cxf/cxf.xml]
+[DOSGI-27] - Discovery problem when two dependent bundles export interfaces
+[DOSGI-28] - Consuming more than 1 service using Spring-DM doesn't work
+[DOSGI-29] - Exception when stopping DOSGi bundle
+[DOSGI-30] - ClassNotFoundException when exposing service.
+[DOSGI-31] - Distributed OSGi having a problem with a custom type method
+ argument
+[DOSGI-32] - The default amount of logging should be significantly reduced
+[DOSGI-33] - Exception when exposing remote service using DOSGi
+[DOSGI-34] - org.apache.servicemix.specs.locator-1.1.1.jar useless?
+[DOSGI-35] - ServicePublication.PROP_KEY_ENDPOINT_LOCATION is now a URI,
+ instead of a URL
+[DOSGI-37] - Fix the dependency on Equinox for the DOSGi system tests
+[DOSGI-38] - single-bundle distribution has incorrect Import-Package and
+ Export-Package declaration in the manifest
+[DOSGI-40] - Remoted service fails to register endpoint after framework is
+ restarted
+[DOSGI-41] - Remove simple-pojo demo (temporarily) as its currently not
+ supported
+[DOSGI-43] - ClassCastException with Declarative Services
+[DOSGI-44] - Existing OSGi Services are not remoted when CXF-DOSGi is started
+[DOSGI-50] - Need to automatically infer SOAP/HTTP transport intents if not
+ explicily set via osgi.remote.requires.intents
+[DOSGI-54] - RemoteServiceAdmin interfaces/classes out of sync with official
+ version
+[DOSGI-61] - The Zookeeper Discovery only supports primitive types as service
+ properties
+[DOSGI-62] - The DSW creates endpoints with localhost URLs
+[DOSGI-66] - The DSW only loads the intent map when certain spring bundles
+ are loaded and started upfront
+[DOSGI-67] - Enable filters on DOSGi endpoints
+[DOSGI-72] - DOSGI not working with HTTP Service
+[DOSGI-74] - Update CXF version to 2.2.9
+
+
+Apache CXF Distributed OSGi 1.1 Release Notes
+=============================================
+
+Specific issues, features, and improvements fixed in this version
+-----------------------------------------------------------------
+
+The Distributed OSGi 1.1 release provides the Reference Implementation
+of the Remote Services Specification version 1.0. Chapter 13 in the OSGi
+Compendium Specification (http://www.osgi.org/Download/Release4V42).
+
+New features in this release include:
+
+* A live Discovery System is now supported. The CXF-DOSGi implementation
+ makes use of Apache Zookeeper (http://hadoop.apache.org/zookeeper) as
+ the Discovery Server and provides client-side bundles for transparent
+ interaction with Zookeeper. See the Discovery Documentation pages
+ (http://cxf.apache.org/dosgi-discovery.html) for more details.
+
+* REST support for JAX-RS-based Remoted Services and Consumers through
+ the org.apache.cxf.rs configuration type.
+
+* Many user issues have been addressed. In addition the following bugs
+ have been fixed:
+
+[CXF-2182] - Exceptions when remoting pre-existing service
+[CXF-2337] - org.apache.cxf.dosgi.dsw.ClassUtils#getInterfaceClass() method
+ should search through super class interfaces too
+[CXF-2435] - Distributed OSGi having a problem with a custom type method
+ argument
+[CXF-2288] - Bundle cannot be restarted
+[CXF-2385] - Discovery doesn't fully translate 'localhost' into a proper
+ machine name
+[CXF-2200] - Consuming more than 1 service using Spring-DM doesn't work
+
+
+Known limitations :
+
+* Schema validation can not be done for JAX-RS-based endpoints which use
+ Aegis databinding
diff --git a/trunk/distribution/subsystem/pom.xml b/trunk/distribution/subsystem/pom.xml
new file mode 100644
index 0000000..bade03c
--- /dev/null
+++ b/trunk/distribution/subsystem/pom.xml
@@ -0,0 +1,393 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-subsystem-distribution</artifactId>
+ <name>Distributed OSGi Subsystem Distribution</name>
+ <url>http://cxf.apache.org</url>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-distribution-parent</artifactId>
+ <version>1.4-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <dosgi.version>${project.version}</dosgi.version>
+ <topDirectoryLocation>../..</topDirectoryLocation>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-annotation_1.0_spec</artifactId>
+ <version>1.1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-activation_1.1_spec</artifactId>
+ <version>1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-javamail_1.4_spec</artifactId>
+ <version>1.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-ws-metadata_2.0_spec</artifactId>
+ <version>1.1.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_${servlet.version}_spec</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>com.springsource.org.apache.commons.logging</artifactId>
+ <version>1.1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.enterprise</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-jcl</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jdom</groupId>
+ <artifactId>com.springsource.org.jdom</artifactId>
+ <version>1.1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.aopalliance</groupId>
+ <artifactId>com.springsource.org.aopalliance</artifactId>
+ <version>1.0.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-asm</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-expression</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-io</artifactId>
+ <version>${spring.osgi.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ <version>${spring.osgi.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-extender</artifactId>
+ <version>${spring.osgi.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.aggregate</groupId>
+ <artifactId>jetty-all-server</artifactId>
+ <version>${jetty.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.web</groupId>
+ <artifactId>pax-web-spi</artifactId>
+ <version>${pax.web.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.web</groupId>
+ <artifactId>pax-web-runtime</artifactId>
+ <version>${pax.web.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.web</groupId>
+ <artifactId>pax-web-jetty</artifactId>
+ <version>${pax.web.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.saaj-api-1.3</artifactId>
+ <version>${servicemix.specs.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.stax-api-1.0</artifactId>
+ <version>${servicemix.specs.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.jaxb-api-2.1</artifactId>
+ <version>${servicemix.specs.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.jaxws-api-2.1</artifactId>
+ <version>${servicemix.specs.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.jsr311-api-1.1.1</artifactId>
+ <version>${servicemix.specs.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ws.xmlschema</groupId>
+ <artifactId>xmlschema-core</artifactId>
+ <version>${xmlschema.bundle.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.xmlresolver</artifactId>
+ <version>${xmlresolver.bundle.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.neethi</groupId>
+ <artifactId>neethi</artifactId>
+ <version>${neethi.bundle.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.wsdl4j</artifactId>
+ <version>${wsdl4j.bundle.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.xmlsec</artifactId>
+ <version>${xmlsec.bundle.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.jaxb-impl</artifactId>
+ <version>${jaxbimpl.bundle.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.asm</artifactId>
+ <version>${asm.bundle.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.woodstox</groupId>
+ <artifactId>stax2-api</artifactId>
+ <version>3.1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.woodstox</groupId>
+ <artifactId>woodstox-core-asl</artifactId>
+ <version>${woodstox.bundle.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-pool</artifactId>
+ <version>${commons.pool.bundle.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.joda-time</artifactId>
+ <version>1.5.2_4</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.opensaml</artifactId>
+ <version>2.4.1_1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-bundle-minimal</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-discovery-local</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-dsw-cxf</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-topology-manager</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- Discovery dependencies -->
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.configadmin</artifactId>
+ <version>1.2.8</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.fileinstall</artifactId>
+ <version>3.1.10</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.log4j</groupId>
+ <artifactId>com.springsource.org.apache.log4j</artifactId>
+ <version>${log4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hadoop</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>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-discovery-distributed</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-discovery-distributed-zookeeper-server</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-discovery-distributed-zookeeper-server-config</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+
+ <plugins>
+ <!-- The assembly plugin is used to actually build the subsystem archive -->
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>make-assembly</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <appendAssemblyId>false</appendAssemblyId>
+ <descriptors>
+ <descriptor>./src/main/assembly/subsystem-assembly.xml</descriptor>
+ </descriptors>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- Since the assembly plugin has no way to output to an .esa file extension use the antrun
+ plugin to copy the file to one with the proper extension -->
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <configuration>
+ <target>
+ <copy file="${project.build.directory}/${project.build.finalName}.zip"
+ tofile="${project.build.directory}/${project.build.finalName}.esa" />
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- Attach the .esa file to the project so that it ends up in the repo -->
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.7</version>
+ <executions>
+ <execution>
+ <id>attach-instrumented-jar</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>attach-artifact</goal>
+ </goals>
+ <configuration>
+ <artifacts>
+ <artifact>
+ <file>${project.build.directory}/${project.build.finalName}.esa</file>
+ <type>esa</type>
+ </artifact>
+ </artifacts>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/distribution/subsystem/src/main/assembly/subsystem-assembly.xml b/trunk/distribution/subsystem/src/main/assembly/subsystem-assembly.xml
new file mode 100644
index 0000000..c0daf6c
--- /dev/null
+++ b/trunk/distribution/subsystem/src/main/assembly/subsystem-assembly.xml
@@ -0,0 +1,46 @@
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/component-1.1.2.xsd">
+<!--
+ 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.
+-->
+ <id>subsystem-feature</id>
+ <formats>
+ <format>zip</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <files>
+ <file>
+ <source>target/classes/OSGI-INF/SUBSYSTEM.MF</source>
+ <outputDirectory>OSGI-INF</outputDirectory>
+ </file>
+ </files>
+
+ <dependencySets>
+ <dependencySet>
+ <scope>runtime</scope>
+ <useTransitiveDependencies>false</useTransitiveDependencies>
+ <excludes>
+ <!-- Exclude the current artifact as it doesn't contribute anything other than the
+ logic to build the subsystem. -->
+ <exclude>org.apache.cxf.dosgi:cxf-dosgi-ri-subsystem-distribution</exclude>
+ </excludes>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/trunk/distribution/subsystem/src/main/resources/OSGI-INF/SUBSYSTEM.MF b/trunk/distribution/subsystem/src/main/resources/OSGI-INF/SUBSYSTEM.MF
new file mode 100644
index 0000000..e2631e0
--- /dev/null
+++ b/trunk/distribution/subsystem/src/main/resources/OSGI-INF/SUBSYSTEM.MF
@@ -0,0 +1,51 @@
+Manifest-Version: 2.0
+Subsystem-ManifestVersion: 1.0
+Subsystem-SymbolicName: ${project.artifactId}
+Subsystem-Version: 1.4.0
+Subsystem-Name: ${project.name}
+Subsystem-Content: org.apache.geronimo.specs.geronimo-annotation_1.0_spec;start-order:=1,
+ org.apache.geronimo.specs.geronimo-activation_1.1_spec;start-order:=2,
+ org.apache.geronimo.specs.geronimo-javamail_1.4_spec;start-order:=3,
+ org.apache.geronimo.specs.geronimo-servlet_3.0_spec;start-order:=4,
+ org.apache.geronimo.specs.geronimo-ws-metadata_2.0_spec;start-order:=5,
+ com.springsource.org.apache.commons.logging;start-order:=6,
+ com.springsource.org.jdom;start-order:=7,
+ org.springframework.core;start-order:=8,
+ org.springframework.beans;start-order:=9,
+ org.springframework.context;start-order:=10,
+ com.springsource.org.aopalliance;start-order:=11,
+ slf4j-api;start-order:=11,
+ slf4j-jcl,
+ org.springframework.aop;start-order:=12,
+ org.springframework.asm;start-order:=13,
+ org.springframework.expression;start-order:=14,
+ org.springframework.osgi.io;start-order:=15,
+ org.springframework.osgi.core;start-order:=16,
+ org.springframework.osgi.extender;start-order:=17,
+ org.eclipse.jetty.aggregate.jetty-all-server;start-order:=18,
+ org.ops4j.pax.web.pax-web-spi;start-order:=19,
+ org.ops4j.pax.web.pax-web-runtime;start-order:=20,
+ org.ops4j.pax.web.pax-web-jetty;start-order:=21,
+ org.apache.servicemix.bundles.jaxb-impl;start-order:=22,
+ org.apache.servicemix.bundles.wsdl4j;start-order:=23,
+ org.apache.servicemix.bundles.xmlsec;start-order:=24,
+ org.apache.ws.xmlschema.core;start-order:=25,
+ org.apache.servicemix.bundles.asm;start-order:=26,
+ org.apache.servicemix.bundles.xmlresolver;start-order:=27,
+ org.apache.neethi;start-order:=28,
+ stax2-api;start-order:=29,
+ woodstox-core-asl;start-order:=30,
+ org.apache.servicemix.bundles.commons-pool;start-order:=31,
+ org.apache.servicemix.specs.saaj-api-1.3;start-order:=32,
+ org.apache.servicemix.specs.stax-api-1.0;start-order:=33,
+ org.apache.servicemix.specs.jaxb-api-2.1;start-order:=34,
+ org.apache.servicemix.specs.jaxws-api-2.1;start-order:=35,
+ org.apache.servicemix.specs.jsr311-api-1.1.1;start-order:=36,
+ org.apache.servicemix.bundles.joda-time;start-order:=37,
+ org.apache.servicemix.bundles.opensaml;start-order:=38,
+ org.apache.cxf.bundle-minimal;start-order:=39,
+ cxf-dosgi-ri-discovery-local;start-order:=40,
+ osgi.enterprise;start-order:=41,
+ cxf-dosgi-ri-dsw-cxf;start-order:=42,
+ cxf-dosgi-ri-topology-manager;start-order:=43
+Subsystem-Type: osgi.subsystem.feature
diff --git a/trunk/dsw/cxf-dsw/pom.xml b/trunk/dsw/cxf-dsw/pom.xml
new file mode 100644
index 0000000..eb81732
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/pom.xml
@@ -0,0 +1,143 @@
+<?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>
+ <artifactId>cxf-dosgi-ri-dsw-cxf</artifactId>
+ <packaging>bundle</packaging>
+ <name>CXF dOSGi Remote Service Admin Implementation</name>
+ <description>The CXF Remote Service Admin as described in the OSGi Remote Service Admin specification</description>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>../..</topDirectoryLocation>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-core</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxws</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-databinding-aegis</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-rs-extension-providers</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_${servlet.version}_spec</artifactId>
+ <version>1.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.jvnet.jaxb2.maven2</groupId>
+ <artifactId>maven-jaxb2-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Import-Package>
+ javax.servlet*;version="[0.0,4)",
+ *
+ </Import-Package>
+ <Export-Package>
+ !*
+ </Export-Package>
+ <Bundle-Activator>org.apache.cxf.dosgi.dsw.Activator</Bundle-Activator>
+
+ <!-- Is currently needed to create a proxy of an interface given in String form -->
+ <DynamicImport-Package>*</DynamicImport-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/TestUtils*</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/Activator.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/Activator.java
new file mode 100644
index 0000000..470ecc3
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/Activator.java
@@ -0,0 +1,169 @@
+/**
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.BusFactory;
+import org.apache.cxf.dosgi.dsw.decorator.ServiceDecorator;
+import org.apache.cxf.dosgi.dsw.decorator.ServiceDecoratorBundleListener;
+import org.apache.cxf.dosgi.dsw.decorator.ServiceDecoratorImpl;
+import org.apache.cxf.dosgi.dsw.handlers.ConfigTypeHandlerFactory;
+import org.apache.cxf.dosgi.dsw.handlers.HttpServiceManager;
+import org.apache.cxf.dosgi.dsw.qos.DefaultIntentMapFactory;
+import org.apache.cxf.dosgi.dsw.qos.IntentManager;
+import org.apache.cxf.dosgi.dsw.qos.IntentManagerImpl;
+import org.apache.cxf.dosgi.dsw.qos.IntentMap;
+import org.apache.cxf.dosgi.dsw.qos.IntentTracker;
+import org.apache.cxf.dosgi.dsw.service.RemoteServiceAdminCore;
+import org.apache.cxf.dosgi.dsw.service.RemoteServiceadminFactory;
+import org.apache.cxf.dosgi.dsw.util.Utils;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// registered as spring bean -> start / stop called accordingly
+public class Activator implements ManagedService, BundleActivator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Activator.class);
+ private static final int DEFAULT_INTENT_TIMEOUT = 30000;
+ private static final String CONFIG_SERVICE_PID = "cxf-dsw";
+ private ServiceRegistration rsaFactoryReg;
+ private ServiceRegistration decoratorReg;
+ private IntentTracker intentTracker;
+ private HttpServiceManager httpServiceManager;
+ private BundleContext bc;
+ private BundleListener bundleListener;
+
+ public void start(BundleContext bundlecontext) throws Exception {
+ LOG.debug("RemoteServiceAdmin Implementation is starting up");
+ this.bc = bundlecontext;
+ // Disable the fast infoset as it's not compatible (yet) with OSGi
+ System.setProperty("org.apache.cxf.nofastinfoset", "true");
+ init(new Hashtable<String, Object>());
+ registerManagedService(bc);
+ }
+
+ private synchronized void init(Map<String, Object> config) {
+ String httpBase = (String) config.get(org.apache.cxf.dosgi.dsw.Constants.HTTP_BASE);
+ String cxfServletAlias = (String) config.get(org.apache.cxf.dosgi.dsw.Constants.CXF_SERVLET_ALIAS);
+
+ IntentMap intentMap = new IntentMap(new DefaultIntentMapFactory().create());
+ intentTracker = new IntentTracker(bc, intentMap);
+ intentTracker.open();
+ IntentManager intentManager = new IntentManagerImpl(intentMap, DEFAULT_INTENT_TIMEOUT);
+ httpServiceManager = new HttpServiceManager(bc, httpBase, cxfServletAlias);
+ ConfigTypeHandlerFactory configTypeHandlerFactory
+ = new ConfigTypeHandlerFactory(bc, intentManager, httpServiceManager);
+ RemoteServiceAdminCore rsaCore = new RemoteServiceAdminCore(bc, configTypeHandlerFactory);
+ RemoteServiceadminFactory rsaf = new RemoteServiceadminFactory(rsaCore);
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ String[] supportedIntents = intentMap.keySet().toArray(new String[] {});
+ props.put("remote.intents.supported", supportedIntents);
+ props.put("remote.configs.supported",
+ obtainSupportedConfigTypes(configTypeHandlerFactory.getSupportedConfigurationTypes()));
+ LOG.info("Registering RemoteServiceAdminFactory...");
+ rsaFactoryReg = bc.registerService(RemoteServiceAdmin.class.getName(), rsaf, props);
+ ServiceDecoratorImpl serviceDecorator = new ServiceDecoratorImpl();
+ bundleListener = new ServiceDecoratorBundleListener(serviceDecorator);
+ bc.addBundleListener(bundleListener);
+ decoratorReg = bc.registerService(ServiceDecorator.class.getName(), serviceDecorator, null);
+ }
+
+ // The CT sometimes uses the first element returned to register a service, but
+ // does not provide any additional configuration.
+ // Return the configuration type that works without additional configuration as the first in the list.
+ private String[] obtainSupportedConfigTypes(List<String> types) {
+ List<String> l = new ArrayList<String>(types);
+ if (l.contains(org.apache.cxf.dosgi.dsw.Constants.WS_CONFIG_TYPE)) {
+ // make sure its the first element...
+ l.remove(org.apache.cxf.dosgi.dsw.Constants.WS_CONFIG_TYPE);
+ l.add(0, org.apache.cxf.dosgi.dsw.Constants.WS_CONFIG_TYPE);
+ }
+ return l.toArray(new String[] {});
+ }
+
+ private void registerManagedService(BundleContext bundlecontext) {
+ Dictionary<String, String> props = new Hashtable<String, String>();
+ props.put(Constants.SERVICE_PID, CONFIG_SERVICE_PID);
+ // No need to store the registration. Will be unregistered in stop by framework
+ bundlecontext.registerService(ManagedService.class.getName(), this, props);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ LOG.debug("RemoteServiceAdmin Implementation is shutting down now");
+ bc.removeBundleListener(bundleListener);
+ intentTracker.close();
+ // This also triggers the unimport and unexport of the remote services
+ rsaFactoryReg.unregister();
+ decoratorReg.unregister();
+ httpServiceManager.close();
+ httpServiceManager = null;
+ intentTracker = null;
+ rsaFactoryReg = null;
+ decoratorReg = null;
+ shutdownCXFBus();
+ }
+
+ /**
+ * Causes also the shutdown of the embedded HTTP server
+ */
+ private void shutdownCXFBus() {
+ Bus b = BusFactory.getDefaultBus();
+ if (b != null) {
+ LOG.debug("Shutting down the CXF Bus");
+ b.shutdown(true);
+ }
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public synchronized void updated(Dictionary config) throws ConfigurationException {
+ LOG.debug("RemoteServiceAdmin Implementation configuration is updated with {}", config);
+ if (config != null) {
+ try {
+ bc.removeBundleListener(bundleListener);
+ intentTracker.close();
+ // This also triggers the unimport and unexport of the remote services
+ rsaFactoryReg.unregister();
+ decoratorReg.unregister();
+ httpServiceManager.close();
+ httpServiceManager = null;
+ intentTracker = null;
+ rsaFactoryReg = null;
+ decoratorReg = null;
+ } catch (Exception e) {
+ LOG.error(e.getMessage(), e);
+ }
+ init(Utils.toMap(config));
+ }
+ }
+
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/Constants.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/Constants.java
new file mode 100644
index 0000000..218bec6
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/Constants.java
@@ -0,0 +1,146 @@
+/**
+ * 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;
+
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+public final class Constants {
+
+ // Constants from RFC 119, they should ultimately be picked up from an OSGi class.
+ @Deprecated
+ public static final String EXPORTED_INTERFACES = RemoteConstants.SERVICE_EXPORTED_INTERFACES;
+ @Deprecated
+ public static final String EXPORTED_INTERFACES_OLD = "osgi.remote.interfaces"; // for BW compatibility
+
+ @Deprecated
+ public static final String EXPORTED_CONFIGS = RemoteConstants.SERVICE_EXPORTED_CONFIGS;
+ @Deprecated
+ public static final String EXPORTED_CONFIGS_OLD = "osgi.remote.configuration.type"; // for BW compatibility
+
+ @Deprecated
+ public static final String EXPORTED_INTENTS = RemoteConstants.SERVICE_EXPORTED_INTENTS;
+ @Deprecated
+ public static final String EXPORTED_INTENTS_EXTRA = RemoteConstants.SERVICE_EXPORTED_INTENTS_EXTRA;
+ @Deprecated
+ public static final String EXPORTED_INTENTS_OLD = "osgi.remote.requires.intents";
+
+ @Deprecated
+ public static final String IMPORTED = RemoteConstants.SERVICE_IMPORTED;
+ @Deprecated
+ public static final String IMPORTD_CONFIGS = RemoteConstants.SERVICE_IMPORTED_CONFIGS;
+
+ @Deprecated
+ public static final String INTENTS = RemoteConstants.SERVICE_INTENTS;
+
+ // WSDL
+ public static final String WSDL_CONFIG_TYPE = "wsdl";
+ public static final String WSDL_CONFIG_PREFIX = "osgi.remote.configuration" + "." + WSDL_CONFIG_TYPE;
+ public static final String WSDL_SERVICE_NAMESPACE = WSDL_CONFIG_PREFIX + ".service.ns";
+ public static final String WSDL_SERVICE_NAME = WSDL_CONFIG_PREFIX + ".service.name";
+ public static final String WSDL_PORT_NAME = WSDL_CONFIG_PREFIX + ".port.name";
+ public static final String WSDL_LOCATION = WSDL_CONFIG_PREFIX + ".location";
+ public static final String WSDL_HTTP_SERVICE_CONTEXT = WSDL_CONFIG_PREFIX + ".httpservice.context";
+ // Provider prefix
+ public static final String PROVIDER_PREFIX = "org.apache.cxf";
+
+ // WS
+ public static final String WS_CONFIG_TYPE = PROVIDER_PREFIX + ".ws";
+ public static final String WS_ADDRESS_PROPERTY = WS_CONFIG_TYPE + ".address";
+ public static final String WS_PORT_PROPERTY = WS_CONFIG_TYPE + ".port";
+ public static final String WS_HTTP_SERVICE_CONTEXT = WS_CONFIG_TYPE + ".httpservice.context";
+
+ public static final String WS_FRONTEND_PROP_KEY = WS_CONFIG_TYPE + ".frontend";
+ public static final String WS_FRONTEND_JAXWS = "jaxws";
+ public static final String WS_FRONTEND_SIMPLE = "simple";
+
+ public static final String WS_IN_INTERCEPTORS_PROP_KEY = WS_CONFIG_TYPE + ".in.interceptors";
+ public static final String WS_OUT_INTERCEPTORS_PROP_KEY = WS_CONFIG_TYPE + ".out.interceptors";
+ public static final String WS_OUT_FAULT_INTERCEPTORS_PROP_KEY = WS_CONFIG_TYPE + ".out.fault.interceptors";
+ public static final String WS_IN_FAULT_INTERCEPTORS_PROP_KEY = WS_CONFIG_TYPE + ".in.fault.interceptors";
+ public static final String WS_CONTEXT_PROPS_PROP_KEY = WS_CONFIG_TYPE + ".context.properties";
+ public static final String WS_FEATURES_PROP_KEY = WS_CONFIG_TYPE + ".features";
+
+ public static final String WS_DATABINDING_PROP_KEY = WS_CONFIG_TYPE + ".databinding";
+ public static final String WS_DATABINDING_BEAN_PROP_KEY = WS_DATABINDING_PROP_KEY + ".bean";
+ public static final String WS_DATA_BINDING_JAXB = "jaxb";
+ public static final String WS_DATA_BINDING_AEGIS = "aegis";
+
+ public static final String WS_WSDL_SERVICE_NAMESPACE = WS_CONFIG_TYPE + ".service.ns";
+ public static final String WS_WSDL_SERVICE_NAME = WS_CONFIG_TYPE + ".service.name";
+ public static final String WS_WSDL_PORT_NAME = WS_CONFIG_TYPE + ".port.name";
+ public static final String WS_WSDL_LOCATION = WS_CONFIG_TYPE + ".wsdl.location";
+ // Rest
+ public static final String RS_CONFIG_TYPE = PROVIDER_PREFIX + ".rs";
+ public static final String RS_ADDRESS_PROPERTY = RS_CONFIG_TYPE + ".address";
+ public static final String RS_HTTP_SERVICE_CONTEXT = RS_CONFIG_TYPE + ".httpservice.context";
+ public static final String RS_DATABINDING_PROP_KEY = RS_CONFIG_TYPE + ".databinding";
+ public static final String RS_IN_INTERCEPTORS_PROP_KEY = RS_CONFIG_TYPE + ".in.interceptors";
+ public static final String RS_OUT_INTERCEPTORS_PROP_KEY = RS_CONFIG_TYPE + ".out.interceptors";
+ public static final String RS_IN_FAULT_INTERCEPTORS_PROP_KEY = RS_CONFIG_TYPE + ".in.fault.interceptors";
+ public static final String RS_OUT_FAULT_INTERCEPTORS_PROP_KEY = RS_CONFIG_TYPE + ".out.fault.interceptors";
+ public static final String RS_CONTEXT_PROPS_PROP_KEY = RS_CONFIG_TYPE + ".context.properties";
+ public static final String RS_FEATURES_PROP_KEY = RS_CONFIG_TYPE + ".features";
+ public static final String RS_PROVIDER_PROP_KEY = RS_CONFIG_TYPE + ".provider";
+ public static final String RS_PROVIDER_EXPECTED_PROP_KEY = RS_PROVIDER_PROP_KEY + ".expected";
+ public static final String RS_PROVIDER_GLOBAL_PROP_KEY = RS_PROVIDER_PROP_KEY + ".globalquery";
+ public static final String RS_WADL_LOCATION = RS_CONFIG_TYPE + ".wadl.location";
+ // POJO (old value for WS)
+ public static final String WS_CONFIG_TYPE_OLD = "pojo";
+ public static final String WS_CONFIG_OLD_PREFIX = "osgi.remote.configuration." + WS_CONFIG_TYPE_OLD;
+ public static final String WS_ADDRESS_PROPERTY_OLD = WS_CONFIG_OLD_PREFIX + ".address";
+ public static final String WS_HTTP_SERVICE_CONTEXT_OLD = WS_CONFIG_OLD_PREFIX + ".httpservice.context";
+
+ // Common Configuration Properties
+ public static final String CHECK_BUNDLE = "check.bundle";
+
+ // The following constants are not evaluated anymore
+ @Deprecated
+ public static final String DEFAULT_PORT_CONFIG = "default.port";
+ @Deprecated
+ public static final String DEFAULT_HOST_CONFIG = "default.host";
+ @Deprecated
+ public static final String DEFAULT_PORT_VALUE = "9000";
+ @Deprecated
+ public static final String DEFAULT_HOST_VALUE = "localhost";
+ @Deprecated
+ public static final String USE_MASTER_MAP = "use.master.map";
+
+ // DSW Identification - TODO do we really need this one?
+ public static final String DSW_CLIENT_ID = PROVIDER_PREFIX + ".remote.dsw.client";
+
+ public static final String INTENT_NAME_PROP = "org.apache.cxf.dosgi.IntentName";
+
+ /**
+ * Prefix to create an absolute URL from a relative URL.
+ * See HttpServiceManager.getAbsoluteAddress
+ *
+ * Defaults to: http://<host name>:8181
+ */
+ public static final String HTTP_BASE = "httpBase";
+
+ /**
+ * Name of the cxf servlet alias
+ */
+ public static final String CXF_SERVLET_ALIAS = "cxfServletAlias";
+ public static final String DEFAULT_CXF_SERVLET_ALIAS = "/cxf";
+
+ private Constants() {
+ // never constructed
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/DecorationParser.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/DecorationParser.java
new file mode 100644
index 0000000..c7a8f96
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/DecorationParser.java
@@ -0,0 +1,79 @@
+/**
+ * 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.decorator;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+
+import org.xml.sax.SAXException;
+import org.apache.cxf.xmlns.service_decoration._1_0.ServiceDecorationType;
+import org.apache.cxf.xmlns.service_decoration._1_0.ServiceDecorationsType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class DecorationParser {
+ private static final Logger LOG = LoggerFactory.getLogger(ServiceDecoratorImpl.class);
+ private JAXBContext jaxbContext;
+ private Schema schema;
+
+ DecorationParser() {
+ try {
+ jaxbContext = JAXBContext.newInstance(ServiceDecorationsType.class.getPackage().getName(),
+ this.getClass().getClassLoader());
+ SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ URL resource = getClass().getResource("/service-decoration.xsd");
+ schema = schemaFactory.newSchema(resource);
+ } catch (JAXBException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ } catch (SAXException e) {
+ throw new RuntimeException("Error loading decorations schema", e);
+ }
+
+ }
+
+ List<ServiceDecorationType> getDecorations(URL resourceURL) {
+ if (resourceURL == null) {
+ return new ArrayList<ServiceDecorationType>();
+ }
+ try {
+ Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+ unmarshaller.setSchema(schema);
+ InputStream is = resourceURL.openStream();
+ Source source = new StreamSource(is);
+ JAXBElement<ServiceDecorationsType> jaxb = unmarshaller.unmarshal(source, ServiceDecorationsType.class);
+ ServiceDecorationsType decorations = jaxb.getValue();
+ return decorations.getServiceDecoration();
+ } catch (Exception ex) {
+ LOG.warn("Problem parsing: " + resourceURL, ex);
+ return new ArrayList<ServiceDecorationType>();
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/InterfaceRule.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/InterfaceRule.java
new file mode 100644
index 0000000..14b1ab8
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/InterfaceRule.java
@@ -0,0 +1,96 @@
+/**
+ * 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.decorator;
+
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InterfaceRule implements Rule {
+
+ private static final Logger LOG = LoggerFactory.getLogger(InterfaceRule.class);
+
+ private final Bundle bundle;
+ private final Pattern matchPattern;
+ private final Map<String, String> propMatches = new HashMap<String, String>();
+ private final Map<String, Object> addProps = new HashMap<String, Object>();
+
+ public InterfaceRule(Bundle b, String im) {
+ bundle = b;
+ matchPattern = Pattern.compile(im);
+ }
+
+ public synchronized void addPropMatch(String name, String value) {
+ propMatches.put(name, value);
+ }
+
+ public synchronized void addProperty(String name, String value, String type) {
+ Object obj = value;
+
+ if (type != null && !String.class.getName().equals(type)) {
+ try {
+ Class<?> cls = getClass().getClassLoader().loadClass(type);
+ Constructor<?> ctor = cls.getConstructor(new Class[] {String.class});
+ obj = ctor.newInstance(value);
+ } catch (Throwable th) {
+ LOG.warn("Could not handle property '" + name
+ + "' with value '" + value + "' of type: " + type, th);
+ return;
+ }
+ }
+
+ addProps.put(name, obj);
+ }
+
+ public synchronized void apply(ServiceReference sref, Map<String, Object> target) {
+ String[] objectClass = (String[]) sref.getProperty(Constants.OBJECTCLASS);
+ boolean matches = false;
+ for (String cls : objectClass) {
+ Matcher m = matchPattern.matcher(cls);
+ if (m.matches()) {
+ for (Map.Entry<String, String> pm : propMatches.entrySet()) {
+ Object value = sref.getProperty(pm.getKey());
+ if (value == null || !Pattern.matches(pm.getValue(), value.toString())) {
+ return;
+ }
+ }
+ matches = true;
+ break;
+ }
+ }
+ if (!matches) {
+ return;
+ }
+
+ LOG.info("Adding the following properties to " + sref + ": " + addProps);
+ target.putAll(addProps);
+ }
+
+ public Bundle getBundle() {
+ return bundle;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/Rule.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/Rule.java
new file mode 100644
index 0000000..a280442
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/Rule.java
@@ -0,0 +1,41 @@
+/**
+ * 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.decorator;
+
+import java.util.Map;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+public interface Rule {
+
+ /**
+ * When the ServiceReference passed in matches the rule's condition,
+ * set the additional properties in the target.
+ * @param sref The Service Reference to be checked.
+ * @param target Any additional properties are to be set in this map.
+ */
+ void apply(ServiceReference sref, Map<String, Object> target);
+
+ /**
+ * Returns the bundle that provided this rule.
+ * @return The Bundle where the Rule was defined.
+ */
+ Bundle getBundle();
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecorator.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecorator.java
new file mode 100644
index 0000000..07a0b85
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecorator.java
@@ -0,0 +1,28 @@
+/**
+ * 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.decorator;
+
+import java.util.Map;
+
+import org.osgi.framework.ServiceReference;
+
+public interface ServiceDecorator {
+
+ void decorate(ServiceReference sref, Map<String, Object> properties);
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecoratorBundleListener.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecoratorBundleListener.java
new file mode 100644
index 0000000..79c2908
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecoratorBundleListener.java
@@ -0,0 +1,48 @@
+/**
+ * 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.decorator;
+
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+
+public class ServiceDecoratorBundleListener implements BundleListener {
+ /**
+ *
+ */
+ private final ServiceDecoratorImpl serviceDecorator;
+
+ /**
+ * @param serviceDecorator
+ */
+ public ServiceDecoratorBundleListener(ServiceDecoratorImpl serviceDecorator) {
+ this.serviceDecorator = serviceDecorator;
+ }
+
+ public void bundleChanged(BundleEvent be) {
+ switch(be.getType()) {
+ case BundleEvent.STARTED:
+ this.serviceDecorator.addDecorations(be.getBundle());
+ break;
+ case BundleEvent.STOPPING:
+ this.serviceDecorator.removeDecorations(be.getBundle());
+ break;
+ default:
+ }
+ }
+}
\ No newline at end of file
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecoratorImpl.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecoratorImpl.java
new file mode 100644
index 0000000..b67c1d6
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecoratorImpl.java
@@ -0,0 +1,90 @@
+/**
+ * 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.decorator;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.cxf.xmlns.service_decoration._1_0.AddPropertyType;
+import org.apache.cxf.xmlns.service_decoration._1_0.MatchPropertyType;
+import org.apache.cxf.xmlns.service_decoration._1_0.MatchType;
+import org.apache.cxf.xmlns.service_decoration._1_0.ServiceDecorationType;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+public class ServiceDecoratorImpl implements ServiceDecorator {
+ final List<Rule> decorations = new CopyOnWriteArrayList<Rule>();
+
+ private DecorationParser parser;
+
+ public ServiceDecoratorImpl() {
+ parser = new DecorationParser();
+ }
+
+ public void decorate(ServiceReference sref, Map<String, Object> target) {
+ for (Rule matcher : decorations) {
+ matcher.apply(sref, target);
+ }
+ }
+
+ void addDecorations(Bundle bundle) {
+ for (ServiceDecorationType decoration : getDecorationElements(bundle)) {
+ for (MatchType match : decoration.getMatch()) {
+ decorations.add(getRule(bundle, match));
+ }
+ }
+ }
+
+ private Rule getRule(Bundle bundle, MatchType match) {
+ InterfaceRule m = new InterfaceRule(bundle, match.getInterface());
+ for (MatchPropertyType propMatch : match.getMatchProperty()) {
+ m.addPropMatch(propMatch.getName(), propMatch.getValue());
+ }
+ for (AddPropertyType addProp : match.getAddProperty()) {
+ m.addProperty(addProp.getName(), addProp.getValue(), addProp.getType());
+ }
+ return m;
+ }
+
+ List<ServiceDecorationType> getDecorationElements(Bundle bundle) {
+ @SuppressWarnings("rawtypes")
+ Enumeration entries = bundle.findEntries("OSGI-INF/remote-service", "*.xml", false);
+ if (entries == null) {
+ return Collections.emptyList();
+ }
+ List<ServiceDecorationType> elements = new ArrayList<ServiceDecorationType>();
+ while (entries.hasMoreElements()) {
+ elements.addAll(parser.getDecorations((URL)entries.nextElement()));
+ }
+ return elements;
+ }
+
+ void removeDecorations(Bundle bundle) {
+ for (Rule r : decorations) {
+ if (bundle.equals(r.getBundle())) {
+ decorations.remove(r); // the iterator doesn't support 'remove'
+ }
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/AbstractPojoConfigurationTypeHandler.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/AbstractPojoConfigurationTypeHandler.java
new file mode 100644
index 0000000..98308e7
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/AbstractPojoConfigurationTypeHandler.java
@@ -0,0 +1,256 @@
+/**
+ * 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.handlers;
+
+import java.lang.reflect.Proxy;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.common.util.PackageUtils;
+import org.apache.cxf.dosgi.dsw.Constants;
+import org.apache.cxf.dosgi.dsw.qos.IntentManager;
+import org.apache.cxf.dosgi.dsw.qos.IntentUtils;
+import org.apache.cxf.dosgi.dsw.util.ClassUtils;
+import org.apache.cxf.dosgi.dsw.util.OsgiUtils;
+import org.apache.cxf.endpoint.AbstractEndpointFactory;
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.frontend.AbstractWSDLBasedEndpointFactory;
+import org.apache.cxf.frontend.ClientFactoryBean;
+import org.apache.cxf.frontend.ServerFactoryBean;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.interceptor.Interceptor;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractPojoConfigurationTypeHandler implements ConfigurationTypeHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractPojoConfigurationTypeHandler.class);
+ protected BundleContext bundleContext;
+ protected IntentManager intentManager;
+ protected HttpServiceManager httpServiceManager;
+
+ public AbstractPojoConfigurationTypeHandler(BundleContext dswBC, IntentManager intentManager,
+ HttpServiceManager httpServiceManager) {
+ this.bundleContext = dswBC;
+ this.intentManager = intentManager;
+ this.httpServiceManager = httpServiceManager;
+ }
+
+ protected Object getProxy(Object serviceProxy, Class<?> iType) {
+ return Proxy.newProxyInstance(iType.getClassLoader(), new Class[] {
+ iType
+ }, new ServiceInvocationHandler(serviceProxy, iType));
+ }
+
+ protected Map<String, Object> createEndpointProps(Map<String, Object> sd, Class<?> iClass,
+ String[] importedConfigs, String address, String[] intents) {
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ copyEndpointProperties(sd, props);
+
+ String[] sa = new String[] {
+ iClass.getName()
+ };
+ String pkg = iClass.getPackage().getName();
+
+ props.remove(org.osgi.framework.Constants.SERVICE_ID);
+ props.put(org.osgi.framework.Constants.OBJECTCLASS, sa);
+ props.put(RemoteConstants.ENDPOINT_SERVICE_ID, sd.get(org.osgi.framework.Constants.SERVICE_ID));
+ props.put(RemoteConstants.ENDPOINT_FRAMEWORK_UUID, OsgiUtils.getUUID(bundleContext));
+ props.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, importedConfigs);
+ props.put(RemoteConstants.ENDPOINT_PACKAGE_VERSION_ + pkg, OsgiUtils.getVersion(iClass, bundleContext));
+
+ for (String configurationType : importedConfigs) {
+ if (Constants.WS_CONFIG_TYPE.equals(configurationType)) {
+ props.put(Constants.WS_ADDRESS_PROPERTY, address);
+ } else if (Constants.RS_CONFIG_TYPE.equals(configurationType)) {
+ props.put(Constants.RS_ADDRESS_PROPERTY, address);
+ } else if (Constants.WS_CONFIG_TYPE_OLD.equals(configurationType)) {
+ props.put(Constants.WS_ADDRESS_PROPERTY_OLD, address);
+ props.put(Constants.WS_ADDRESS_PROPERTY, address);
+ }
+ }
+
+ String[] allIntents = IntentUtils.mergeArrays(intents, IntentUtils.getIntentsImplementedByTheService(sd));
+ props.put(RemoteConstants.SERVICE_INTENTS, allIntents);
+ props.put(RemoteConstants.ENDPOINT_ID, address);
+ return props;
+ }
+
+ private void copyEndpointProperties(Map<String, Object> sd, Map<String, Object> endpointProps) {
+ Set<Map.Entry<String, Object>> keys = sd.entrySet();
+ for (Map.Entry<String, Object> entry : keys) {
+ try {
+ String skey = entry.getKey();
+ if (!skey.startsWith(".")) {
+ endpointProps.put(skey, entry.getValue());
+ }
+ } catch (ClassCastException e) {
+ LOG.warn("ServiceProperties Map contained non String key. Skipped " + entry + " "
+ + e.getLocalizedMessage());
+ }
+ }
+ }
+
+ protected void setCommonWsdlProperties(AbstractWSDLBasedEndpointFactory factory, BundleContext context,
+ Map<String, Object> sd, boolean wsdlType) {
+ String location = OsgiUtils.getProperty(sd, wsdlType ? Constants.WSDL_LOCATION : Constants.WS_WSDL_LOCATION);
+ if (location != null) {
+ URL wsdlURL = context.getBundle().getResource(location);
+ if (wsdlURL != null) {
+ factory.setWsdlURL(wsdlURL.toString());
+ }
+ QName serviceName = getServiceQName(null, sd,
+ wsdlType ? Constants.WSDL_SERVICE_NAMESPACE : Constants.WS_WSDL_SERVICE_NAMESPACE,
+ wsdlType ? Constants.WSDL_SERVICE_NAME : Constants.WS_WSDL_SERVICE_NAME);
+ if (serviceName != null) {
+ factory.setServiceName(serviceName);
+ QName portName = getPortQName(serviceName.getNamespaceURI(), sd,
+ wsdlType ? Constants.WSDL_PORT_NAME : Constants.WS_WSDL_PORT_NAME);
+ if (portName != null) {
+ factory.setEndpointName(portName);
+ }
+ }
+ }
+ }
+
+ protected void setWsdlProperties(ServerFactoryBean factory, BundleContext callingContext, Map<String, Object> sd,
+ boolean wsdlType) {
+ setCommonWsdlProperties(factory, callingContext, sd, wsdlType);
+ }
+
+ protected void setClientWsdlProperties(ClientFactoryBean factory, BundleContext dswContext, Map<String, Object> sd,
+ boolean wsdlType) {
+ setCommonWsdlProperties(factory, dswContext, sd, wsdlType);
+ }
+
+ protected static QName getServiceQName(Class<?> iClass, Map<String, Object> sd, String nsPropName,
+ String namePropName) {
+ String serviceNs = OsgiUtils.getProperty(sd, nsPropName);
+ String serviceName = OsgiUtils.getProperty(sd, namePropName);
+ if (iClass == null && (serviceNs == null || serviceName == null)) {
+ return null;
+ }
+ if (serviceNs == null) {
+ serviceNs = PackageUtils.getNamespace(PackageUtils.getPackageName(iClass));
+ }
+ if (serviceName == null) {
+ serviceName = iClass.getSimpleName();
+ }
+ return new QName(serviceNs, serviceName);
+ }
+
+ protected static QName getPortQName(String ns, Map<String, Object> sd, String propName) {
+ String portName = OsgiUtils.getProperty(sd, propName);
+ if (portName == null) {
+ return null;
+ }
+ return new QName(ns, portName);
+ }
+
+ protected String getClientAddress(Map<String, Object> sd) {
+ return OsgiUtils.getFirstNonEmptyStringProperty(sd, RemoteConstants.ENDPOINT_ID,
+ Constants.WS_ADDRESS_PROPERTY,
+ Constants.WS_ADDRESS_PROPERTY_OLD,
+ Constants.RS_ADDRESS_PROPERTY);
+ }
+
+ protected String getServerAddress(Map<String, Object> sd, Class<?> iClass) {
+ String address = getClientAddress(sd);
+ return address == null ? httpServiceManager.getDefaultAddress(iClass) : address;
+ }
+
+ protected ExportResult createServerFromFactory(ServerFactoryBean factory, Map<String, Object> endpointProps) {
+ ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(ServerFactoryBean.class.getClassLoader());
+ Server server = factory.create();
+ return new ExportResult(endpointProps, server);
+ } catch (Exception e) {
+ return new ExportResult(endpointProps, e);
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldClassLoader);
+ }
+ }
+
+ protected static void addWsInterceptorsFeaturesProps(AbstractEndpointFactory factory, BundleContext callingContext,
+ Map<String, Object> sd) {
+ addInterceptors(factory, callingContext, sd, Constants.WS_IN_INTERCEPTORS_PROP_KEY);
+ addInterceptors(factory, callingContext, sd, Constants.WS_OUT_INTERCEPTORS_PROP_KEY);
+ addInterceptors(factory, callingContext, sd, Constants.WS_OUT_FAULT_INTERCEPTORS_PROP_KEY);
+ addInterceptors(factory, callingContext, sd, Constants.WS_IN_FAULT_INTERCEPTORS_PROP_KEY);
+ addFeatures(factory, callingContext, sd, Constants.WS_FEATURES_PROP_KEY);
+ addContextProperties(factory, sd, Constants.WS_CONTEXT_PROPS_PROP_KEY);
+ }
+
+ static void addRsInterceptorsFeaturesProps(AbstractEndpointFactory factory, BundleContext callingContext,
+ Map<String, Object> sd) {
+ addInterceptors(factory, callingContext, sd, Constants.RS_IN_INTERCEPTORS_PROP_KEY);
+ addInterceptors(factory, callingContext, sd, Constants.RS_OUT_INTERCEPTORS_PROP_KEY);
+ addInterceptors(factory, callingContext, sd, Constants.RS_OUT_FAULT_INTERCEPTORS_PROP_KEY);
+ addInterceptors(factory, callingContext, sd, Constants.RS_IN_FAULT_INTERCEPTORS_PROP_KEY);
+ addFeatures(factory, callingContext, sd, Constants.RS_FEATURES_PROP_KEY);
+ addContextProperties(factory, sd, Constants.RS_CONTEXT_PROPS_PROP_KEY);
+ }
+
+ private static void addInterceptors(AbstractEndpointFactory factory, BundleContext callingContext,
+ Map<String, Object> sd, String propName) {
+ List<Object> providers = ClassUtils.loadProviderClasses(callingContext, sd, propName);
+ boolean in = propName.contains("in.interceptors");
+ boolean out = propName.contains("out.interceptors");
+ boolean inFault = propName.contains("in.fault.interceptors");
+ boolean outFault = propName.contains("out.fault.interceptors");
+ for (Object provider : providers) {
+ Interceptor<?> interceptor = (Interceptor<?>) provider;
+ if (in) {
+ factory.getInInterceptors().add(interceptor);
+ } else if (out) {
+ factory.getOutInterceptors().add(interceptor);
+ } else if (inFault) {
+ factory.getInFaultInterceptors().add(interceptor);
+ } else if (outFault) {
+ factory.getOutFaultInterceptors().add(interceptor);
+ }
+ }
+ }
+
+ private static void addFeatures(AbstractEndpointFactory factory, BundleContext callingContext,
+ Map<String, Object> sd, String propName) {
+ List<Object> providers = ClassUtils.loadProviderClasses(callingContext, sd, propName);
+ if (!providers.isEmpty()) {
+ factory.getFeatures().addAll(CastUtils.cast(providers, AbstractFeature.class));
+ }
+ }
+
+ private static void addContextProperties(AbstractEndpointFactory factory, Map<String, Object> sd, String propName) {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> props = (Map<String, Object>)sd.get(propName);
+ if (props != null) {
+ factory.getProperties(true).putAll(props);
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ConfigTypeHandlerFactory.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ConfigTypeHandlerFactory.java
new file mode 100644
index 0000000..b7380fb
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ConfigTypeHandlerFactory.java
@@ -0,0 +1,169 @@
+/**
+ * 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.handlers;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.dsw.Constants;
+import org.apache.cxf.dosgi.dsw.qos.IntentManager;
+import org.apache.cxf.dosgi.dsw.util.OsgiUtils;
+import org.apache.cxf.dosgi.dsw.util.Utils;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConfigTypeHandlerFactory {
+
+ protected static final String DEFAULT_CONFIGURATION_TYPE = Constants.WS_CONFIG_TYPE;
+ private static final Logger LOG = LoggerFactory.getLogger(ConfigTypeHandlerFactory.class);
+
+ // protected because of tests
+ protected final List<String> supportedConfigurationTypes;
+
+ private IntentManager intentManager;
+ private PojoConfigurationTypeHandler pojoConfigurationTypeHandler;
+ private JaxRSPojoConfigurationTypeHandler jaxRsPojoConfigurationTypeHandler;
+ private WsdlConfigurationTypeHandler wsdlConfigurationTypeHandler;
+
+ public ConfigTypeHandlerFactory(BundleContext bc, IntentManager intentManager,
+ HttpServiceManager httpServiceManager) {
+ this.intentManager = intentManager;
+ this.pojoConfigurationTypeHandler = new PojoConfigurationTypeHandler(bc, intentManager, httpServiceManager);
+ this.jaxRsPojoConfigurationTypeHandler = new JaxRSPojoConfigurationTypeHandler(bc,
+ intentManager,
+ httpServiceManager);
+ this.wsdlConfigurationTypeHandler = new WsdlConfigurationTypeHandler(bc, intentManager, httpServiceManager);
+ supportedConfigurationTypes = new ArrayList<String>();
+ supportedConfigurationTypes.add(Constants.WSDL_CONFIG_TYPE);
+ supportedConfigurationTypes.add(Constants.RS_CONFIG_TYPE);
+ supportedConfigurationTypes.add(Constants.WS_CONFIG_TYPE);
+ supportedConfigurationTypes.add(Constants.WS_CONFIG_TYPE_OLD);
+ }
+
+ public ConfigurationTypeHandler getHandler(BundleContext dswBC,
+ Map<String, Object> serviceProperties) {
+ List<String> configurationTypes = determineConfigurationTypes(serviceProperties);
+ return getHandler(dswBC, configurationTypes, serviceProperties);
+ }
+
+ public ConfigurationTypeHandler getHandler(BundleContext dswBC, EndpointDescription endpoint) {
+ List<String> configurationTypes = determineConfigTypesForImport(endpoint);
+ return getHandler(dswBC, configurationTypes, endpoint.getProperties());
+ }
+
+ private ConfigurationTypeHandler getHandler(BundleContext dswBC,
+ List<String> configurationTypes,
+ Map<String, Object> serviceProperties) {
+ intentManager.assertAllIntentsSupported(serviceProperties);
+ if (configurationTypes.contains(Constants.WS_CONFIG_TYPE)
+ || configurationTypes.contains(Constants.WS_CONFIG_TYPE_OLD)
+ || configurationTypes.contains(Constants.RS_CONFIG_TYPE)) {
+ boolean jaxrs = isJaxrsRequested(configurationTypes, serviceProperties);
+ return jaxrs ? jaxRsPojoConfigurationTypeHandler : pojoConfigurationTypeHandler;
+ } else if (configurationTypes.contains(Constants.WSDL_CONFIG_TYPE)) {
+ return wsdlConfigurationTypeHandler;
+ }
+ throw new RuntimeException("None of the configuration types in " + configurationTypes + " is supported.");
+ }
+
+ private boolean isJaxrsRequested(Collection<String> types, Map<String, Object> serviceProperties) {
+ if (types == null) {
+ return false;
+ }
+
+ if (types.contains(Constants.RS_CONFIG_TYPE)) {
+ Collection<String> intentsProperty
+ = OsgiUtils.getMultiValueProperty(serviceProperties.get(RemoteConstants.SERVICE_EXPORTED_INTENTS));
+ boolean hasHttpIntent = false;
+ boolean hasSoapIntent = false;
+ if (intentsProperty != null) {
+ for (String intent : intentsProperty) {
+ if (intent.contains("SOAP")) {
+ hasSoapIntent = true;
+ break;
+ }
+
+ if (intent.contains("HTTP")) {
+ hasHttpIntent = true;
+ }
+ }
+ }
+ if ((hasHttpIntent && !hasSoapIntent) || intentsProperty == null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * determine which configuration types should be used / if the requested are
+ * supported
+ */
+ private List<String> determineConfigurationTypes(Map<String, Object> serviceProperties) {
+ String[] requestedConfigurationTypes = Utils.normalizeStringPlus(serviceProperties
+ .get(RemoteConstants.SERVICE_EXPORTED_CONFIGS));
+ if (requestedConfigurationTypes == null || requestedConfigurationTypes.length == 0) {
+ return Collections.singletonList(DEFAULT_CONFIGURATION_TYPE);
+ }
+
+ List<String> configurationTypes = new ArrayList<String>();
+ for (String rct : requestedConfigurationTypes) {
+ if (supportedConfigurationTypes.contains(rct)) {
+ configurationTypes.add(rct);
+ }
+ }
+ LOG.info("configuration types selected for export: " + configurationTypes);
+ if (configurationTypes.isEmpty()) {
+ throw new RuntimeException("the requested configuration types are not supported");
+ }
+ return configurationTypes;
+ }
+
+ private List<String> determineConfigTypesForImport(EndpointDescription endpoint) {
+ List<String> remoteConfigurationTypes = endpoint.getConfigurationTypes();
+
+ if (remoteConfigurationTypes == null) {
+ throw new RuntimeException("The supplied endpoint has no configuration type");
+ }
+
+ List<String> usableConfigurationTypes = new ArrayList<String>();
+ for (String ct : supportedConfigurationTypes) {
+ if (remoteConfigurationTypes.contains(ct)) {
+ usableConfigurationTypes.add(ct);
+ }
+ }
+
+ if (usableConfigurationTypes.isEmpty()) {
+ throw new RuntimeException("The supplied endpoint has no compatible configuration type. "
+ + "Supported types are: " + supportedConfigurationTypes
+ + " Types needed by the endpoint: " + remoteConfigurationTypes);
+ }
+ return usableConfigurationTypes;
+ }
+
+ public List<String> getSupportedConfigurationTypes() {
+ return supportedConfigurationTypes;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ConfigurationTypeHandler.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ConfigurationTypeHandler.java
new file mode 100644
index 0000000..a69c5e1
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ConfigurationTypeHandler.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.cxf.dosgi.dsw.handlers;
+
+import java.util.Map;
+
+import org.apache.cxf.dosgi.dsw.qos.IntentUnsatisfiedException;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+
+public interface ConfigurationTypeHandler {
+
+ String[] getSupportedTypes();
+
+ ExportResult createServer(ServiceReference serviceReference,
+ BundleContext dswContext,
+ BundleContext callingContext,
+ Map<String, Object> sd,
+ Class<?> iClass,
+ Object serviceBean);
+
+ Object createProxy(ServiceReference serviceReference,
+ BundleContext dswContext,
+ BundleContext callingContext,
+ Class<?> iClass, EndpointDescription endpoint) throws IntentUnsatisfiedException;
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ExportResult.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ExportResult.java
new file mode 100644
index 0000000..47b7bf6
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ExportResult.java
@@ -0,0 +1,54 @@
+/**
+ * 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.handlers;
+
+import java.util.Map;
+
+import org.apache.cxf.endpoint.Server;
+
+public class ExportResult {
+
+ private final Map<String, Object> endpointProps;
+ private final Server server;
+ private final Exception exception;
+
+ public ExportResult(Map<String, Object> endpointProps, Server server) {
+ this.endpointProps = endpointProps;
+ this.server = server;
+ this.exception = null;
+ }
+
+ public ExportResult(Map<String, Object> endpointProps, Exception ex) {
+ this.endpointProps = endpointProps;
+ this.server = null;
+ this.exception = ex;
+ }
+
+ public Map<String, Object> getEndpointProps() {
+ return endpointProps;
+ }
+
+ public Server getServer() {
+ return server;
+ }
+
+ public Exception getException() {
+ return exception;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/HttpServiceManager.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/HttpServiceManager.java
new file mode 100644
index 0000000..4aaf3ae
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/HttpServiceManager.java
@@ -0,0 +1,183 @@
+/**
+ * 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.handlers;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.bus.CXFBusFactory;
+import org.apache.cxf.dosgi.dsw.Constants;
+import org.apache.cxf.dosgi.dsw.util.OsgiUtils;
+import org.apache.cxf.transport.http.DestinationRegistry;
+import org.apache.cxf.transport.http.DestinationRegistryImpl;
+import org.apache.cxf.transport.servlet.CXFNonSpringServlet;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceException;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HttpServiceManager {
+
+ private static final Logger LOG = LoggerFactory.getLogger(HttpServiceManager.class);
+ private ServiceTracker tracker;
+ private BundleContext bundleContext;
+ private Map<Long, String> exportedAliases = Collections.synchronizedMap(new HashMap<Long, String>());
+ private String httpBase;
+ private String cxfServletAlias;
+
+ public HttpServiceManager(BundleContext bundleContext, String httpBase, String cxfServletAlias) {
+ this(bundleContext, httpBase, cxfServletAlias,
+ new ServiceTracker(bundleContext, HttpService.class.getName(), null));
+ this.tracker.open();
+ }
+
+ // Only for tests
+ public HttpServiceManager(BundleContext bundleContext,
+ String httpBase, String cxfServletAlias,
+ ServiceTracker tracker) {
+ this.bundleContext = bundleContext;
+ this.tracker = tracker;
+ this.httpBase = getWithDefault(httpBase, "http://" + LocalHostUtil.getLocalIp() + ":8181");
+ this.cxfServletAlias = getWithDefault(cxfServletAlias, "/cxf");
+ }
+
+ private String getWithDefault(String value, String defaultValue) {
+ return value == null ? defaultValue : value;
+ }
+
+ public Bus registerServletAndGetBus(String contextRoot, BundleContext callingContext,
+ ServiceReference sref) {
+ Bus bus = new CXFBusFactory().createBus();
+ bus.setExtension(new DestinationRegistryImpl(), DestinationRegistry.class);
+ CXFNonSpringServlet cxf = new CXFNonSpringServlet();
+ cxf.setBus(bus);
+ try {
+ HttpService httpService = getHttpService();
+ httpService.registerServlet(contextRoot, cxf, new Hashtable<String, String>(),
+ getHttpContext(callingContext, httpService));
+ registerUnexportHook(sref, contextRoot);
+
+ LOG.info("Successfully registered CXF DOSGi servlet at " + contextRoot);
+ } catch (Exception e) {
+ throw new ServiceException("CXF DOSGi: problem registering CXF HTTP Servlet", e);
+ }
+ return bus;
+ }
+
+ protected HttpService getHttpService() {
+ Object service = tracker.getService();
+ if (service == null) {
+ throw new RuntimeException("No HTTPService found");
+ }
+ return (HttpService) service;
+ }
+
+ public String getServletContextRoot(Map<String, Object> sd) {
+ return OsgiUtils.getFirstNonEmptyStringProperty(sd,
+ Constants.WS_HTTP_SERVICE_CONTEXT,
+ Constants.WS_HTTP_SERVICE_CONTEXT_OLD,
+ Constants.WSDL_HTTP_SERVICE_CONTEXT,
+ Constants.RS_HTTP_SERVICE_CONTEXT);
+ }
+
+ private HttpContext getHttpContext(BundleContext bc, HttpService httpService) {
+ HttpContext httpContext = httpService.createDefaultHttpContext();
+ return new SecurityDelegatingHttpContext(bc, httpContext);
+ }
+
+ /**
+ * This listens for service removal events and "un-exports" the service
+ * from the HttpService.
+ *
+ * @param sref the service reference to track
+ * @param alias the HTTP servlet context alias
+ */
+ private void registerUnexportHook(ServiceReference sref, String alias) {
+ final Long sid = (Long) sref.getProperty(org.osgi.framework.Constants.SERVICE_ID);
+ LOG.debug("Registering service listener for service with ID {}", sid);
+
+ String previous = exportedAliases.put(sid, alias);
+ if (previous != null) {
+ LOG.warn("Overwriting service export for service with ID {}", sid);
+ }
+
+ try {
+ Filter f = bundleContext.createFilter("(" + org.osgi.framework.Constants.SERVICE_ID + "=" + sid + ")");
+ if (f != null) {
+ bundleContext.addServiceListener(new UnregisterListener(), f.toString());
+ } else {
+ LOG.warn("Service listener could not be started. The service will not be automatically unexported.");
+ }
+ } catch (InvalidSyntaxException e) {
+ LOG.warn("Service listener could not be started. The service will not be automatically unexported.", e);
+ }
+ }
+
+ protected String getDefaultAddress(Class<?> type) {
+ return "/" + type.getName().replace('.', '/');
+ }
+
+ protected String getAbsoluteAddress(String contextRoot, String relativeEndpointAddress) {
+ if (relativeEndpointAddress.startsWith("http")) {
+ return relativeEndpointAddress;
+ }
+ String effContextRoot = contextRoot == null ? cxfServletAlias : contextRoot;
+ return this.httpBase + effContextRoot + relativeEndpointAddress;
+ }
+
+ public void close() {
+ tracker.close();
+ }
+
+ private final class UnregisterListener implements ServiceListener {
+
+ public void serviceChanged(ServiceEvent event) {
+ if (!(event.getType() == ServiceEvent.UNREGISTERING)) {
+ return;
+ }
+ final ServiceReference sref = event.getServiceReference();
+ final Long sid = (Long) sref.getProperty(org.osgi.framework.Constants.SERVICE_ID);
+ final String alias = exportedAliases.remove(sid);
+ if (alias == null) {
+ LOG.error("Unable to unexport HTTP servlet for service class '{}',"
+ + " service-id {}: no servlet alias found",
+ sref.getProperty(org.osgi.framework.Constants.OBJECTCLASS), sid);
+ return;
+ }
+ LOG.debug("Unexporting HTTP servlet for alias '{}'", alias);
+ try {
+ HttpService http = getHttpService();
+ http.unregister(alias);
+ } catch (Exception e) {
+ LOG.warn("An exception occurred while unregistering service for HTTP servlet alias '{}'", alias, e);
+ }
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSPojoConfigurationTypeHandler.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSPojoConfigurationTypeHandler.java
new file mode 100644
index 0000000..b73e48b
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSPojoConfigurationTypeHandler.java
@@ -0,0 +1,204 @@
+/**
+ * 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.handlers;
+
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.dosgi.dsw.Constants;
+import org.apache.cxf.dosgi.dsw.qos.IntentManager;
+import org.apache.cxf.dosgi.dsw.qos.IntentUnsatisfiedException;
+import org.apache.cxf.dosgi.dsw.util.OsgiUtils;
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.client.Client;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
+import org.apache.cxf.jaxrs.client.ProxyClassLoader;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.jaxrs.model.UserResource;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JaxRSPojoConfigurationTypeHandler extends AbstractPojoConfigurationTypeHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(JaxRSPojoConfigurationTypeHandler.class);
+
+ public JaxRSPojoConfigurationTypeHandler(BundleContext dswBC,
+ IntentManager intentManager,
+ HttpServiceManager httpServiceManager) {
+ super(dswBC, intentManager, httpServiceManager);
+ }
+
+ public String[] getSupportedTypes() {
+ return new String[] {Constants.RS_CONFIG_TYPE};
+ }
+
+ public Object createProxy(ServiceReference serviceReference, BundleContext dswContext,
+ BundleContext callingContext, Class<?> iClass,
+ EndpointDescription endpoint) throws IntentUnsatisfiedException {
+ String address = getPojoAddress(endpoint, iClass);
+ if (address == null) {
+ LOG.warn("Remote address is unavailable");
+ return null;
+ }
+
+ ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ return createJaxrsProxy(address, callingContext, dswContext, iClass, null, endpoint);
+ } catch (Throwable e) {
+ Thread.currentThread().setContextClassLoader(oldClassLoader);
+ }
+
+ try {
+ ProxyClassLoader cl = new ProxyClassLoader();
+ cl.addLoader(iClass.getClassLoader());
+ cl.addLoader(Client.class.getClassLoader());
+ return createJaxrsProxy(address, callingContext, dswContext, iClass, cl, endpoint);
+ } catch (Throwable e) {
+ LOG.warn("proxy creation failed", e);
+ }
+
+ return null;
+ }
+
+ protected Object createJaxrsProxy(String address,
+ BundleContext dswContext,
+ BundleContext callingContext,
+ Class<?> iClass,
+ ClassLoader loader,
+ EndpointDescription endpoint) {
+ JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();
+ bean.setAddress(address);
+ if (loader != null) {
+ bean.setClassLoader(loader);
+ }
+
+ addRsInterceptorsFeaturesProps(bean, callingContext, endpoint.getProperties());
+
+ List<UserResource> resources = JaxRSUtils.getModel(callingContext, iClass);
+ if (resources != null) {
+ bean.setModelBeansWithServiceClass(resources, iClass);
+ } else {
+ bean.setServiceClass(iClass);
+ }
+ List<Object> providers = JaxRSUtils.getProviders(callingContext, endpoint.getProperties());
+ if (providers != null && !providers.isEmpty()) {
+ bean.setProviders(providers);
+ }
+ Thread.currentThread().setContextClassLoader(JAXRSClientFactoryBean.class.getClassLoader());
+ return getProxy(bean.create(), iClass);
+ }
+
+ public ExportResult createServer(ServiceReference sref,
+ BundleContext dswContext,
+ BundleContext callingContext,
+ Map<String, Object> sd, Class<?> iClass,
+ Object serviceBean) throws IntentUnsatisfiedException {
+ String contextRoot = httpServiceManager.getServletContextRoot(sd);
+ String address;
+ if (contextRoot == null) {
+ address = getServerAddress(sd, iClass);
+ } else {
+ address = getClientAddress(sd);
+ if (address == null) {
+ address = "/";
+ }
+ }
+
+ Bus bus = contextRoot != null
+ ? httpServiceManager.registerServletAndGetBus(contextRoot, callingContext, sref) : null;
+
+ LOG.info("Creating a " + iClass.getName()
+ + " endpoint via JaxRSPojoConfigurationTypeHandler, address is " + address);
+
+ JAXRSServerFactoryBean factory = createServerFactory(callingContext, sd, iClass, serviceBean, address, bus);
+ String completeEndpointAddress = httpServiceManager.getAbsoluteAddress(contextRoot, address);
+
+ // The properties for the EndpointDescription
+ Map<String, Object> endpointProps = createEndpointProps(sd, iClass, new String[] {Constants.RS_CONFIG_TYPE},
+ completeEndpointAddress, new String[] {"HTTP"});
+
+ return createServerFromFactory(factory, endpointProps);
+ }
+
+ private ExportResult createServerFromFactory(JAXRSServerFactoryBean factory,
+ Map<String, Object> endpointProps) {
+ ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(JAXRSServerFactoryBean.class.getClassLoader());
+ Server server = factory.create();
+ return new ExportResult(endpointProps, server);
+ } catch (Exception e) {
+ return new ExportResult(endpointProps, e);
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldClassLoader);
+ }
+ }
+
+ private JAXRSServerFactoryBean createServerFactory(BundleContext callingContext,
+ Map<String, Object> sd,
+ Class<?> iClass,
+ Object serviceBean,
+ String address,
+ Bus bus) {
+ JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
+ if (bus != null) {
+ factory.setBus(bus);
+ }
+ List<UserResource> resources = JaxRSUtils.getModel(callingContext, iClass);
+ if (resources != null) {
+ factory.setModelBeansWithServiceClass(resources, iClass);
+ factory.setServiceBeanObjects(serviceBean);
+ } else {
+ factory.setServiceClass(iClass);
+ factory.setResourceProvider(iClass, new SingletonResourceProvider(serviceBean));
+ }
+ factory.setAddress(address);
+ List<Object> providers = JaxRSUtils.getProviders(callingContext, sd);
+ if (providers != null && !providers.isEmpty()) {
+ factory.setProviders(providers);
+ }
+ addRsInterceptorsFeaturesProps(factory, callingContext, sd);
+ String location = OsgiUtils.getProperty(sd, Constants.RS_WADL_LOCATION);
+ if (location != null) {
+ URL wadlURL = callingContext.getBundle().getResource(location);
+ if (wadlURL != null) {
+ factory.setDocLocation(wadlURL.toString());
+ }
+ }
+ return factory;
+ }
+
+ protected String getPojoAddress(EndpointDescription endpoint, Class<?> iClass) {
+ String address = OsgiUtils.getProperty(endpoint, Constants.RS_ADDRESS_PROPERTY);
+
+ if (address == null) {
+ address = httpServiceManager.getDefaultAddress(iClass);
+ if (address != null) {
+ LOG.info("Using a default address: " + address);
+ }
+ }
+ return address;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSUtils.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSUtils.java
new file mode 100644
index 0000000..5573c09
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSUtils.java
@@ -0,0 +1,119 @@
+/**
+ * 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.handlers;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.dsw.util.ClassUtils;
+import org.apache.cxf.dosgi.dsw.util.OsgiUtils;
+import org.apache.cxf.jaxrs.model.UserResource;
+import org.apache.cxf.jaxrs.provider.aegis.AegisElementProvider;
+import org.apache.cxf.jaxrs.utils.ResourceUtils;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class JaxRSUtils {
+
+ public static final String MODEL_FOLDER = "/OSGI-INF/cxf/jaxrs/";
+ public static final String DEFAULT_MODEL = "/OSGI-INF/cxf/jaxrs/model.xml";
+ public static final String PROVIDERS_FILTER = "(|"
+ + "(objectClass=javax.ws.rs.ext.MessageBodyReader)"
+ + "(objectClass=javax.ws.rs.ext.MessageBodyWriter)"
+ + "(objectClass=javax.ws.rs.ext.ExceptionMapper)"
+ + "(objectClass=org.apache.cxf.jaxrs.ext.RequestHandler)"
+ + "(objectClass=org.apache.cxf.jaxrs.ext.ResponseHandler)"
+ + "(objectClass=org.apache.cxf.jaxrs.ext.ParameterHandler)"
+ + "(objectClass=org.apache.cxf.jaxrs.ext.ResponseExceptionMapper)"
+ + ")";
+ private static final Logger LOG = LoggerFactory.getLogger(JaxRSUtils.class);
+
+ private JaxRSUtils() {
+ // never constructed
+ }
+
+ static List<Object> getProviders(BundleContext callingContext, Map<String, Object> sd) {
+ List<Object> providers = new ArrayList<Object>();
+ if ("aegis".equals(sd.get(org.apache.cxf.dosgi.dsw.Constants.RS_DATABINDING_PROP_KEY))) {
+ providers.add(new AegisElementProvider());
+ }
+
+ providers.addAll(ClassUtils.loadProviderClasses(callingContext,
+ sd,
+ org.apache.cxf.dosgi.dsw.Constants.RS_PROVIDER_PROP_KEY));
+
+ Object globalQueryProp = sd.get(org.apache.cxf.dosgi.dsw.Constants.RS_PROVIDER_GLOBAL_PROP_KEY);
+ boolean globalQueryRequired = globalQueryProp == null || OsgiUtils.toBoolean(globalQueryProp);
+ if (!globalQueryRequired) {
+ return providers;
+ }
+
+ boolean cxfProvidersOnly = OsgiUtils.getBooleanProperty(sd,
+ org.apache.cxf.dosgi.dsw.Constants.RS_PROVIDER_EXPECTED_PROP_KEY);
+
+ try {
+ ServiceReference[] refs = callingContext.getServiceReferences((String)null, PROVIDERS_FILTER);
+ if (refs != null) {
+ for (ServiceReference ref : refs) {
+ if (!cxfProvidersOnly
+ || OsgiUtils.toBoolean(ref
+ .getProperty(org.apache.cxf.dosgi.dsw.Constants.RS_PROVIDER_PROP_KEY))) {
+ providers.add(callingContext.getService(ref));
+ }
+ }
+ }
+ } catch (Exception ex) {
+ LOG.debug("Problems finding JAXRS providers " + ex.getMessage(), ex);
+ }
+ return providers;
+ }
+
+ static List<UserResource> getModel(BundleContext callingContext, Class<?> iClass) {
+ String classModel = MODEL_FOLDER + iClass.getSimpleName() + "-model.xml";
+ List<UserResource> list = getModel(callingContext, iClass, classModel);
+ return list != null ? list : getModel(callingContext, iClass, DEFAULT_MODEL);
+ }
+
+ private static List<UserResource> getModel(BundleContext callingContext, Class<?> iClass, String name) {
+ InputStream r = iClass.getClassLoader().getResourceAsStream(name);
+ if (r == null) {
+ URL u = callingContext.getBundle().getResource(name);
+ if (u != null) {
+ try {
+ r = u.openStream();
+ } catch (Exception ex) {
+ LOG.info("Problems opening a user model resource at " + u.toString());
+ }
+ }
+ }
+ if (r != null) {
+ try {
+ return ResourceUtils.getUserResources(r);
+ } catch (Exception ex) {
+ LOG.info("Problems reading a user model, it will be ignored");
+ }
+ }
+ return null;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/LocalHostUtil.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/LocalHostUtil.java
new file mode 100644
index 0000000..50e2127
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/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.cxf.dosgi.dsw.handlers;
+
+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;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/PojoConfigurationTypeHandler.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/PojoConfigurationTypeHandler.java
new file mode 100644
index 0000000..844d1e2
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/PojoConfigurationTypeHandler.java
@@ -0,0 +1,174 @@
+/**
+ * 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.handlers;
+
+import java.util.Map;
+
+import javax.jws.WebService;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.aegis.databinding.AegisDatabinding;
+import org.apache.cxf.databinding.DataBinding;
+import org.apache.cxf.dosgi.dsw.Constants;
+import org.apache.cxf.dosgi.dsw.qos.IntentManager;
+import org.apache.cxf.dosgi.dsw.qos.IntentUnsatisfiedException;
+import org.apache.cxf.frontend.ClientProxyFactoryBean;
+import org.apache.cxf.frontend.ServerFactoryBean;
+import org.apache.cxf.jaxb.JAXBDataBinding;
+import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
+import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PojoConfigurationTypeHandler extends AbstractPojoConfigurationTypeHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PojoConfigurationTypeHandler.class);
+
+ public PojoConfigurationTypeHandler(BundleContext dswBC,
+ IntentManager intentManager,
+ HttpServiceManager httpServiceManager) {
+ super(dswBC, intentManager, httpServiceManager);
+ }
+
+ public String[] getSupportedTypes() {
+ return new String[] {Constants.WS_CONFIG_TYPE, Constants.WS_CONFIG_TYPE_OLD};
+ }
+
+ public Object createProxy(ServiceReference sref, BundleContext dswContext, BundleContext callingContext,
+ Class<?> iClass, EndpointDescription endpoint) throws IntentUnsatisfiedException {
+ Map<String, Object> sd = endpoint.getProperties();
+ String address = getClientAddress(sd);
+ if (address == null) {
+ LOG.warn("Remote address is unavailable");
+ // TODO: fire Event
+ return null;
+ }
+
+ LOG.info("Creating a " + iClass.getName() + " client, endpoint address is " + address);
+
+ ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ ClientProxyFactoryBean factory = createClientProxyFactoryBean(sd, iClass);
+ factory.getServiceFactory().setDataBinding(getDataBinding(sd, iClass));
+ factory.setServiceClass(iClass);
+ factory.setAddress(address);
+ addWsInterceptorsFeaturesProps(factory.getClientFactoryBean(), callingContext, sd);
+ setClientWsdlProperties(factory.getClientFactoryBean(), dswContext, sd, false);
+
+ intentManager.applyIntents(factory.getFeatures(), factory.getClientFactoryBean(), sd);
+
+ Thread.currentThread().setContextClassLoader(ClientProxyFactoryBean.class.getClassLoader());
+ return getProxy(factory.create(), iClass);
+ } catch (Exception e) {
+ LOG.warn("proxy creation failed", e);
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldClassLoader);
+ }
+ return null;
+ }
+
+ public ExportResult createServer(ServiceReference sref,
+ BundleContext dswContext,
+ BundleContext callingContext,
+ Map<String, Object> sd,
+ Class<?> iClass,
+ Object serviceBean) throws IntentUnsatisfiedException {
+ try {
+ String address = getPojoAddress(sd, iClass);
+ String contextRoot = httpServiceManager.getServletContextRoot(sd);
+
+ ServerFactoryBean factory = createServerFactoryBean(sd, iClass);
+ factory.setDataBinding(getDataBinding(sd, iClass));
+ if (contextRoot != null) {
+ Bus bus = httpServiceManager.registerServletAndGetBus(contextRoot, callingContext, sref);
+ factory.setBus(bus);
+ }
+ factory.setServiceClass(iClass);
+ factory.setAddress(address);
+ factory.setServiceBean(serviceBean);
+ addWsInterceptorsFeaturesProps(factory, callingContext, sd);
+ setWsdlProperties(factory, callingContext, sd, false);
+ String[] intents = intentManager.applyIntents(factory.getFeatures(), factory, sd);
+
+ String completeEndpointAddress = httpServiceManager.getAbsoluteAddress(contextRoot, address);
+
+ // The properties for the EndpointDescription
+ Map<String, Object> endpointProps = createEndpointProps(sd, iClass,
+ new String[]{Constants.WS_CONFIG_TYPE},
+ completeEndpointAddress, intents);
+
+ return createServerFromFactory(factory, endpointProps);
+ } catch (RuntimeException re) {
+ return new ExportResult(sd, re);
+ }
+ }
+
+ private String getPojoAddress(Map<String, Object> sd, Class<?> iClass) {
+ String address = getClientAddress(sd);
+ if (address != null) {
+ return address;
+ }
+
+ // If the property is not of type string this will cause an ClassCastException which
+ // will be propagated to the ExportRegistration exception property.
+ Object port = sd.get(Constants.WS_PORT_PROPERTY);
+ if (port == null) {
+ port = "9000";
+ }
+
+ address = "http://localhost:" + port + "/" + iClass.getName().replace('.', '/');
+ LOG.info("Using a default address: " + address);
+ return address;
+ }
+
+ private DataBinding getDataBinding(Map<String, Object> sd, Class<?> iClass) {
+ Object dataBindingBeanProp = sd.get(Constants.WS_DATABINDING_BEAN_PROP_KEY);
+ if (dataBindingBeanProp instanceof DataBinding) {
+ return (DataBinding)dataBindingBeanProp;
+ }
+ return isJAXB(sd, iClass) ? new JAXBDataBinding() : new AegisDatabinding();
+ }
+
+ private boolean isJAXB(Map<String, Object> sd, Class<?> iClass) {
+ String dataBindingName = (String)sd.get(Constants.WS_DATABINDING_PROP_KEY);
+ return (iClass.getAnnotation(WebService.class) != null
+ || Constants.WS_DATA_BINDING_JAXB.equals(dataBindingName))
+ && !Constants.WS_DATA_BINDING_AEGIS.equals(dataBindingName);
+ }
+
+ // Isolated so that it can be substituted for testing
+ protected ClientProxyFactoryBean createClientProxyFactoryBean(Map<String, Object> sd, Class<?> iClass) {
+ return isJAXWS(sd, iClass) ? new JaxWsProxyFactoryBean() : new ClientProxyFactoryBean();
+ }
+
+ // Isolated so that it can be substituted for testing
+ protected ServerFactoryBean createServerFactoryBean(Map<String, Object> sd, Class<?> iClass) {
+ return isJAXWS(sd, iClass) ? new JaxWsServerFactoryBean() : new ServerFactoryBean();
+ }
+
+ private boolean isJAXWS(Map<String, Object> sd, Class<?> iClass) {
+ String frontEnd = (String)sd.get(Constants.WS_FRONTEND_PROP_KEY);
+ return (iClass.getAnnotation(WebService.class) != null
+ || Constants.WS_FRONTEND_JAXWS.equals(frontEnd))
+ && !Constants.WS_FRONTEND_SIMPLE.equals(frontEnd);
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/SecurityDelegatingHttpContext.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/SecurityDelegatingHttpContext.java
new file mode 100644
index 0000000..a9c12ef
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/SecurityDelegatingHttpContext.java
@@ -0,0 +1,130 @@
+/**
+ * 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.handlers;
+
+import java.io.IOException;
+import java.net.URL;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <p>
+ * An HttpContext that delegates to another HttpContext for all things other than security. This implementation handles
+ * security by delegating to a {@link FilterChain} based on the set of {@link Filter}s registered with a
+ * {@link #FILTER_PROP} property.
+ * </p>
+ * <p>
+ * If the {@link BundleContext} contains a {@link #FILTER_REQUIRED_PROP} property with value "true", requests will not
+ * be allowed until at least one {@link Filter} with a {@link #FILTER_PROP} property is registered.
+ * </p>
+ */
+public class SecurityDelegatingHttpContext implements HttpContext {
+
+ public static final String FILTER_PROP = "org.apache.cxf.httpservice.filter";
+ public static final String FILTER_REQUIRED_PROP = "org.apache.cxf.httpservice.requirefilter";
+ private static final Logger LOG = LoggerFactory.getLogger(SecurityDelegatingHttpContext.class);
+ private static final String FILTER_FILTER = "(" + FILTER_PROP + "=*)";
+
+ BundleContext bundleContext;
+ HttpContext delegate;
+ boolean requireFilter;
+
+ public SecurityDelegatingHttpContext(BundleContext bundleContext, HttpContext delegate) {
+ this.bundleContext = bundleContext;
+ this.delegate = delegate;
+ requireFilter = Boolean.TRUE.toString().equalsIgnoreCase(bundleContext.getProperty(FILTER_REQUIRED_PROP));
+ }
+
+ public String getMimeType(String name) {
+ return delegate.getMimeType(name);
+ }
+
+ public URL getResource(String name) {
+ return delegate.getResource(name);
+ }
+
+ public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ ServiceReference[] refs;
+ try {
+ refs = bundleContext.getServiceReferences(Filter.class.getName(), FILTER_FILTER);
+ } catch (InvalidSyntaxException e) {
+ LOG.warn(e.getMessage(), e);
+ return false;
+ }
+ if (refs == null || refs.length == 0) {
+ LOG.info("No filter registered.");
+ return !requireFilter;
+ }
+ Filter[] filters = new Filter[refs.length];
+ try {
+ for (int i = 0; i < refs.length; i++) {
+ filters[i] = (Filter)bundleContext.getService(refs[i]);
+ }
+ try {
+ new Chain(filters).doFilter(request, response);
+ return !response.isCommitted();
+ } catch (ServletException e) {
+ LOG.warn(e.getMessage(), e);
+ return false;
+ }
+ } finally {
+ for (int i = 0; i < refs.length; i++) {
+ if (filters[i] != null) {
+ bundleContext.ungetService(refs[i]);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * A {@link FilterChain} composed of {@link Filter}s with the
+ */
+class Chain implements FilterChain {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Chain.class);
+
+ int current;
+ final Filter[] filters;
+
+ Chain(Filter[] filters) {
+ this.filters = filters;
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
+ if (current < filters.length && !response.isCommitted()) {
+ Filter filter = filters[current++];
+ LOG.info("doFilter() on {}", filter);
+ filter.doFilter(request, response, this);
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ServiceInvocationHandler.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ServiceInvocationHandler.java
new file mode 100644
index 0000000..6171a53
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/ServiceInvocationHandler.java
@@ -0,0 +1,100 @@
+/**
+ * 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.handlers;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.ServiceException;
+
+public class ServiceInvocationHandler implements InvocationHandler {
+
+ private static final String REMOTE_EXCEPTION_TYPE = "REMOTE";
+ private static final Collection<Method> OBJECT_METHODS = Arrays.asList(Object.class.getMethods());
+
+ private Map<Method, List<Class<?>>> exceptionsMap = new HashMap<Method, List<Class<?>>>();
+ private Object serviceObject;
+
+ public ServiceInvocationHandler(Object serviceObject, Class<?> iType) {
+ this.serviceObject = serviceObject;
+ introspectType(iType);
+ }
+
+ public Object invoke(Object proxy, final Method m, Object[] params) throws Throwable {
+ if (OBJECT_METHODS.contains(m)) {
+ if (m.getName().equals("equals")) {
+ params = new Object[] {Proxy.getInvocationHandler(params[0])};
+ }
+ return m.invoke(this, params);
+ }
+
+ ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+ final Object[] paramsFinal = params;
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ public Object run() throws Exception {
+ return m.invoke(serviceObject, paramsFinal);
+ }
+ });
+ } catch (Throwable ex) {
+ Throwable theCause = ex.getCause() == null ? ex : ex.getCause();
+ Throwable theCauseCause = theCause.getCause() == null ? theCause : theCause.getCause();
+ List<Class<?>> excTypes = exceptionsMap.get(m);
+ if (excTypes != null) {
+ for (Class<?> type : excTypes) {
+ if (type.isAssignableFrom(theCause.getClass())) {
+ throw theCause;
+ }
+ if (type.isAssignableFrom(theCauseCause.getClass())) {
+ throw theCauseCause;
+ }
+ }
+ }
+
+ throw new ServiceException(REMOTE_EXCEPTION_TYPE, theCause);
+ } finally {
+ Thread.currentThread().setContextClassLoader(oldCl);
+ }
+ }
+
+ private void introspectType(Class<?> iType) {
+ for (Method m : iType.getDeclaredMethods()) {
+ for (Class<?> excType : m.getExceptionTypes()) {
+ if (Exception.class.isAssignableFrom(excType)) {
+ List<Class<?>> types = exceptionsMap.get(m);
+ if (types == null) {
+ types = new ArrayList<Class<?>>();
+ exceptionsMap.put(m, types);
+ }
+ types.add(excType);
+ }
+ }
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/WsdlConfigurationTypeHandler.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/WsdlConfigurationTypeHandler.java
new file mode 100644
index 0000000..bada39f
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/handlers/WsdlConfigurationTypeHandler.java
@@ -0,0 +1,160 @@
+/**
+ * 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.handlers;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+
+import javax.xml.namespace.QName;
+import javax.xml.ws.Service;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.common.util.PackageUtils;
+import org.apache.cxf.databinding.DataBinding;
+import org.apache.cxf.dosgi.dsw.Constants;
+import org.apache.cxf.dosgi.dsw.qos.IntentManager;
+import org.apache.cxf.dosgi.dsw.util.OsgiUtils;
+import org.apache.cxf.jaxb.JAXBDataBinding;
+import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class WsdlConfigurationTypeHandler extends AbstractPojoConfigurationTypeHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(WsdlConfigurationTypeHandler.class);
+
+ public WsdlConfigurationTypeHandler(BundleContext dswBC,
+ IntentManager intentManager,
+ HttpServiceManager httpServiceManager) {
+ super(dswBC, intentManager, httpServiceManager);
+ }
+
+ public String[] getSupportedTypes() {
+ return new String[] {Constants.WSDL_CONFIG_TYPE};
+ }
+
+ public Object createProxy(ServiceReference serviceReference,
+ BundleContext dswContext,
+ BundleContext callingContext,
+ Class<?> iClass,
+ EndpointDescription endpoint) {
+ String wsdlAddressProp = getWsdlAddress(endpoint, iClass);
+ if (wsdlAddressProp == null) {
+ LOG.warn("WSDL address is unavailable");
+ return null;
+ }
+
+ URL wsdlAddress;
+ try {
+ wsdlAddress = new URL(wsdlAddressProp);
+ } catch (MalformedURLException ex) {
+ LOG.warn("WSDL address is malformed");
+ return null;
+ }
+
+ LOG.info("Creating a " + endpoint.getInterfaces().toArray()[0] + " client, wsdl address is "
+ + OsgiUtils.getProperty(endpoint, Constants.WSDL_CONFIG_PREFIX));
+
+ String serviceNs = OsgiUtils.getProperty(endpoint, Constants.WSDL_SERVICE_NAMESPACE);
+ if (serviceNs == null) {
+ serviceNs = PackageUtils.getNamespace(PackageUtils.getPackageName(iClass));
+ }
+ String serviceName = OsgiUtils.getProperty(endpoint, Constants.WSDL_SERVICE_NAME);
+ if (serviceName == null) {
+ serviceName = iClass.getSimpleName();
+ }
+ QName serviceQname = getServiceQName(iClass, endpoint.getProperties(),
+ Constants.WSDL_SERVICE_NAMESPACE,
+ Constants.WSDL_SERVICE_NAME);
+ QName portQname = getPortQName(serviceQname.getNamespaceURI(),
+ endpoint.getProperties(), Constants.WSDL_PORT_NAME);
+ Service service = createWebService(wsdlAddress, serviceQname);
+ Object proxy = getProxy(portQname == null ? service.getPort(iClass) : service.getPort(portQname, iClass),
+ iClass);
+ // MARC: FIXME!!!! getDistributionProvider().addRemoteService(serviceReference);
+ return proxy;
+ }
+
+ // Isolated so that it can be overridden for test purposes.
+ Service createWebService(URL wsdlAddress, QName serviceQname) {
+ return Service.create(wsdlAddress, serviceQname);
+ }
+
+ public ExportResult createServer(ServiceReference sref,
+ BundleContext dswContext,
+ BundleContext callingContext,
+ Map<String, Object> sd,
+ Class<?> iClass,
+ Object serviceBean) {
+ String location = OsgiUtils.getProperty(sd, Constants.WSDL_LOCATION);
+ if (location == null) {
+ throw new RuntimeException("WSDL location property is unavailable");
+ }
+ URL wsdlURL = dswContext.getBundle().getResource(location);
+ if (wsdlURL == null) {
+ throw new RuntimeException("WSDL resource at " + location + " is unavailable");
+ }
+
+ String address = getServerAddress(sd, iClass);
+ String contextRoot = httpServiceManager.getServletContextRoot(sd);
+ if (address == null && contextRoot == null) {
+ throw new RuntimeException("Remote address is unavailable");
+ }
+
+ LOG.info("Creating a " + iClass.getName() + " endpoint from CXF PublishHook, address is " + address);
+
+ DataBinding databinding = new JAXBDataBinding();
+ JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
+ if (contextRoot != null) {
+ Bus bus = httpServiceManager.registerServletAndGetBus(contextRoot, callingContext, sref);
+ factory.setBus(bus);
+ }
+ factory.setServiceClass(iClass);
+ factory.setAddress(address != null ? address : "/");
+ factory.getServiceFactory().setDataBinding(databinding);
+ factory.setServiceBean(serviceBean);
+
+ addWsInterceptorsFeaturesProps(factory, callingContext, sd);
+
+ setWsdlProperties(factory, callingContext, sd, true);
+
+ String[] intents = intentManager.applyIntents(factory.getFeatures(), factory, sd);
+
+ // The properties for the EndpointDescription
+ Map<String, Object> endpointProps = createEndpointProps(sd, iClass,
+ new String[]{Constants.WS_CONFIG_TYPE},
+ address, intents);
+ return createServerFromFactory(factory, endpointProps);
+ }
+
+ private String getWsdlAddress(EndpointDescription endpoint, Class<?> iClass) {
+ String address = OsgiUtils.getProperty(endpoint, Constants.WSDL_CONFIG_PREFIX);
+ if (address == null) {
+ address = httpServiceManager.getDefaultAddress(iClass);
+ if (address != null) {
+ address += "?wsdl";
+ }
+ }
+ return address;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/DefaultIntentMapFactory.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/DefaultIntentMapFactory.java
new file mode 100644
index 0000000..7e0dd2e
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/DefaultIntentMapFactory.java
@@ -0,0 +1,52 @@
+/**
+ * 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.qos;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cxf.binding.soap.Soap11;
+import org.apache.cxf.binding.soap.Soap12;
+import org.apache.cxf.binding.soap.SoapBindingConfiguration;
+import org.apache.cxf.binding.soap.SoapVersion;
+import org.apache.cxf.feature.LoggingFeature;
+
+public class DefaultIntentMapFactory {
+
+ public Map<String, Object> create() {
+ Map<String, Object> intentMap = new HashMap<String, Object>();
+ intentMap.put("logging", getLoggingFeature());
+ Object soap11 = getSoapBinding(Soap11.getInstance());
+ intentMap.put("SOAP", soap11);
+ intentMap.put("SOAP.1_1", soap11);
+ intentMap.put("SOAP.1_2", getSoapBinding(Soap12.getInstance()));
+ intentMap.put("HTTP", "PROVIDED");
+ return intentMap;
+ }
+
+ private Object getLoggingFeature() {
+ return new LoggingFeature();
+ }
+
+ private Object getSoapBinding(SoapVersion soapVersion) {
+ SoapBindingConfiguration soapBindingConfig = new SoapBindingConfiguration();
+ soapBindingConfig.setVersion(soapVersion);
+ return soapBindingConfig;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManager.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManager.java
new file mode 100644
index 0000000..ecaf070
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManager.java
@@ -0,0 +1,31 @@
+/**
+ * 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.qos;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.endpoint.AbstractEndpointFactory;
+import org.apache.cxf.feature.Feature;
+
+public interface IntentManager {
+
+ String[] applyIntents(List<Feature> features, AbstractEndpointFactory factory, Map<String, Object> props);
+ void assertAllIntentsSupported(Map<String, Object> serviceProperties);
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManagerImpl.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManagerImpl.java
new file mode 100644
index 0000000..d7b50f2
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentManagerImpl.java
@@ -0,0 +1,154 @@
+/**
+ * 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.qos;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cxf.binding.BindingConfiguration;
+import org.apache.cxf.endpoint.AbstractEndpointFactory;
+import org.apache.cxf.feature.Feature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class IntentManagerImpl implements IntentManager {
+
+ static final Logger LOG = LoggerFactory.getLogger(IntentManagerImpl.class);
+ private static final String PROVIDED_INTENT_VALUE = "PROVIDED";
+
+ private final IntentMap intentMap;
+ private final long maxIntentWaitTime;
+
+ public IntentManagerImpl(IntentMap intentMap) {
+ this(intentMap, 0);
+ }
+
+ public IntentManagerImpl(IntentMap intentMap, int maxIntentWaitTime) {
+ this.intentMap = intentMap;
+ this.maxIntentWaitTime = maxIntentWaitTime;
+ }
+
+ public String[] applyIntents(List<Feature> features, AbstractEndpointFactory factory,
+ Map<String, Object> props) throws IntentUnsatisfiedException {
+ Set<String> requestedIntents = IntentUtils.getRequestedIntents(props);
+ Set<String> appliedIntents = new HashSet<String>();
+ appliedIntents.addAll(reverseLookup(intentMap, PROVIDED_INTENT_VALUE));
+ boolean bindingApplied = false;
+ for (String intentName : requestedIntents) {
+ bindingApplied |= processIntent(features, factory, intentName, intentMap.get(intentName));
+ appliedIntents.add(intentName);
+ }
+ if (!bindingApplied) {
+ String defaultBindingName = "SOAP";
+ processIntent(features, factory, defaultBindingName, intentMap.get(defaultBindingName));
+ appliedIntents.add(defaultBindingName);
+ }
+ appliedIntents.addAll(addSynonymIntents(appliedIntents, intentMap));
+ return appliedIntents.toArray(new String[appliedIntents.size()]);
+ }
+
+ private boolean processIntent(List<Feature> features, AbstractEndpointFactory factory,
+ String intentName, Object intent) throws IntentUnsatisfiedException {
+ if (intent instanceof String) {
+ if (PROVIDED_INTENT_VALUE.equalsIgnoreCase((String) intent)) {
+ return false;
+ }
+ } else if (intent instanceof BindingConfiguration) {
+ BindingConfiguration bindingCfg = (BindingConfiguration)intent;
+ LOG.info("Applying intent: " + intentName + " via binding config: " + bindingCfg);
+ factory.setBindingConfig(bindingCfg);
+ return true;
+ } else if (intent instanceof Feature) {
+ Feature feature = (Feature) intent;
+ LOG.info("Applying intent: " + intentName + " via feature: " + feature);
+ features.add(feature);
+ return false;
+ } else {
+ LOG.info("No mapping for intent: " + intentName);
+ throw new IntentUnsatisfiedException(intentName);
+ }
+ return false;
+ }
+
+ private static Collection<String> addSynonymIntents(Collection<String> appliedIntents,
+ IntentMap map) {
+ // E.g. SOAP and SOAP.1_1 are synonyms
+ List<Object> values = new ArrayList<Object>();
+ for (String key : appliedIntents) {
+ values.add(map.get(key));
+ }
+ return reverseLookup(map, values);
+ }
+
+ private static Collection<String> reverseLookup(IntentMap im, Object obj) {
+ return reverseLookup(im, Collections.singleton(obj));
+ }
+
+ /**
+ * Retrieves all keys whose mapped values are found in the given collection.
+ *
+ * @param im an intent map
+ * @param values a collection of potential values
+ * @return all keys whose mapped values are found in the given collection
+ */
+ private static Collection<String> reverseLookup(IntentMap im, Collection<?> values) {
+ Set<String> intentsFound = new HashSet<String>();
+ for (Map.Entry<String, Object> entry : im.entrySet()) {
+ if (values.contains(entry.getValue())) {
+ intentsFound.add(entry.getKey());
+ }
+ }
+ return intentsFound;
+ }
+
+ public void assertAllIntentsSupported(Map<String, Object> serviceProperties) {
+ long endTime = System.currentTimeMillis() + maxIntentWaitTime;
+ Set<String> requiredIntents = IntentUtils.getRequestedIntents(serviceProperties);
+ List<String> unsupportedIntents = new ArrayList<String>();
+ do {
+ unsupportedIntents.clear();
+ for (String ri : requiredIntents) {
+ if (!intentMap.containsKey(ri)) {
+ unsupportedIntents.add(ri);
+ }
+ }
+ long remainingSeconds = (endTime - System.currentTimeMillis()) / 1000;
+ if (!unsupportedIntents.isEmpty() && remainingSeconds > 0) {
+ LOG.debug("Waiting for custom intents " + unsupportedIntents + " timeout in " + remainingSeconds);
+ try {
+ synchronized (intentMap) {
+ intentMap.wait(1000);
+ }
+ } catch (InterruptedException e) {
+ LOG.warn(e.getMessage(), e);
+ }
+ }
+ } while (!unsupportedIntents.isEmpty() && System.currentTimeMillis() < endTime);
+
+ if (!unsupportedIntents.isEmpty()) {
+ throw new RuntimeException("service cannot be exported because the following "
+ + "intents are not supported by this RSA: " + unsupportedIntents);
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentMap.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentMap.java
new file mode 100644
index 0000000..5ed4ef6
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentMap.java
@@ -0,0 +1,62 @@
+/**
+ * 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.qos;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Maps intent names to intent objects
+ * An intent object can be a Feature, a BindingConfiguration or a String
+ *
+ * Also supports a default intent map. Custom intents can override the defaults
+ */
+public class IntentMap extends ConcurrentHashMap<String, Object> {
+
+ private static final long serialVersionUID = 2606460607920520767L;
+ private Map<String, Object> defaultMap;
+
+ public IntentMap() {
+ this(new HashMap<String, Object>());
+ }
+
+ public IntentMap(Map<String, Object> defaultMap) {
+ this.defaultMap = defaultMap;
+ putAll(defaultMap);
+ }
+
+ @Override
+ public Object put(String key, Object value) {
+ Object result = super.put(key, value);
+ synchronized (this) {
+ notifyAll();
+ }
+ return result;
+ }
+
+ @Override
+ public Object remove(Object key) {
+ Object old = super.remove(key);
+ if (defaultMap.containsKey(key)) {
+ put((String)key, defaultMap.get(key));
+ }
+ return old;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentTracker.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentTracker.java
new file mode 100644
index 0000000..4cae464
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentTracker.java
@@ -0,0 +1,60 @@
+/**
+ * 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.qos;
+
+import org.apache.cxf.dosgi.dsw.Constants;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class IntentTracker extends ServiceTracker {
+
+ private final IntentMap intentMap;
+
+ public IntentTracker(BundleContext context, IntentMap intentMap) {
+ super(context, getFilter(context), null);
+ this.intentMap = intentMap;
+ }
+
+ static Filter getFilter(BundleContext context) {
+ try {
+ return context.createFilter("(" + Constants.INTENT_NAME_PROP + "=*)");
+ } catch (InvalidSyntaxException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public Object addingService(ServiceReference reference) {
+ String intentName = (String) reference.getProperty(Constants.INTENT_NAME_PROP);
+ Object intent = super.addingService(reference);
+ IntentManagerImpl.LOG.info("Adding custom intent " + intentName);
+ intentMap.put(intentName, intent);
+ return intent;
+ }
+
+ @Override
+ public void removedService(ServiceReference reference, Object service) {
+ String intentName = (String) reference.getProperty(Constants.INTENT_NAME_PROP);
+ intentMap.remove(intentName);
+ super.removedService(reference, service);
+ }
+}
\ No newline at end of file
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentUnsatisfiedException.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentUnsatisfiedException.java
new file mode 100644
index 0000000..a1b61fa
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentUnsatisfiedException.java
@@ -0,0 +1,35 @@
+/**
+ * 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.qos;
+
+public class IntentUnsatisfiedException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String intent;
+
+ public IntentUnsatisfiedException(String intent) {
+ super(intent);
+ this.intent = intent;
+ }
+
+ public String getIntent() {
+ return intent;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentUtils.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentUtils.java
new file mode 100644
index 0000000..407b2c3
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/qos/IntentUtils.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.cxf.dosgi.dsw.qos;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cxf.dosgi.dsw.Constants;
+import org.apache.cxf.dosgi.dsw.util.OsgiUtils;
+import org.apache.cxf.dosgi.dsw.util.Utils;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+public final class IntentUtils {
+
+ private IntentUtils() {
+ // never constructed
+ }
+
+ public static String[] getIntentsImplementedByTheService(Map<String, Object> serviceProperties) {
+ // Get the Intents that are implemented by the service
+ return Utils.normalizeStringPlus(serviceProperties.get(RemoteConstants.SERVICE_INTENTS));
+ }
+
+ public static String[] mergeArrays(String[] a1, String[] a2) {
+ if (a1 == null) {
+ return a2;
+ }
+ if (a2 == null) {
+ return a1;
+ }
+
+ List<String> list = new ArrayList<String>(a1.length + a2.length);
+ Collections.addAll(list, a1);
+ for (String s : a2) {
+ if (!list.contains(s)) {
+ list.add(s);
+ }
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+
+ public static Set<String> getRequestedIntents(Map<String, Object> sd) {
+ Collection<String> intents = OsgiUtils.getMultiValueProperty(sd.get(RemoteConstants.SERVICE_EXPORTED_INTENTS));
+ Collection<String> intents2
+ = OsgiUtils.getMultiValueProperty(sd.get(RemoteConstants.SERVICE_EXPORTED_INTENTS_EXTRA));
+ @SuppressWarnings("deprecation")
+ Collection<String> oldIntents = OsgiUtils.getMultiValueProperty(sd.get(Constants.EXPORTED_INTENTS_OLD));
+ Set<String> allIntents = new HashSet<String>();
+ if (intents != null) {
+ allIntents.addAll(parseIntents(intents));
+ }
+ if (intents2 != null) {
+ allIntents.addAll(parseIntents(intents2));
+ }
+ if (oldIntents != null) {
+ allIntents.addAll(parseIntents(oldIntents));
+ }
+
+ return allIntents;
+ }
+
+ private static Collection<String> parseIntents(Collection<String> intents) {
+ List<String> parsed = new ArrayList<String>();
+ for (String intent : intents) {
+ parsed.addAll(Arrays.asList(intent.split("[ ]")));
+ }
+ return parsed;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactory.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactory.java
new file mode 100644
index 0000000..b77950a
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/ClientServiceFactory.java
@@ -0,0 +1,106 @@
+/**
+ * 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.Arrays;
+import java.util.List;
+
+import org.apache.cxf.dosgi.dsw.handlers.ConfigurationTypeHandler;
+import org.apache.cxf.dosgi.dsw.qos.IntentUnsatisfiedException;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ClientServiceFactory implements ServiceFactory {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ClientServiceFactory.class);
+
+ private BundleContext dswContext;
+ private Class<?> iClass;
+ private EndpointDescription endpoint;
+ private ConfigurationTypeHandler handler;
+ private ImportRegistrationImpl importRegistration;
+
+ private boolean closeable;
+ private int serviceCounter;
+
+ public ClientServiceFactory(BundleContext dswContext, Class<?> iClass, EndpointDescription endpoint,
+ ConfigurationTypeHandler handler, ImportRegistrationImpl ir) {
+ this.dswContext = dswContext;
+ this.iClass = iClass;
+ this.endpoint = endpoint;
+ this.handler = handler;
+ this.importRegistration = ir;
+ }
+
+ public Object getService(final Bundle requestingBundle, final ServiceRegistration sreg) {
+ List<String> interfaces = endpoint.getInterfaces();
+ String interfaceName = interfaces == null || interfaces.isEmpty() ? null : interfaces.get(0);
+ LOG.debug("getService() from serviceFactory for {}", interfaceName);
+ try {
+ Object proxy = AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ return handler.createProxy(sreg.getReference(), dswContext,
+ requestingBundle.getBundleContext(), iClass, endpoint);
+ }
+ });
+
+ synchronized (this) {
+ serviceCounter++;
+ }
+ return proxy;
+ } catch (IntentUnsatisfiedException iue) {
+ LOG.info("Did not create proxy for {} because intent {} could not be satisfied",
+ interfaceName, iue.getIntent());
+ } catch (Exception e) {
+ LOG.warn("Problem creating a remote proxy for {}", interfaceName, 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();
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/EventAdminHelper.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/EventAdminHelper.java
new file mode 100644
index 0000000..133d3c7
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/EventAdminHelper.java
@@ -0,0 +1,144 @@
+/**
+ * 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;
+
+import static org.apache.cxf.dosgi.dsw.util.Utils.setIfNotNull;
+
+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);
+ }
+
+ 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 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;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/EventProducer.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/EventProducer.java
new file mode 100644
index 0000000..74ed7e2
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/EventProducer.java
@@ -0,0 +1,111 @@
+/**
+ * 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");
+ }
+ }
+
+ 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);
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportReferenceImpl.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportReferenceImpl.java
new file mode 100644
index 0000000..fcfc289
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportReferenceImpl.java
@@ -0,0 +1,76 @@
+/**
+ * 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;
+
+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;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportRegistrationImpl.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportRegistrationImpl.java
new file mode 100644
index 0000000..2a9757b
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/ExportRegistrationImpl.java
@@ -0,0 +1,144 @@
+/**
+ * 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.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cxf.endpoint.Server;
+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;
+
+public class ExportRegistrationImpl implements ExportRegistration {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ExportRegistrationImpl.class);
+
+ private final RemoteServiceAdminCore rsaCore;
+ private final ExportReferenceImpl exportReference;
+ private final Server server;
+ private final Throwable exception;
+
+ private final ExportRegistrationImpl parent;
+ private int instanceCount;
+ private volatile boolean closed;
+
+ private ExportRegistrationImpl(ExportRegistrationImpl parent, RemoteServiceAdminCore rsaCore,
+ ExportReferenceImpl exportReference, Server 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, EndpointDescription endpoint,
+ RemoteServiceAdminCore rsaCore, Server server) {
+ this(null, rsaCore, new ExportReferenceImpl(sref, endpoint), server, null);
+ }
+
+ // create a new (parent) instance which failed to be exported with the given exception
+ public ExportRegistrationImpl(ServiceReference sref, EndpointDescription endpoint,
+ RemoteServiceAdminCore rsaCore, Throwable exception) {
+ this(null, rsaCore, new ExportReferenceImpl(sref, endpoint), null, exception);
+ }
+
+ private void ensureParent() {
+ if (parent != this) {
+ throw new IllegalStateException("this method may only be called on the parent");
+ }
+ }
+
+ public ExportReference getExportReference() {
+ 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) {
+ server.destroy();
+ }
+ }
+ }
+ }
+
+ @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;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImpl.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImpl.java
new file mode 100644
index 0000000..0edde9f
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImpl.java
@@ -0,0 +1,229 @@
+/**
+ * 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;
+
+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;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCore.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCore.java
new file mode 100644
index 0000000..e9bafa5
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCore.java
@@ -0,0 +1,515 @@
+/**
+ * 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.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cxf.dosgi.dsw.handlers.ConfigTypeHandlerFactory;
+import org.apache.cxf.dosgi.dsw.handlers.ConfigurationTypeHandler;
+import org.apache.cxf.dosgi.dsw.handlers.ExportResult;
+import org.apache.cxf.dosgi.dsw.util.ClassUtils;
+import org.apache.cxf.dosgi.dsw.util.OsgiUtils;
+import org.apache.cxf.dosgi.dsw.util.Utils;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.ExportReference;
+import org.osgi.service.remoteserviceadmin.ExportRegistration;
+import org.osgi.service.remoteserviceadmin.ImportReference;
+import org.osgi.service.remoteserviceadmin.ImportRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RemoteServiceAdminCore implements RemoteServiceAdmin {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RemoteServiceAdminCore.class);
+
+ private final Map<Map<String, Object>, Collection<ExportRegistration>> exportedServices
+ = new LinkedHashMap<Map<String, Object>, Collection<ExportRegistration>>();
+ private final Map<EndpointDescription, Collection<ImportRegistrationImpl>> importedServices
+ = new LinkedHashMap<EndpointDescription, Collection<ImportRegistrationImpl>>();
+
+ // Is stored in exportedServices while the export is in progress as a marker
+ private final List<ExportRegistration> exportInProgress = Collections.emptyList();
+
+ private final BundleContext bctx;
+ private final EventProducer eventProducer;
+ private final ConfigTypeHandlerFactory configTypeHandlerFactory;
+ private final ServiceListener exportedServiceListener;
+
+ public RemoteServiceAdminCore(BundleContext bc, ConfigTypeHandlerFactory configTypeHandlerFactory) {
+ this.bctx = bc;
+ this.eventProducer = new EventProducer(bctx);
+ this.configTypeHandlerFactory = configTypeHandlerFactory;
+ // listen for exported services being unregistered so we can close the export
+ this.exportedServiceListener = new ServiceListener() {
+ public void serviceChanged(ServiceEvent event) {
+ if (event.getType() == ServiceEvent.UNREGISTERING) {
+ removeServiceExports(event.getServiceReference());
+ }
+ }
+ };
+ try {
+ String filter = "(" + RemoteConstants.SERVICE_EXPORTED_INTERFACES + "=*)";
+ bc.addServiceListener(exportedServiceListener, filter);
+ } catch (InvalidSyntaxException ise) {
+ throw new RuntimeException(ise); // can never happen
+ }
+ }
+
+ @Override
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ public List<ExportRegistration> exportService(ServiceReference serviceReference, Map additionalProperties)
+ throws IllegalArgumentException, UnsupportedOperationException {
+ Map<String, Object> serviceProperties = OsgiUtils.getProperties(serviceReference);
+ if (additionalProperties != null) {
+ OsgiUtils.overlayProperties(serviceProperties, additionalProperties);
+ }
+ Map<String, Object> key = makeKey(serviceProperties);
+
+ List<String> interfaces = getInterfaces(serviceProperties);
+
+ if (isCreatedByThisRSA(serviceReference)) {
+ LOG.debug("Skipping export of this service as we created it ourselves as a proxy {}", interfaces);
+ // TODO: publish error event? Not sure
+ return Collections.emptyList();
+ }
+
+ synchronized (exportedServices) {
+ // check if it is already exported...
+ Collection<ExportRegistration> existingRegs = exportedServices.get(key);
+
+ // if the export is already in progress, wait for it to be complete
+ while (existingRegs == exportInProgress) {
+ try {
+ exportedServices.wait();
+ existingRegs = exportedServices.get(key);
+ } catch (InterruptedException ie) {
+ LOG.debug("interrupted while waiting for export in progress");
+ return Collections.emptyList();
+ }
+ }
+
+ // if the export is complete, return a copy of existing export
+ if (existingRegs != null) {
+ LOG.debug("already exported this service. Returning existing exportRegs {} ", interfaces);
+ return copyExportRegistration(existingRegs);
+ }
+
+ // mark export as being in progress
+ exportedServices.put(key, exportInProgress);
+ }
+
+ try {
+ // do the export
+ List<ExportRegistration> exportRegs = exportInterfaces(interfaces, serviceReference, serviceProperties);
+ if (!exportRegs.isEmpty()) {
+ // enlist initial export registrations in global list of exportRegistrations
+ synchronized (exportedServices) {
+ exportedServices.put(key, new ArrayList<ExportRegistration>(exportRegs));
+ }
+ eventProducer.publishNotification(exportRegs);
+ }
+ return exportRegs;
+ } finally {
+ synchronized (exportedServices) {
+ if (exportedServices.get(key) == exportInProgress) {
+ exportedServices.remove(key);
+ }
+ exportedServices.notifyAll(); // in any case, always notify waiting threads
+ }
+ }
+ }
+
+ private List<ExportRegistration> exportInterfaces(List<String> interfaces,
+ ServiceReference serviceReference, Map<String, Object> serviceProperties) {
+ LOG.info("interfaces selected for export: " + interfaces);
+ ConfigurationTypeHandler handler;
+ try {
+ handler = configTypeHandlerFactory.getHandler(bctx, serviceProperties);
+ } catch (RuntimeException e) {
+ LOG.error(e.getMessage(), e);
+ return Collections.emptyList();
+ }
+ List<ExportRegistration> exportRegs = new ArrayList<ExportRegistration>(1);
+ Object service = bctx.getService(serviceReference);
+ Bundle bundle = serviceReference.getBundle();
+
+ // if service has been unregistered in the meantime
+ if (service == null || bundle == null) {
+ LOG.info("service has been unregistered, aborting export");
+ return exportRegs;
+ }
+
+ for (String iface : interfaces) {
+ LOG.info("creating server for interface " + iface);
+ // this is an extra sanity check, but do we really need it now?
+ Class<?> interfaceClass = ClassUtils.getInterfaceClass(service, iface);
+ if (interfaceClass != null) {
+ ExportResult exportResult = handler.createServer(serviceReference, bctx, bundle.getBundleContext(),
+ serviceProperties, interfaceClass, service);
+ EndpointDescription endpoint = new EndpointDescription(exportResult.getEndpointProps());
+ ExportRegistrationImpl exportRegistration;
+ if (exportResult.getException() == null) {
+ LOG.info("created server for interface " + iface);
+ exportRegistration = new ExportRegistrationImpl(serviceReference, endpoint, this,
+ exportResult.getServer());
+ } else {
+ LOG.error("failed to create server for interface " + iface, exportResult.getException());
+ exportRegistration = new ExportRegistrationImpl(serviceReference, endpoint, this,
+ exportResult.getException());
+ }
+ exportRegs.add(exportRegistration);
+ }
+ }
+ return exportRegs;
+ }
+
+ /**
+ * Determines which interfaces should be exported.
+ *
+ * @param serviceProperties the exported service properties
+ * @return the interfaces to be exported
+ * @throws IllegalArgumentException if the service parameters are invalid
+ * @see RemoteServiceAdmin#exportService
+ * @see org.osgi.framework.Constants#OBJECTCLASS
+ * @see RemoteConstants#SERVICE_EXPORTED_INTERFACES
+ */
+ private List<String> getInterfaces(Map<String, Object> serviceProperties) {
+ String[] providedInterfaces = (String[])serviceProperties.get(org.osgi.framework.Constants.OBJECTCLASS);
+ if (providedInterfaces == null || providedInterfaces.length == 0) {
+ throw new IllegalArgumentException("service is missing the objectClass property");
+ }
+
+ String[] allowedInterfaces
+ = Utils.normalizeStringPlus(serviceProperties.get(RemoteConstants.SERVICE_EXPORTED_INTERFACES));
+ if (allowedInterfaces == null || allowedInterfaces.length == 0) {
+ throw new IllegalArgumentException("service is missing the service.exported.interfaces property");
+ }
+
+ List<String> interfaces = new ArrayList<String>(1);
+ if (allowedInterfaces.length == 1 && "*".equals(allowedInterfaces[0])) {
+ // FIXME: according to the spec, this should only return the interfaces, and not
+ // non-interface classes (which are valid OBJECTCLASS values, even if discouraged)
+ Collections.addAll(interfaces, providedInterfaces);
+ } else {
+ List<String> providedList = Arrays.asList(providedInterfaces);
+ List<String> allowedList = Arrays.asList(allowedInterfaces);
+ if (!providedList.containsAll(allowedList)) {
+ throw new IllegalArgumentException(String.format(
+ "exported interfaces %s must be a subset of the service's registered types %s",
+ allowedList, providedList));
+ }
+
+ Collections.addAll(interfaces, allowedInterfaces);
+ }
+ return interfaces;
+ }
+
+ /**
+ * Converts the given properties map into one that can be used as a map key itself.
+ * For example, if a value is an array, it is converted into a list so that the
+ * equals method will compare it properly.
+ *
+ * @param properties a properties map
+ * @return a map that represents the given map, but can be safely used as a map key itself
+ */
+ private Map<String, Object> makeKey(Map<String, Object> properties) {
+ // FIXME: we should also make logically equal values actually compare as equal
+ // (e.g. String+ values should be normalized)
+ Map<String, Object> converted = new HashMap<String, Object>(properties.size());
+ for (Map.Entry<String, Object> entry : properties.entrySet()) {
+ Object val = entry.getValue();
+ // convert arrays into lists so that they can be compared via equals()
+ if (val instanceof Object[]) {
+ val = Arrays.asList((Object[])val);
+ }
+ converted.put(entry.getKey(), val);
+ }
+ return converted;
+ }
+
+ private List<ExportRegistration> copyExportRegistration(Collection<ExportRegistration> regs) {
+ Set<EndpointDescription> copiedEndpoints = new HashSet<EndpointDescription>();
+
+ // create a new list with copies of the exportRegistrations
+ List<ExportRegistration> copy = new ArrayList<ExportRegistration>(regs.size());
+ for (ExportRegistration exportRegistration : regs) {
+ if (exportRegistration instanceof ExportRegistrationImpl) {
+ ExportRegistrationImpl exportRegistrationImpl = (ExportRegistrationImpl) exportRegistration;
+ EndpointDescription epd = exportRegistration.getExportReference().getExportedEndpoint();
+ // create one copy for each distinct endpoint description
+ if (!copiedEndpoints.contains(epd)) {
+ copiedEndpoints.add(epd);
+ copy.add(new ExportRegistrationImpl(exportRegistrationImpl));
+ }
+ }
+ }
+
+ regs.addAll(copy);
+
+ eventProducer.publishNotification(copy);
+ return copy;
+ }
+
+ private boolean isCreatedByThisRSA(ServiceReference sref) {
+ return bctx.getBundle().equals(sref.getBundle()); // sref bundle can be null
+ }
+
+ @Override
+ public Collection<ExportReference> getExportedServices() {
+ synchronized (exportedServices) {
+ List<ExportReference> ers = new ArrayList<ExportReference>();
+ for (Collection<ExportRegistration> exportRegistrations : exportedServices.values()) {
+ for (ExportRegistration er : exportRegistrations) {
+ ers.add(new ExportReferenceImpl(er.getExportReference()));
+ }
+ }
+ return Collections.unmodifiableCollection(ers);
+ }
+ }
+
+ @Override
+ public Collection<ImportReference> getImportedEndpoints() {
+ synchronized (importedServices) {
+ List<ImportReference> irs = new ArrayList<ImportReference>();
+ for (Collection<ImportRegistrationImpl> irl : importedServices.values()) {
+ for (ImportRegistrationImpl impl : irl) {
+ irs.add(impl.getImportReference());
+ }
+ }
+ return Collections.unmodifiableCollection(irs);
+ }
+ }
+
+ /**
+ * Importing form here...
+ */
+ @Override
+ public ImportRegistration importService(EndpointDescription endpoint) {
+ LOG.debug("importService() Endpoint: {}", endpoint.getProperties());
+
+ synchronized (importedServices) {
+ Collection<ImportRegistrationImpl> imRegs = importedServices.get(endpoint);
+ if (imRegs != null && !imRegs.isEmpty()) {
+ LOG.debug("creating copy of existing import registrations");
+ ImportRegistrationImpl irParent = imRegs.iterator().next();
+ ImportRegistrationImpl ir = new ImportRegistrationImpl(irParent);
+ imRegs.add(ir);
+ eventProducer.publishNotification(ir);
+ return ir;
+ }
+
+ ConfigurationTypeHandler handler;
+ try {
+ handler = configTypeHandlerFactory.getHandler(bctx, endpoint);
+ } catch (RuntimeException e) {
+ LOG.error("no handler found: " + e.getMessage(), e);
+ return null;
+ }
+
+ LOG.debug("Handler: {}", handler);
+
+ // TODO: somehow select the interfaces that should be imported ---> job of the TopologyManager?
+ List<String> matchingInterfaces = endpoint.getInterfaces();
+
+ LOG.info("Matching Interfaces for import: " + matchingInterfaces);
+
+ if (matchingInterfaces.size() == 1) {
+ LOG.info("Proxifying interface: " + matchingInterfaces.get(0));
+
+ ImportRegistrationImpl imReg = new ImportRegistrationImpl(endpoint, this);
+
+ proxifyMatchingInterface(matchingInterfaces.get(0), imReg, handler, bctx);
+ if (imRegs == null) {
+ imRegs = new ArrayList<ImportRegistrationImpl>();
+ importedServices.put(endpoint, imRegs);
+ }
+ imRegs.add(imReg);
+ eventProducer.publishNotification(imReg);
+ return imReg;
+ }
+ return null;
+ }
+ }
+
+ protected void proxifyMatchingInterface(String interfaceName, ImportRegistrationImpl imReg,
+ ConfigurationTypeHandler handler, BundleContext requestingContext) {
+ try {
+ // MARC: relies on dynamic imports?
+ Class<?> iClass = bctx.getBundle().loadClass(interfaceName);
+ if (iClass == null) {
+ throw new ClassNotFoundException("Cannot load interface class");
+ }
+
+ BundleContext actualContext = bctx;
+ Class<?> actualClass = requestingContext.getBundle().loadClass(interfaceName);
+ if (actualClass != iClass) {
+ LOG.info("Class " + interfaceName + " loaded by DSW's bundle context is not "
+ + "equal to the one loaded by the requesting bundle context, "
+ + "DSW will use the requesting bundle context to register a proxy service");
+ iClass = actualClass;
+ actualContext = requestingContext;
+ }
+
+ EndpointDescription endpoint = imReg.getImportedEndpointDescription();
+ /* TODO: add additional local params... */
+ Dictionary<String, Object> serviceProps = new Hashtable<String, Object>(endpoint.getProperties());
+ serviceProps.put(RemoteConstants.SERVICE_IMPORTED, true);
+ serviceProps.remove(RemoteConstants.SERVICE_EXPORTED_INTERFACES);
+
+ ClientServiceFactory csf = new ClientServiceFactory(actualContext, iClass, endpoint, handler, imReg);
+ imReg.setClientServiceFactory(csf);
+ ServiceRegistration proxyReg = actualContext.registerService(interfaceName, csf, serviceProps);
+ imReg.setImportedServiceRegistration(proxyReg);
+ } catch (Exception ex) {
+ // Only logging at debug level as this might be written to the log at the TopologyManager
+ LOG.debug("Can not proxy service with interface " + interfaceName + ": " + ex.getMessage(), ex);
+ imReg.setException(ex);
+ }
+ }
+
+ /**
+ * Removes and closes all exports for the given service.
+ * This is called when the service is unregistered.
+ *
+ * @param sref the service whose exports should be removed and closed
+ */
+ protected void removeServiceExports(ServiceReference sref) {
+ List<ExportRegistration> regs = new ArrayList<ExportRegistration>(1);
+ synchronized (exportedServices) {
+ for (Iterator<Collection<ExportRegistration>> it = exportedServices.values().iterator(); it.hasNext();) {
+ Collection<ExportRegistration> value = it.next();
+ for (Iterator<ExportRegistration> it2 = value.iterator(); it2.hasNext();) {
+ ExportRegistration er = it2.next();
+ if (er.getExportReference().getExportedService().equals(sref)) {
+ regs.add(er);
+ }
+ }
+ }
+ // do this outside of iteration to avoid concurrent modification
+ for (ExportRegistration er : regs) {
+ LOG.debug("closing export for service {}", sref);
+ er.close();
+ }
+ }
+
+ }
+
+ /**
+ * Removes the provided Export Registration from the internal management structures.
+ * This is called from the ExportRegistration itself when it is closed (so should
+ * not attempt to close it again here).
+ *
+ * @param eri the export registration to remove
+ */
+ protected void removeExportRegistration(ExportRegistrationImpl eri) {
+ synchronized (exportedServices) {
+ for (Iterator<Collection<ExportRegistration>> it = exportedServices.values().iterator(); it.hasNext();) {
+ Collection<ExportRegistration> value = it.next();
+ for (Iterator<ExportRegistration> it2 = value.iterator(); it2.hasNext();) {
+ ExportRegistration er = it2.next();
+ if (er.equals(eri)) {
+ eventProducer.notifyRemoval(eri);
+ it2.remove();
+ if (value.isEmpty()) {
+ it.remove();
+ }
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ // remove all export registrations associated with the given bundle
+ protected void removeExportRegistrations(Bundle exportingBundle) {
+ List<ExportRegistration> bundleExports = getExportsForBundle(exportingBundle);
+ for (ExportRegistration export : bundleExports) {
+ export.close();
+ }
+ }
+
+ // remove all import registrations
+ protected void removeImportRegistrations() {
+ Collection<ImportRegistrationImpl> copy = new ArrayList<ImportRegistrationImpl>();
+ synchronized (importedServices) {
+ for (Collection<ImportRegistrationImpl> irs : importedServices.values()) {
+ copy.addAll(irs);
+ }
+ }
+ for (ImportRegistrationImpl ir : copy) {
+ removeImportRegistration(ir);
+ }
+ }
+
+ private List<ExportRegistration> getExportsForBundle(Bundle exportingBundle) {
+ synchronized (exportedServices) {
+ List<ExportRegistration> bundleRegs = new ArrayList<ExportRegistration>();
+ for (Collection<ExportRegistration> regs : exportedServices.values()) {
+ if (!regs.isEmpty()) {
+ Bundle regBundle = regs.iterator().next().getExportReference().getExportedService().getBundle();
+ if (exportingBundle.equals(regBundle)) {
+ bundleRegs.addAll(regs);
+ }
+ }
+ }
+ return bundleRegs;
+ }
+ }
+
+ protected void removeImportRegistration(ImportRegistrationImpl iri) {
+ synchronized (importedServices) {
+ LOG.debug("Removing importRegistration {}", iri);
+
+ Collection<ImportRegistrationImpl> imRegs = importedServices.get(iri.getImportedEndpointAlways());
+ if (imRegs != null && imRegs.contains(iri)) {
+ imRegs.remove(iri);
+ eventProducer.notifyRemoval(iri);
+ }
+ if (imRegs == null || imRegs.isEmpty()) {
+ importedServices.remove(iri.getImportedEndpointAlways());
+ }
+ }
+ }
+
+ public void close() {
+ removeImportRegistrations();
+ bctx.removeServiceListener(exportedServiceListener);
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminInstance.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminInstance.java
new file mode 100644
index 0000000..286ce54
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminInstance.java
@@ -0,0 +1,93 @@
+/**
+ * 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.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.dsw.util.OsgiUtils;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.EndpointPermission;
+import org.osgi.service.remoteserviceadmin.ExportReference;
+import org.osgi.service.remoteserviceadmin.ExportRegistration;
+import org.osgi.service.remoteserviceadmin.ImportReference;
+import org.osgi.service.remoteserviceadmin.ImportRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+
+import static org.apache.cxf.dosgi.dsw.util.OsgiUtils.checkPermission;
+
+public class RemoteServiceAdminInstance implements RemoteServiceAdmin {
+
+ private final BundleContext bctx;
+ private final RemoteServiceAdminCore rsaCore;
+
+ private boolean closed;
+
+ public RemoteServiceAdminInstance(BundleContext bc, RemoteServiceAdminCore core) {
+ bctx = bc;
+ rsaCore = core;
+ }
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ public List<ExportRegistration> exportService(final ServiceReference ref, final Map properties) {
+ checkPermission(new EndpointPermission("*", EndpointPermission.EXPORT));
+ return AccessController.doPrivileged(new PrivilegedAction<List<ExportRegistration>>() {
+ public List<ExportRegistration> run() {
+ return closed ? Collections.<ExportRegistration>emptyList() : rsaCore.exportService(ref, properties);
+ }
+ });
+ }
+
+ @Override
+ public Collection<ExportReference> getExportedServices() {
+ checkPermission(new EndpointPermission("*", EndpointPermission.READ));
+ return closed ? null : rsaCore.getExportedServices();
+ }
+
+ @Override
+ public Collection<ImportReference> getImportedEndpoints() {
+ checkPermission(new EndpointPermission("*", EndpointPermission.READ));
+ return closed ? null : rsaCore.getImportedEndpoints();
+ }
+
+ @Override
+ public ImportRegistration importService(final EndpointDescription endpoint) {
+ checkPermission(new EndpointPermission(endpoint, OsgiUtils.getUUID(bctx), EndpointPermission.IMPORT));
+ return AccessController.doPrivileged(new PrivilegedAction<ImportRegistration>() {
+ public ImportRegistration run() {
+ return closed ? null : rsaCore.importService(endpoint);
+ }
+ });
+ }
+
+ public void close(boolean closeAll) {
+ closed = true;
+ rsaCore.removeExportRegistrations(bctx.getBundle());
+ if (closeAll) {
+ rsaCore.close();
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceadminFactory.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceadminFactory.java
new file mode 100644
index 0000000..11f91c9
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceadminFactory.java
@@ -0,0 +1,49 @@
+/**
+ * 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.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RemoteServiceadminFactory implements ServiceFactory {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RemoteServiceadminFactory.class);
+
+ private final RemoteServiceAdminCore rsaCore;
+ private int instances;
+
+ public RemoteServiceadminFactory(RemoteServiceAdminCore rsaCore) {
+ this.rsaCore = rsaCore;
+ }
+
+ public synchronized Object getService(Bundle b, ServiceRegistration sreg) {
+ LOG.debug("new RemoteServiceAdmin ServiceInstance created for Bundle {}", b.getSymbolicName());
+ instances++;
+ return new RemoteServiceAdminInstance(b.getBundleContext(), rsaCore);
+ }
+
+ public synchronized void ungetService(Bundle b, ServiceRegistration sreg, Object serviceObject) {
+ LOG.debug("RemoteServiceAdmin ServiceInstance removed for Bundle {}", b.getSymbolicName());
+ instances--;
+ ((RemoteServiceAdminInstance)serviceObject).close(instances == 0);
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/ClassUtils.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/ClassUtils.java
new file mode 100644
index 0000000..d51527b
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/ClassUtils.java
@@ -0,0 +1,142 @@
+/**
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.helpers.CastUtils;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class ClassUtils {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ClassUtils.class);
+
+ private ClassUtils() {
+ }
+
+ public static Class<?> getInterfaceClass(Object service, String interfaceName) {
+ return getInterfaceClass(service.getClass(), interfaceName);
+ }
+
+ private static Class<?> getInterfaceClass(Class<?> serviceClass, String interfaceName) {
+ for (Class<?> iClass : serviceClass.getInterfaces()) {
+ if (iClass.getName().equals(interfaceName)) {
+ return iClass;
+ }
+ Class<?> intf = getInterfaceClass(iClass, interfaceName);
+ if (intf != null) {
+ return intf;
+ }
+ }
+
+ if (serviceClass.getName().equals(interfaceName)) {
+ return serviceClass;
+ }
+
+ Class<?> interfaceOnProxiedClass = getInterfaceClassOnSuperClasses(serviceClass, interfaceName);
+ if (interfaceOnProxiedClass != null) {
+ return interfaceOnProxiedClass;
+ }
+
+ return null;
+ }
+
+ /**
+ * This method tries to deal specifically with classes that might have been proxied
+ * eg. CGLIB proxies of which there might be a chain of proxies as different osgi frameworks
+ * might be proxying the original service class that has been registered and then proxying the proxy.
+ *
+ * @param serviceClass
+ * @param interfaceName
+ * @return
+ */
+ private static Class<?> getInterfaceClassOnSuperClasses(Class<?> serviceClass, String interfaceName) {
+ Class<?> superClass = serviceClass.getSuperclass();
+ if (superClass != null) {
+ for (Class<?> iClass : superClass.getInterfaces()) {
+ if (iClass.getName().equals(interfaceName)) {
+ return iClass;
+ }
+ Class<?> intf = getInterfaceClass(iClass, interfaceName);
+ if (intf != null) {
+ return intf;
+ }
+ }
+ Class<?> foundOnSuperclass = getInterfaceClassOnSuperClasses(superClass, interfaceName);
+ if (foundOnSuperclass != null) {
+ return foundOnSuperclass;
+ }
+ }
+ return null;
+ }
+
+ public static List<Object> loadProviderClasses(BundleContext callingContext,
+ Map<String, Object> sd, String propName) {
+ Object serviceProviders = sd.get(propName);
+ if (serviceProviders != null) {
+ if (serviceProviders.getClass().isArray()) {
+ if (serviceProviders.getClass().getComponentType() == String.class) {
+ return loadProviders(callingContext, (String[])serviceProviders);
+ } else {
+ return Arrays.asList((Object[])serviceProviders);
+ }
+ } else if (serviceProviders.getClass() == String.class) {
+ String[] classNames = serviceProviders.toString().split(",");
+ return loadProviders(callingContext, classNames);
+ } else if (serviceProviders instanceof List) {
+ List<Object> list = CastUtils.cast((List<?>)serviceProviders);
+ if (!list.isEmpty()) {
+ List<Object> providers;
+ if (list.get(0).getClass() == String.class) {
+ providers = loadProviders(callingContext, list.toArray(new String[]{}));
+ } else {
+ providers = list;
+ }
+ return providers;
+ }
+ } else {
+ return Arrays.asList(serviceProviders);
+ }
+ }
+ return Collections.emptyList();
+
+ }
+
+ private static List<Object> loadProviders(BundleContext callingContext, String[] classNames) {
+ List<Object> providers = new ArrayList<Object>();
+ for (String className : classNames) {
+ try {
+ String realName = className.trim();
+ if (!realName.isEmpty()) {
+ Class<?> pClass = callingContext.getBundle().loadClass(realName);
+ providers.add(pClass.newInstance());
+ }
+ } catch (Exception ex) {
+ LOG.warn("Provider " + className.trim() + " can not be loaded or created " + ex.getMessage(), ex);
+ }
+ }
+ return providers;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/OsgiUtils.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/OsgiUtils.java
new file mode 100644
index 0000000..9f4216d
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/OsgiUtils.java
@@ -0,0 +1,196 @@
+/**
+ * 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.util;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+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.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.EndpointPermission;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class OsgiUtils {
+
+ public static final Logger LOG = LoggerFactory.getLogger(OsgiUtils.class);
+
+ private OsgiUtils() {
+ }
+
+ public static boolean getBooleanProperty(Map<String, Object> sd, String name) {
+ return toBoolean(sd.get(name));
+ }
+
+ public static boolean toBoolean(Object value) {
+ return value instanceof Boolean && (Boolean) value
+ || value instanceof String && Boolean.parseBoolean((String)value);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Collection<String> getMultiValueProperty(Object property) {
+ if (property == null) {
+ return null;
+ } else if (property instanceof Collection) {
+ return (Collection<String>)property;
+ } else if (property instanceof String[]) {
+ return Arrays.asList((String[])property);
+ } else {
+ return Collections.singleton(property.toString());
+ }
+ }
+
+ public static String getProperty(EndpointDescription endpoint, String name) {
+ return getProperty(endpoint.getProperties(), name);
+ }
+
+ public static String getProperty(Map<String, Object> dict, String name) {
+ Object value = dict.get(name);
+ return value instanceof String ? (String) value : null;
+ }
+
+ public static String getFirstNonEmptyStringProperty(Map<String, Object> dict, String ... keys) {
+ for (String key : keys) {
+ String value = getProperty(dict, key);
+ if (value != null) {
+ return value;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 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 paRef = bc.getServiceReference(PackageAdmin.class.getName());
+ if (paRef != null) {
+ PackageAdmin pa = (PackageAdmin)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: {}", 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";
+ }
+
+ public static String getUUID(BundleContext bc) {
+ synchronized ("org.osgi.framework.uuid") {
+ String uuid = bc.getProperty("org.osgi.framework.uuid");
+ if (uuid == null) {
+ uuid = UUID.randomUUID().toString();
+ System.setProperty("org.osgi.framework.uuid", uuid);
+ }
+ return uuid;
+ }
+ }
+
+ public static void overlayProperties(Map<String, Object> serviceProperties,
+ Map<String, Object> additionalProperties) {
+ Map<String, String> keysLowerCase = new HashMap<String, String>();
+ for (String key : serviceProperties.keySet()) {
+ keysLowerCase.put(key.toLowerCase(), key);
+ }
+
+ for (Map.Entry<String, Object> e : additionalProperties.entrySet()) {
+ String key = e.getKey();
+ String lowerKey = key.toLowerCase();
+ if (org.osgi.framework.Constants.SERVICE_ID.toLowerCase().equals(lowerKey)
+ || org.osgi.framework.Constants.OBJECTCLASS.toLowerCase().equals(lowerKey)) {
+ // objectClass and service.id must not be overwritten
+ LOG.info("exportService called with additional properties map that contained illegal key: "
+ + key + ", the key is ignored");
+ } else {
+ String origKey = keysLowerCase.get(lowerKey);
+ if (origKey != null) {
+ LOG.debug("Overwriting property [{}] with value [{}]", origKey, e.getValue());
+ } else {
+ origKey = key;
+ keysLowerCase.put(lowerKey, origKey);
+ }
+ serviceProperties.put(origKey, e.getValue());
+ }
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ public static void checkPermission(EndpointPermission permission) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(permission);
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/Utils.java b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/Utils.java
new file mode 100644
index 0000000..a101d21
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/java/org/apache/cxf/dosgi/dsw/util/Utils.java
@@ -0,0 +1,94 @@
+/**
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class Utils {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Utils.class);
+
+ private Utils() {
+ // never constructed
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static String[] normalizeStringPlus(Object object) {
+ if (object instanceof String) {
+ String s = (String)object;
+ String[] values = s.split(",");
+ List<String> list = new ArrayList<String>();
+ for (String val : values) {
+ String actualValue = val.trim();
+ if (!actualValue.isEmpty()) {
+ list.add(actualValue);
+ }
+ }
+ return list.toArray(new String[list.size()]);
+ }
+
+ if (object instanceof String[]) {
+ return (String[])object;
+ }
+
+ if (object instanceof Collection) {
+ Collection col = (Collection)object;
+ List<String> ar = new ArrayList<String>(col.size());
+ for (Object o : col) {
+ if (o instanceof String) {
+ String s = (String)o;
+ ar.add(s);
+ } else {
+ LOG.warn("stringPlus contained non string element in list! Element was skipped");
+ }
+ }
+ return ar.toArray(new String[ar.size()]);
+ }
+
+ return null;
+ }
+
+ public static <K, V> Map<K, V> toMap(Dictionary<K, V> dict) {
+ Map<K, V> map = new HashMap<K, V>();
+ if (dict == null) {
+ return map;
+ }
+ Enumeration<K> keys = dict.keys();
+ while (keys.hasMoreElements()) {
+ K key = keys.nextElement();
+ map.put(key, dict.get(key));
+ }
+ return map;
+ }
+
+ public static <K, V> void setIfNotNull(Map<K, V> map, K key, V val) {
+ if (val != null) {
+ map.put(key, val);
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/main/resources/service-decoration.xsd b/trunk/dsw/cxf-dsw/src/main/resources/service-decoration.xsd
new file mode 100644
index 0000000..66e8d30
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/main/resources/service-decoration.xsd
@@ -0,0 +1,67 @@
+<?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.
+-->
+<schema targetNamespace="http://cxf.apache.org/xmlns/service-decoration/1.0.0" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://cxf.apache.org/xmlns/service-decoration/1.0.0">
+ <element name="service-decorations" type="tns:ServiceDecorationsType"></element>
+
+ <complexType name="ServiceDecorationsType">
+ <sequence>
+ <element maxOccurs="unbounded" minOccurs="0"
+ ref="tns:service-decoration">
+ </element>
+ </sequence>
+ </complexType>
+
+ <complexType name="ServiceDecorationType">
+ <sequence>
+ <element maxOccurs="unbounded" minOccurs="0"
+ ref="tns:match">
+ </element>
+ </sequence>
+ </complexType>
+
+ <complexType name="MatchType">
+ <sequence>
+ <element maxOccurs="unbounded" minOccurs="0"
+ ref="tns:match-property">
+ </element>
+ <element maxOccurs="unbounded" minOccurs="0"
+ ref="tns:add-property">
+ </element>
+ </sequence>
+ <attribute name="interface" type="string"></attribute>
+ </complexType>
+
+ <complexType name="MatchPropertyType">
+ <attribute name="name" type="string"></attribute>
+ <attribute name="value" type="string"></attribute>
+ </complexType>
+
+ <complexType name="AddPropertyType">
+ <attribute name="name" type="string"></attribute>
+ <attribute name="value" type="string"></attribute>
+ <attribute name="type" type="string"></attribute>
+ </complexType>
+ <element name="service-decoration"
+ type="tns:ServiceDecorationType">
+ </element>
+ <element name="match" type="tns:MatchType"></element>
+ <element name="match-property" type="tns:MatchPropertyType"></element>
+ <element name="add-property" type="tns:AddPropertyType"></element>
+</schema>
\ No newline at end of file
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/ActivatorTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/ActivatorTest.java
new file mode 100644
index 0000000..62007c7
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/ActivatorTest.java
@@ -0,0 +1,63 @@
+/**
+ * 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;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import junit.framework.TestCase;
+
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+
+public class ActivatorTest extends TestCase {
+
+ private BundleContext getMockBundleContext(IMocksControl control) {
+ Bundle b = control.createMock(Bundle.class);
+ Dictionary<String, String> ht = new Hashtable<String, String>();
+ EasyMock.expect(b.getHeaders()).andReturn(ht).anyTimes();
+ BundleContext bc = control.createMock(BundleContext.class);
+
+ EasyMock.expect(b.getBundleContext()).andReturn(bc).anyTimes();
+ EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes();
+ return bc;
+ }
+
+ public void testCreateAndShutdownRemoteServiceAdminService() throws Exception {
+ IMocksControl control = EasyMock.createNiceControl();
+ BundleContext bc = getMockBundleContext(control);
+ Filter filter = control.createMock(Filter.class);
+ EasyMock.expect(bc.createFilter(EasyMock.<String>anyObject())).andReturn(filter);
+ EasyMock.expectLastCall().atLeastOnce();
+ ServiceRegistration sr = control.createMock(ServiceRegistration.class);
+ EasyMock.expect(bc.registerService(EasyMock.eq(RemoteServiceAdmin.class.getName()),
+ EasyMock.anyObject(), (Dictionary<String, String>)EasyMock.anyObject()))
+ .andReturn(sr).atLeastOnce();
+
+ control.replay();
+ Activator a = new Activator();
+ a.start(bc);
+ control.verify();
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/TestUtils.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/TestUtils.java
new file mode 100644
index 0000000..b81bf3e
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/TestUtils.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;
+
+public final class TestUtils {
+
+// private TestUtils() {
+// }
+//
+// public static ServiceEndpointDescription mockServiceDescription(String... interfaceNames) {
+// List<String> iList = new ArrayList<String>();
+// for (String iName : interfaceNames) {
+// iList.add(iName);
+// }
+//
+// ServiceEndpointDescription sd = EasyMock.createNiceMock(ServiceEndpointDescription.class);
+// sd.getProvidedInterfaces();
+// EasyMock.expectLastCall().andReturn(iList);
+// return sd;
+// }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/decorator/DecorationParserTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/decorator/DecorationParserTest.java
new file mode 100644
index 0000000..37c018a
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/decorator/DecorationParserTest.java
@@ -0,0 +1,60 @@
+/**
+ * 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.decorator;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.cxf.xmlns.service_decoration._1_0.AddPropertyType;
+import org.apache.cxf.xmlns.service_decoration._1_0.MatchPropertyType;
+import org.apache.cxf.xmlns.service_decoration._1_0.MatchType;
+import org.apache.cxf.xmlns.service_decoration._1_0.ServiceDecorationType;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class DecorationParserTest {
+
+ @Test
+ public void testGetDecoratorForSD() {
+ URL resource = getClass().getResource("/test-resources/sd.xml");
+ List<ServiceDecorationType> elements = new DecorationParser().getDecorations(resource);
+ assertEquals(1, elements.size());
+ ServiceDecorationType decoration = elements.get(0);
+ assertEquals(1, decoration.getMatch().size());
+ MatchType match = decoration.getMatch().get(0);
+ assertEquals("org.acme.foo.*", match.getInterface());
+ assertEquals(1, match.getMatchProperty().size());
+ MatchPropertyType matchProp = match.getMatchProperty().get(0);
+ assertEquals("test.prop", matchProp.getName());
+ assertEquals("xyz", matchProp.getValue());
+ assertEquals(1, match.getAddProperty().size());
+ AddPropertyType addProp = match.getAddProperty().get(0);
+ assertEquals("test.too", addProp.getName());
+ assertEquals("ahaha", addProp.getValue());
+ assertEquals("java.lang.String", addProp.getType());
+ }
+
+ @Test
+ public void testGetDecorationForNull() {
+ List<ServiceDecorationType> elements = new DecorationParser().getDecorations(null);
+ Assert.assertEquals(0, elements.size());
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/decorator/InterfaceRuleTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/decorator/InterfaceRuleTest.java
new file mode 100644
index 0000000..8b45d28
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/decorator/InterfaceRuleTest.java
@@ -0,0 +1,162 @@
+/**
+ * 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.decorator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+public class InterfaceRuleTest extends TestCase {
+
+ public void testDUMMY() {
+ assertTrue(true);
+ }
+
+ public void testInterfaceRuleGetBundle() {
+ Bundle b = EasyMock.createMock(Bundle.class);
+ EasyMock.replay(b);
+ InterfaceRule ir = new InterfaceRule(b, "org.apache.Foo");
+ assertSame(b, ir.getBundle());
+ }
+
+ public void testInterfaceRule1() {
+ InterfaceRule ir = new InterfaceRule(null, "org.apache.Foo");
+ ir.addProperty("x", "y", String.class.getName());
+
+ final Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"a.b.C", "org.apache.Foo"});
+ ServiceReference sref = mockServiceReference(serviceProps);
+
+ Map<String, Object> m = new HashMap<String, Object>();
+ m.put("a", "b");
+ ir.apply(sref, m);
+ Map<String, Object> expected = new HashMap<String, Object>();
+ expected.put("a", "b");
+ expected.put("x", "y");
+ assertEquals(expected, m);
+ }
+
+ public void testInterfaceRule2() {
+ InterfaceRule ir = new InterfaceRule(null, "org.apache.F(.*)");
+ ir.addPropMatch("boo", "baah");
+ ir.addProperty("x", "1", Integer.class.getName());
+ ir.addProperty("aaa.bbb", "true", Boolean.class.getName());
+
+ final Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put("boo", "baah");
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"a.b.C", "org.apache.Foo"});
+ ServiceReference sref = mockServiceReference(serviceProps);
+
+ Map<String, Object> m = new HashMap<String, Object>();
+ ir.apply(sref, m);
+ Map<String, Object> expected = new HashMap<String, Object>();
+ expected.put("x", 1);
+ expected.put("aaa.bbb", Boolean.TRUE);
+ assertEquals(expected, m);
+ }
+
+ public void testInterfaceRule3() {
+ InterfaceRule ir = new InterfaceRule(null, "org.apache.F(.*)");
+ ir.addProperty("x", "y", String.class.getName());
+
+ final Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put("boo", "baah");
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"org.apache.Boo"});
+ ServiceReference sref = mockServiceReference(serviceProps);
+
+ Map<String, Object> m = new HashMap<String, Object>();
+ ir.apply(sref, m);
+ assertEquals(0, m.size());
+ }
+
+ public void testInterfaceRule4() {
+ InterfaceRule ir = new InterfaceRule(null, "org.apache.F(.*)");
+ ir.addPropMatch("boo", "baah");
+ ir.addProperty("x", "y", String.class.getName());
+
+ final Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"org.apache.Foo"});
+ ServiceReference sref = mockServiceReference(serviceProps);
+
+ Map<String, Object> m = new HashMap<String, Object>();
+ ir.apply(sref, m);
+ assertEquals(0, m.size());
+ }
+
+ public void testInterfaceRule5() {
+ InterfaceRule ir = new InterfaceRule(null, "org.apache.Foo");
+ ir.addPropMatch("test.int", "42");
+ ir.addProperty("x", "1", Long.class.getName());
+
+ final Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put("test.int", 42);
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"org.apache.Foo"});
+ ServiceReference sref = mockServiceReference(serviceProps);
+
+ Map<String, Object> m = new HashMap<String, Object>();
+ m.put("x", "foo");
+ m.put("aaa.bbb", Boolean.TRUE);
+ ir.apply(sref, m);
+ Map<String, Object> expected = new HashMap<String, Object>();
+ expected.put("x", 1L);
+ expected.put("aaa.bbb", Boolean.TRUE);
+ assertEquals(expected, m);
+ }
+
+ public void testInterfaceRule6() {
+ InterfaceRule ir = new InterfaceRule(null, "org.apache.Foo");
+ ir.addPropMatch("test.int", "42");
+ ir.addProperty("x", "1", Long.class.getName());
+
+ final Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put("test.int", 51);
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"org.apache.Foo"});
+ ServiceReference sref = mockServiceReference(serviceProps);
+
+ Map<String, Object> m = new HashMap<String, Object>();
+ m.put("x", "foo");
+ m.put("aaa.bbb", Boolean.TRUE);
+ ir.apply(sref, m);
+ Map<String, Object> expected = new HashMap<String, Object>();
+ expected.put("x", "foo");
+ expected.put("aaa.bbb", Boolean.TRUE);
+ assertEquals(expected, m);
+ }
+
+ private ServiceReference mockServiceReference(final Map<String, Object> serviceProps) {
+ ServiceReference sref = EasyMock.createMock(ServiceReference.class);
+ EasyMock.expect(sref.getProperty((String) EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ return serviceProps.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.expect(sref.getPropertyKeys())
+ .andReturn(serviceProps.keySet().toArray(new String[] {})).anyTimes();
+
+ EasyMock.replay(sref);
+ return sref;
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecoratorBundleListenerTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecoratorBundleListenerTest.java
new file mode 100644
index 0000000..1ca33fc
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecoratorBundleListenerTest.java
@@ -0,0 +1,69 @@
+/**
+ * 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.decorator;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.easymock.EasyMock;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+
+import static org.junit.Assert.assertEquals;
+
+public class ServiceDecoratorBundleListenerTest {
+
+ @Test
+ public void testBundleListener() {
+ BundleContext bc = EasyMock.createMock(BundleContext.class);
+ EasyMock.replay(bc);
+
+ final List<String> called = new ArrayList<String>();
+ ServiceDecoratorImpl serviceDecorator = new ServiceDecoratorImpl() {
+ @Override
+ void addDecorations(Bundle bundle) {
+ called.add("addDecorations");
+ }
+
+ @Override
+ void removeDecorations(Bundle bundle) {
+ called.add("removeDecorations");
+ }
+ };
+
+ Bundle b = EasyMock.createMock(Bundle.class);
+ EasyMock.replay(b);
+
+ ServiceDecoratorBundleListener listener = new ServiceDecoratorBundleListener(serviceDecorator);
+
+ assertEquals("Precondition failed", 0, called.size());
+ listener.bundleChanged(new BundleEvent(BundleEvent.INSTALLED, b));
+ assertEquals(0, called.size());
+
+ listener.bundleChanged(new BundleEvent(BundleEvent.STARTED, b));
+ assertEquals(Arrays.asList("addDecorations"), called);
+
+ listener.bundleChanged(new BundleEvent(BundleEvent.STOPPING, b));
+ assertEquals(Arrays.asList("addDecorations", "removeDecorations"), called);
+
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecoratorImplTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecoratorImplTest.java
new file mode 100644
index 0000000..b770b89
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/decorator/ServiceDecoratorImplTest.java
@@ -0,0 +1,184 @@
+/**
+ * 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.decorator;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.easymock.EasyMock;
+import org.easymock.IAnswer;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+public class ServiceDecoratorImplTest extends TestCase {
+ private static final Map<String, Object> EMPTY = new HashMap<String, Object>();
+ private static final URL RES_SD = getResource("/test-resources/sd.xml");
+ private static final URL RES_SD1 = getResource("/test-resources/sd1.xml");
+ private static final URL RES_SD2 = getResource("/test-resources/sd2.xml");
+ private static final URL RES_SD0 = getResource("/test-resources/sd0.xml");
+ private static final URL RES_SD_1 = getResource("/test-resources/sd-1.xml");
+
+ public void testAddRemoveDecorations() {
+ final Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"org.acme.foo.Bar"});
+ serviceProps.put("test.prop", "xyz");
+
+ Bundle b = createBundleContaining(RES_SD);
+ ServiceDecoratorImpl sd = new ServiceDecoratorImpl();
+ assertEquals("Precondition failed", 0, sd.decorations.size());
+ sd.addDecorations(b);
+ assertEquals(1, sd.decorations.size());
+
+ Map<String, Object> target = new HashMap<String, Object>();
+ ServiceReference sref = EasyMock.createMock(ServiceReference.class);
+ EasyMock.expect(sref.getProperty((String) EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ return serviceProps.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(sref);
+ sd.decorate(sref, target);
+
+ Map<String, Object> expected = new HashMap<String, Object>();
+ expected.put("test.too", "ahaha");
+ assertEquals(expected, target);
+
+ // remove it again
+ sd.removeDecorations(b);
+ assertEquals(0, sd.decorations.size());
+ Map<String, Object> target2 = new HashMap<String, Object>();
+ sd.decorate(sref, target2);
+ assertEquals(EMPTY, target2);
+ }
+
+ public void testAddDecorations() {
+ final Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"org.acme.foo.Bar"});
+ serviceProps.put("test.prop", "xyz");
+
+ Map<String, Object> expected = new HashMap<String, Object>();
+ expected.put("test.too", "ahaha");
+ assertDecorate(serviceProps, expected, RES_SD);
+ }
+
+ public void testAddDecorations1() {
+ Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"org.test.A"});
+
+ Map<String, Object> expected = new HashMap<String, Object>();
+ expected.put("A", "B");
+ expected.put("C", 2);
+ assertDecorate(serviceProps, expected, RES_SD1, RES_SD2);
+ }
+
+ public void testAddDecorations2() {
+ Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"org.test.D"});
+
+ assertDecorate(serviceProps, EMPTY, RES_SD1, RES_SD2);
+ }
+
+ public void testAddDecorations3() {
+ Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"org.test.B"});
+ serviceProps.put("x", "y");
+
+ Map<String, Object> expected = new HashMap<String, Object>();
+ expected.put("bool", Boolean.TRUE);
+ assertDecorate(serviceProps, expected, RES_SD1, RES_SD2);
+ }
+
+ public void testAddDecorations4() {
+ Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"org.test.C"});
+ serviceProps.put("x", "z");
+
+ Map<String, Object> expected = new HashMap<String, Object>();
+ expected.put("bool", Boolean.FALSE);
+ assertDecorate(serviceProps, expected, RES_SD1, RES_SD2);
+ }
+
+ public void testAddDecorations5() {
+ Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"org.test.C"});
+ serviceProps.put("x", "x");
+
+ assertDecorate(serviceProps, EMPTY, RES_SD1, RES_SD2);
+ }
+
+ public void testAddDecorations6() {
+ Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"org.test.D"});
+
+ assertDecorate(serviceProps, EMPTY, RES_SD0);
+ }
+
+ public void testAddDecorations7() {
+ Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put(Constants.OBJECTCLASS, new String[] {"org.test.D"});
+
+ assertDecorate(serviceProps, EMPTY, RES_SD_1);
+ }
+
+ private void assertDecorate(final Map<String, Object> serviceProps,
+ Map<String, Object> expected, URL ... resources) {
+ Map<String, Object> actual = testDecorate(serviceProps, resources);
+ assertEquals(expected, actual);
+ }
+
+ private Map<String, Object> testDecorate(final Map<String, Object> serviceProps, URL ... resources) {
+ Bundle b = createBundleContaining(resources);
+
+ ServiceDecoratorImpl sd = new ServiceDecoratorImpl();
+ sd.addDecorations(b);
+
+ Map<String, Object> target = new HashMap<String, Object>();
+ ServiceReference sref = EasyMock.createMock(ServiceReference.class);
+ EasyMock.expect(sref.getProperty((String) EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ return serviceProps.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(sref);
+ sd.decorate(sref, target);
+ return target;
+ }
+
+ private Bundle createBundleContaining(URL... resources) {
+ Bundle b = EasyMock.createMock(Bundle.class);
+ EasyMock.expect(b.findEntries("OSGI-INF/remote-service", "*.xml", false)).andReturn(
+ Collections.enumeration(Arrays.asList(resources))).anyTimes();
+ EasyMock.replay(b);
+ return b;
+ }
+
+ private static URL getResource(String path) {
+ URL resource = ServiceDecoratorImplTest.class.getResource(path);
+ Assert.assertNotNull("Resource " + path + " not found!", resource);
+ return resource;
+ }
+
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/ClientServiceFactoryTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/ClientServiceFactoryTest.java
new file mode 100644
index 0000000..315c55b
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/ClientServiceFactoryTest.java
@@ -0,0 +1,70 @@
+/**
+ * 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.handlers;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.cxf.dosgi.dsw.service.ClientServiceFactory;
+import org.apache.cxf.dosgi.dsw.service.ImportRegistrationImpl;
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+public class ClientServiceFactoryTest extends TestCase {
+
+ public void testGetService() {
+ Object myTestProxyObject = new Object();
+
+ IMocksControl control = EasyMock.createNiceControl();
+ BundleContext dswContext = control.createMock(BundleContext.class);
+ Map<String, Object> map = new HashMap<String, Object>();
+ map.put(RemoteConstants.ENDPOINT_ID, "http://google.de");
+ map.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, "myGreatConfiguration");
+ map.put(Constants.OBJECTCLASS, new String[]{"my.class"});
+
+ EndpointDescription endpoint = new EndpointDescription(map);
+ ConfigurationTypeHandler handler = control.createMock(ConfigurationTypeHandler.class);
+
+ ImportRegistrationImpl iri = new ImportRegistrationImpl(endpoint, null);
+
+ BundleContext requestingContext = control.createMock(BundleContext.class);
+ Bundle requestingBundle = control.createMock(Bundle.class);
+ EasyMock.expect(requestingBundle.getBundleContext()).andReturn(requestingContext);
+
+ ServiceReference sr = control.createMock(ServiceReference.class);
+ ServiceRegistration sreg = control.createMock(ServiceRegistration.class);
+ EasyMock.expect(sreg.getReference()).andReturn(sr);
+
+ handler.createProxy(sr, dswContext, requestingContext, String.class, endpoint);
+ EasyMock.expectLastCall().andReturn(myTestProxyObject);
+ control.replay();
+
+ ClientServiceFactory csf = new ClientServiceFactory(dswContext, String.class, endpoint, handler, iri);
+ assertSame(myTestProxyObject, csf.getService(requestingBundle, sreg));
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/ConfigTypeHandlerFactoryTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/ConfigTypeHandlerFactoryTest.java
new file mode 100644
index 0000000..a74b194
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/ConfigTypeHandlerFactoryTest.java
@@ -0,0 +1,112 @@
+/**
+ * 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.handlers;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.dsw.Constants;
+import org.apache.cxf.dosgi.dsw.qos.DefaultIntentMapFactory;
+import org.apache.cxf.dosgi.dsw.qos.IntentManager;
+import org.apache.cxf.dosgi.dsw.qos.IntentManagerImpl;
+import org.apache.cxf.dosgi.dsw.qos.IntentMap;
+import org.easymock.EasyMock;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+import static org.junit.Assert.assertTrue;
+
+public class ConfigTypeHandlerFactoryTest {
+
+ @Test
+ public void testGetDefaultHandlerNoIntents() {
+ ConfigurationTypeHandler handler = getHandlerWith(null, null);
+ assertTrue(handler instanceof PojoConfigurationTypeHandler);
+ }
+
+ @Test
+ public void testGetJaxrsHandlerNoIntents() {
+ ConfigurationTypeHandler handler = getHandlerWith(Constants.RS_CONFIG_TYPE, null);
+ assertTrue(handler instanceof JaxRSPojoConfigurationTypeHandler);
+ }
+
+ @Test
+ public void testGetJaxrsHandlerHttpIntents() {
+ ConfigurationTypeHandler handler = getHandlerWith(Constants.RS_CONFIG_TYPE, "HTTP");
+ assertTrue(handler instanceof JaxRSPojoConfigurationTypeHandler);
+ }
+
+ @Test
+ public void testJaxrsPropertyIgnored() {
+ ConfigurationTypeHandler handler = getHandlerWith(Constants.RS_CONFIG_TYPE, "SOAP HTTP");
+ assertTrue(handler instanceof PojoConfigurationTypeHandler);
+ assertTrue(!(handler instanceof JaxRSPojoConfigurationTypeHandler));
+ }
+
+ @Test
+ public void testJaxrsPropertyIgnored2() {
+ ConfigurationTypeHandler handler = getHandlerWith2(Constants.RS_CONFIG_TYPE, new String[] {"HTTP", "SOAP"});
+ assertTrue(handler instanceof PojoConfigurationTypeHandler);
+ assertTrue(!(handler instanceof JaxRSPojoConfigurationTypeHandler));
+ }
+
+ @Test
+ public void testGetPojoHandler() {
+ ConfigurationTypeHandler handler = getHandlerWith(Constants.WS_CONFIG_TYPE, null);
+ assertTrue(handler instanceof PojoConfigurationTypeHandler);
+ }
+
+ @Test
+ public void testGetWSDLHandler() {
+ ConfigurationTypeHandler handler = getHandlerWith(Constants.WSDL_CONFIG_TYPE, null);
+ assertTrue(handler instanceof WsdlConfigurationTypeHandler);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testUnsupportedConfiguration() {
+ getHandlerWith("notSupportedConfig", null);
+ }
+
+ private ConfigurationTypeHandler getHandlerWith(String configType, String intents) {
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.replay(bc);
+ Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put(RemoteConstants.SERVICE_EXPORTED_CONFIGS, configType);
+ serviceProps.put(RemoteConstants.SERVICE_EXPORTED_INTENTS, intents);
+ IntentMap intentMap = new IntentMap(new DefaultIntentMapFactory().create());
+ IntentManager intentManager = new IntentManagerImpl(intentMap);
+ HttpServiceManager httpServiceManager = new HttpServiceManager(bc, null, null);
+ ConfigTypeHandlerFactory f = new ConfigTypeHandlerFactory(bc, intentManager, httpServiceManager);
+ return f.getHandler(bc, serviceProps);
+ }
+
+ private ConfigurationTypeHandler getHandlerWith2(String configType, String[] intents) {
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.replay(bc);
+ Map<String, Object> serviceProps = new HashMap<String, Object>();
+ serviceProps.put(RemoteConstants.SERVICE_EXPORTED_CONFIGS, configType);
+ serviceProps.put(RemoteConstants.SERVICE_EXPORTED_INTENTS, intents);
+ IntentMap intentMap = new IntentMap(new DefaultIntentMapFactory().create());
+ IntentManager intentManager = new IntentManagerImpl(intentMap);
+ HttpServiceManager httpServiceManager = new HttpServiceManager(bc, null, null);
+ ConfigTypeHandlerFactory f = new ConfigTypeHandlerFactory(bc, intentManager, httpServiceManager);
+ return f.getHandler(bc, serviceProps);
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/HttpServiceManagerTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/HttpServiceManagerTest.java
new file mode 100644
index 0000000..208c457
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/HttpServiceManagerTest.java
@@ -0,0 +1,124 @@
+/**
+ * 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.handlers;
+
+import java.util.Dictionary;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.cxf.Bus;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.NamespaceException;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+
+public class HttpServiceManagerTest extends TestCase {
+
+ public void testGetAbsoluteAddress() {
+ IMocksControl c = EasyMock.createControl();
+ BundleContext bundleContext = c.createMock(BundleContext.class);
+ c.replay();
+ HttpServiceManager manager = new HttpServiceManager(bundleContext, null, null, null);
+ String localIp = LocalHostUtil.getLocalIp();
+
+ String address1 = manager.getAbsoluteAddress(null, "/myservice");
+ assertEquals("http://" + localIp + ":8181/cxf/myservice", address1);
+
+ String address2 = manager.getAbsoluteAddress("/mycontext", "/myservice");
+ assertEquals("http://" + localIp + ":8181/mycontext/myservice", address2);
+
+ c.verify();
+ }
+
+ public void testRegisterAndUnregisterServlet() throws Exception {
+ IMocksControl c = EasyMock.createControl();
+ BundleContext dswContext = c.createMock(BundleContext.class);
+ Filter filter = c.createMock(Filter.class);
+ expect(dswContext.createFilter(EasyMock.eq("(service.id=12345)"))).andReturn(filter).once();
+ Capture<ServiceListener> captured = new Capture<ServiceListener>();
+ dswContext.addServiceListener(EasyMock.capture(captured), EasyMock.<String>anyObject());
+ expectLastCall().atLeastOnce();
+ expect(dswContext.getProperty("org.apache.cxf.httpservice.requirefilter")).andReturn(null).atLeastOnce();
+ ServletConfig config = c.createMock(ServletConfig.class);
+ expect(config.getInitParameter(EasyMock.<String>anyObject())).andReturn(null).atLeastOnce();
+ ServletContext servletContext = c.createMock(ServletContext.class);
+ expect(config.getServletContext()).andReturn(servletContext);
+ final HttpService httpService = new DummyHttpService(config);
+ ServiceReference sr = c.createMock(ServiceReference.class);
+ expect(sr.getProperty(EasyMock.eq("service.id"))).andReturn(12345L).atLeastOnce();
+ expect(servletContext.getResourceAsStream((String)EasyMock.anyObject())).andReturn(null).anyTimes();
+ c.replay();
+
+ HttpServiceManager h = new HttpServiceManager(dswContext, null, null, null) {
+ @Override
+ protected HttpService getHttpService() {
+ return httpService;
+ }
+ };
+ Bus bus = h.registerServletAndGetBus("/myService", dswContext, sr);
+ Assert.assertNotNull(bus);
+
+ ServiceEvent event = new ServiceEvent(ServiceEvent.UNREGISTERING, sr);
+ captured.getValue().serviceChanged(event);
+ c.verify();
+ }
+
+ static class DummyHttpService implements HttpService {
+
+ private ServletConfig config;
+
+ public DummyHttpService(ServletConfig config) {
+ this.config = config;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void registerServlet(String alias, Servlet servlet, Dictionary initparams, HttpContext context)
+ throws ServletException, NamespaceException {
+ Assert.assertEquals("/myService", alias);
+ servlet.init(config);
+ }
+
+ public void registerResources(String alias, String name, HttpContext context) throws NamespaceException {
+ throw new RuntimeException("This method should not be called");
+ }
+
+ public void unregister(String alias) {
+ }
+
+ public HttpContext createDefaultHttpContext() {
+ return EasyMock.createNiceMock(HttpContext.class);
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSUtilsTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSUtilsTest.java
new file mode 100644
index 0000000..b0976af
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/JaxRSUtilsTest.java
@@ -0,0 +1,185 @@
+/**
+ * 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.handlers;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.cxf.dosgi.dsw.Constants;
+import org.apache.cxf.jaxrs.provider.JAXBElementProvider;
+import org.apache.cxf.jaxrs.provider.aegis.AegisElementProvider;
+import org.easymock.EasyMock;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+public class JaxRSUtilsTest extends TestCase {
+
+ private void addRequiredProps(Map<String, Object> props) {
+ props.put(RemoteConstants.ENDPOINT_ID, "http://google.de");
+ props.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, "myGreatConfiguration");
+ props.put(org.osgi.framework.Constants.OBJECTCLASS, new String[] {"my.class"});
+ }
+
+ public void testNoGlobalProviders() {
+ Map<String, Object> props = new HashMap<String, Object>();
+ addRequiredProps(props);
+ props.put(Constants.RS_PROVIDER_GLOBAL_PROP_KEY, "false");
+
+ assertEquals(0, JaxRSUtils.getProviders(null, props).size());
+ }
+
+ public void testAegisProvider() {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(Constants.RS_DATABINDING_PROP_KEY, "aegis");
+ props.put(Constants.RS_PROVIDER_GLOBAL_PROP_KEY, "false");
+
+ addRequiredProps(props);
+
+ List<Object> providers = JaxRSUtils.getProviders(null, props);
+ assertEquals(1, providers.size());
+ assertEquals(AegisElementProvider.class.getName(), providers.get(0).getClass().getName());
+ }
+
+ public void testServiceProviders() {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(Constants.RS_PROVIDER_PROP_KEY, new Object[] {
+ new AegisElementProvider()
+ });
+ props.put(Constants.RS_PROVIDER_GLOBAL_PROP_KEY, "false");
+ addRequiredProps(props);
+
+ List<Object> providers = JaxRSUtils.getProviders(null, props);
+ assertEquals(1, providers.size());
+ assertEquals(AegisElementProvider.class.getName(), providers.get(0).getClass().getName());
+ }
+
+ public void testServiceProviderProperty() throws Exception {
+ BundleContext bc = EasyMock.createMock(BundleContext.class);
+ Bundle bundle = EasyMock.createMock(Bundle.class);
+ bc.getBundle();
+ EasyMock.expectLastCall().andReturn(bundle).times(2);
+ bundle.loadClass(AegisElementProvider.class.getName());
+ EasyMock.expectLastCall().andReturn(AegisElementProvider.class);
+ bundle.loadClass(JAXBElementProvider.class.getName());
+ EasyMock.expectLastCall().andReturn(JAXBElementProvider.class);
+ EasyMock.replay(bc, bundle);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(Constants.RS_PROVIDER_PROP_KEY,
+ "\r\n " + AegisElementProvider.class.getName() + " , \r\n"
+ + JAXBElementProvider.class.getName() + "\r\n");
+
+ props.put(Constants.RS_PROVIDER_GLOBAL_PROP_KEY, "false");
+ addRequiredProps(props);
+
+ List<Object> providers = JaxRSUtils.getProviders(bc, props);
+ assertEquals(2, providers.size());
+ assertEquals(AegisElementProvider.class.getName(), providers.get(0).getClass().getName());
+ assertEquals(JAXBElementProvider.class.getName(), providers.get(1).getClass().getName());
+ }
+
+ public void testServiceProviderStrings() throws Exception {
+ BundleContext bc = EasyMock.createMock(BundleContext.class);
+ Bundle bundle = EasyMock.createMock(Bundle.class);
+ bc.getBundle();
+ EasyMock.expectLastCall().andReturn(bundle).times(2);
+ bundle.loadClass(AegisElementProvider.class.getName());
+ EasyMock.expectLastCall().andReturn(AegisElementProvider.class);
+ bundle.loadClass(JAXBElementProvider.class.getName());
+ EasyMock.expectLastCall().andReturn(JAXBElementProvider.class);
+ EasyMock.replay(bc, bundle);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(Constants.RS_PROVIDER_PROP_KEY, new String[] {
+ "\r\n " + AegisElementProvider.class.getName(),
+ JAXBElementProvider.class.getName() + "\r\n"
+ });
+
+ props.put(Constants.RS_PROVIDER_GLOBAL_PROP_KEY, "false");
+ addRequiredProps(props);
+
+ List<Object> providers = JaxRSUtils.getProviders(bc, props);
+ assertEquals(2, providers.size());
+ assertEquals(AegisElementProvider.class.getName(), providers.get(0).getClass().getName());
+ assertEquals(JAXBElementProvider.class.getName(), providers.get(1).getClass().getName());
+ }
+
+ public void testCustomGlobalProvider() throws Exception {
+ ServiceReference sref = EasyMock.createNiceMock(ServiceReference.class);
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ bc.getServiceReferences((String)null, JaxRSUtils.PROVIDERS_FILTER);
+ EasyMock.expectLastCall().andReturn(new ServiceReference[] {sref});
+ sref.getProperty(Constants.RS_PROVIDER_EXPECTED_PROP_KEY);
+ EasyMock.expectLastCall().andReturn(false);
+ bc.getService(sref);
+ AegisElementProvider<?> p = new AegisElementProvider();
+ EasyMock.expectLastCall().andReturn(p);
+ EasyMock.replay(bc, sref);
+ Map<String, Object> props = new HashMap<String, Object>();
+ addRequiredProps(props);
+
+ List<Object> providers = JaxRSUtils.getProviders(bc, props);
+ assertEquals(1, providers.size());
+ assertSame(p, providers.get(0));
+ }
+
+ public void testNoCustomGlobalProvider() throws Exception {
+ ServiceReference sref = EasyMock.createNiceMock(ServiceReference.class);
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ bc.getServiceReferences((String)null, JaxRSUtils.PROVIDERS_FILTER);
+ EasyMock.expectLastCall().andReturn(new ServiceReference[] {sref});
+ sref.getProperty(Constants.RS_PROVIDER_PROP_KEY);
+ EasyMock.expectLastCall().andReturn(false);
+ bc.getService(sref);
+ AegisElementProvider<?> p = new AegisElementProvider();
+ EasyMock.expectLastCall().andReturn(p);
+ EasyMock.replay(bc);
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(Constants.RS_PROVIDER_EXPECTED_PROP_KEY, "true");
+ addRequiredProps(props);
+
+ List<Object> providers = JaxRSUtils.getProviders(bc, props);
+ assertEquals(0, providers.size());
+ }
+
+ public void testCustomGlobalProviderExpected() throws Exception {
+ ServiceReference sref = EasyMock.createNiceMock(ServiceReference.class);
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ bc.getServiceReferences((String)null, JaxRSUtils.PROVIDERS_FILTER);
+ EasyMock.expectLastCall().andReturn(new ServiceReference[] {sref});
+ sref.getProperty(Constants.RS_PROVIDER_PROP_KEY);
+ EasyMock.expectLastCall().andReturn(true);
+ bc.getService(sref);
+ AegisElementProvider<?> p = new AegisElementProvider();
+ EasyMock.expectLastCall().andReturn(p);
+ EasyMock.replay(bc, sref);
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(Constants.RS_PROVIDER_EXPECTED_PROP_KEY, "true");
+ addRequiredProps(props);
+
+ List<Object> providers = JaxRSUtils.getProviders(bc, props);
+ assertEquals(1, providers.size());
+ assertSame(p, providers.get(0));
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/PojoConfigurationTypeHandlerTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/PojoConfigurationTypeHandlerTest.java
new file mode 100644
index 0000000..3505035
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/PojoConfigurationTypeHandlerTest.java
@@ -0,0 +1,425 @@
+/**
+ * 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.handlers;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.namespace.QName;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.cxf.dosgi.dsw.Constants;
+import org.apache.cxf.dosgi.dsw.handlers.jaxws.MyJaxWsEchoService;
+import org.apache.cxf.dosgi.dsw.handlers.jaxws.MyJaxWsEchoServiceImpl;
+import org.apache.cxf.dosgi.dsw.handlers.simple.MySimpleEchoService;
+import org.apache.cxf.dosgi.dsw.handlers.simple.MySimpleEchoServiceImpl;
+import org.apache.cxf.dosgi.dsw.qos.IntentManager;
+import org.apache.cxf.dosgi.dsw.qos.IntentManagerImpl;
+import org.apache.cxf.dosgi.dsw.qos.IntentMap;
+import org.apache.cxf.endpoint.AbstractEndpointFactory;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.endpoint.EndpointImpl;
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.feature.Feature;
+import org.apache.cxf.frontend.ClientProxyFactoryBean;
+import org.apache.cxf.frontend.ServerFactoryBean;
+import org.apache.cxf.jaxws.support.JaxWsEndpointImpl;
+import org.apache.cxf.service.factory.ReflectionServiceFactoryBean;
+import org.apache.cxf.transport.Destination;
+import org.apache.cxf.ws.addressing.AttributedURIType;
+import org.apache.cxf.ws.addressing.EndpointReferenceType;
+import org.easymock.IAnswer;
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+public class PojoConfigurationTypeHandlerTest extends TestCase {
+
+ public void testGetPojoAddressEndpointURI() {
+ IntentManager intentManager = new IntentManagerImpl(new IntentMap());
+ PojoConfigurationTypeHandler handler = new PojoConfigurationTypeHandler(null,
+ intentManager,
+ dummyHttpServiceManager());
+ Map<String, Object> sd = new HashMap<String, Object>();
+ String url = "http://somewhere:1234/blah";
+ sd.put(RemoteConstants.ENDPOINT_ID, url);
+ assertEquals(url, handler.getServerAddress(sd, String.class));
+ }
+
+ private HttpServiceManager dummyHttpServiceManager() {
+ return new HttpServiceManager(null, null, null, null);
+ }
+
+ public void testGetPojoAddressEndpointCxf() {
+ IntentManager intentManager = new IntentManagerImpl(new IntentMap());
+ PojoConfigurationTypeHandler handler = new PojoConfigurationTypeHandler(null,
+ intentManager,
+ dummyHttpServiceManager());
+ Map<String, Object> sd = new HashMap<String, Object>();
+ String url = "http://somewhere:29/boo";
+ sd.put("org.apache.cxf.ws.address", url);
+ assertEquals(url, handler.getServerAddress(sd, String.class));
+ }
+
+ public void testGetPojoAddressEndpointPojo() {
+ IntentManager intentManager = new IntentManagerImpl(new IntentMap());
+ PojoConfigurationTypeHandler handler = new PojoConfigurationTypeHandler(null,
+ intentManager,
+ dummyHttpServiceManager());
+ Map<String, Object> sd = new HashMap<String, Object>();
+ String url = "http://somewhere:32768/foo";
+ sd.put("osgi.remote.configuration.pojo.address", url);
+ assertEquals(url, handler.getServerAddress(sd, String.class));
+ }
+
+ public void testGetDefaultPojoAddress() {
+ IntentManager intentManager = new IntentManagerImpl(new IntentMap());
+ PojoConfigurationTypeHandler handler = new PojoConfigurationTypeHandler(null,
+ intentManager ,
+ dummyHttpServiceManager());
+ Map<String, Object> sd = new HashMap<String, Object>();
+ assertEquals("/java/lang/String", handler.getServerAddress(sd, String.class));
+ }
+
+ // todo: add test for data bindings
+ public void testCreateProxy() {
+ IMocksControl c = EasyMock.createNiceControl();
+ BundleContext bc1 = c.createMock(BundleContext.class);
+ BundleContext bc2 = c.createMock(BundleContext.class);
+
+ ServiceReference sref = c.createMock(ServiceReference.class);
+
+ final ClientProxyFactoryBean cpfb = c.createMock(ClientProxyFactoryBean.class);
+ ReflectionServiceFactoryBean sf = c.createMock(ReflectionServiceFactoryBean.class);
+ EasyMock.expect(cpfb.getServiceFactory()).andReturn(sf).anyTimes();
+ IntentManager intentManager = new IntentManagerImpl(new IntentMap()) {
+ @Override
+ public String[] applyIntents(List<Feature> features,
+ AbstractEndpointFactory factory,
+ Map<String, Object> sd) {
+ return new String[0];
+ }
+ };
+ PojoConfigurationTypeHandler p = new PojoConfigurationTypeHandler(bc1,
+ intentManager,
+ dummyHttpServiceManager()) {
+ @Override
+ protected ClientProxyFactoryBean createClientProxyFactoryBean(Map<String, Object> sd, Class<?> iClass) {
+ return cpfb;
+ }
+ };
+
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ props.put(RemoteConstants.ENDPOINT_ID, "http://google.de/");
+ props.put(org.osgi.framework.Constants.OBJECTCLASS, new String[]{"my.class"});
+ props.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, new String[]{"my.config"});
+ EndpointDescription endpoint = new EndpointDescription(props);
+
+ cpfb.setAddress((String)EasyMock.eq(props.get(RemoteConstants.ENDPOINT_ID)));
+ EasyMock.expectLastCall().atLeastOnce();
+
+ cpfb.setServiceClass(EasyMock.eq(CharSequence.class));
+ EasyMock.expectLastCall().atLeastOnce();
+
+ c.replay();
+ Object proxy = p.createProxy(sref, bc1, bc2, CharSequence.class, endpoint);
+ assertNotNull(proxy);
+ assertTrue("Proxy is not of the requested type! ", proxy instanceof CharSequence);
+ c.verify();
+ }
+
+ public void testCreateServerWithAddressProperty() {
+ BundleContext dswContext = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.replay(dswContext);
+
+ String myService = "Hi";
+ final ServerFactoryBean sfb = createMockServerFactoryBean();
+
+ IntentMap intentMap = new IntentMap();
+ IntentManager intentManager = new IntentManagerImpl(intentMap) {
+ @Override
+ public String[] applyIntents(List<Feature> features, AbstractEndpointFactory factory,
+ Map<String, Object> sd) {
+ return new String[]{};
+ }
+ };
+ PojoConfigurationTypeHandler p = new PojoConfigurationTypeHandler(dswContext, intentManager,
+ dummyHttpServiceManager()) {
+ @Override
+ protected ServerFactoryBean createServerFactoryBean(Map<String, Object> sd, Class<?> iClass) {
+ return sfb;
+ }
+ };
+
+ ServiceReference sr = EasyMock.createNiceMock(ServiceReference.class);
+ BundleContext callingContext = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.replay(sr);
+ EasyMock.replay(callingContext);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(Constants.WS_ADDRESS_PROPERTY, "http://alternate_host:80/myString");
+
+ ExportResult exportResult = p.createServer(sr, dswContext, callingContext, props, String.class, myService);
+
+ Map<String, Object> edProps = exportResult.getEndpointProps();
+
+ assertNotNull(edProps.get(RemoteConstants.SERVICE_IMPORTED_CONFIGS));
+ assertEquals(1, ((String[])edProps.get(RemoteConstants.SERVICE_IMPORTED_CONFIGS)).length);
+ assertEquals(Constants.WS_CONFIG_TYPE, ((String[])edProps.get(RemoteConstants.SERVICE_IMPORTED_CONFIGS))[0]);
+ assertEquals("http://alternate_host:80/myString", edProps.get(RemoteConstants.ENDPOINT_ID));
+ }
+
+ public void testAddressing() {
+ runAddressingTest(new HashMap<String, Object>(), "http://localhost:9000/java/lang/Runnable");
+
+ Map<String, Object> p1 = new HashMap<String, Object>();
+ p1.put("org.apache.cxf.ws.address", "http://somewhere");
+ runAddressingTest(p1, "http://somewhere");
+
+ Map<String, Object> p2 = new HashMap<String, Object>();
+ p2.put("org.apache.cxf.rs.address", "https://somewhereelse");
+ runAddressingTest(p2, "https://somewhereelse");
+
+ Map<String, Object> p3 = new HashMap<String, Object>();
+ p3.put("org.apache.cxf.ws.port", 65535);
+ runAddressingTest(p3, "http://localhost:65535/java/lang/Runnable");
+
+ Map<String, Object> p4 = new HashMap<String, Object>();
+ p4.put("org.apache.cxf.ws.port", "8181");
+ runAddressingTest(p4, "http://localhost:8181/java/lang/Runnable");
+ }
+
+ private void runAddressingTest(Map<String, Object> properties, String expectedAddress) {
+ BundleContext dswContext = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.replay(dswContext);
+
+ IntentManager intentManager = EasyMock.createNiceMock(IntentManager.class);
+ EasyMock.replay(intentManager);
+
+ PojoConfigurationTypeHandler handler = new PojoConfigurationTypeHandler(dswContext,
+ intentManager,
+ dummyHttpServiceManager()) {
+ @Override
+ protected ExportResult createServerFromFactory(ServerFactoryBean factory,
+ Map<String, Object> endpointProps) {
+ return new ExportResult(endpointProps, (Server) null);
+ }
+ };
+
+ ServiceReference sref = EasyMock.createNiceMock(ServiceReference.class);
+ EasyMock.replay(sref);
+
+ Runnable myService = EasyMock.createMock(Runnable.class);
+ EasyMock.replay(myService);
+ ExportResult result = handler.createServer(sref, null, null, properties, Runnable.class, myService);
+ assertNull(result.getException());
+
+ Map<String, Object> props = result.getEndpointProps();
+ assertEquals(expectedAddress, props.get("org.apache.cxf.ws.address"));
+ assertEquals("Version of java. package is always 0", "0.0.0", props.get("endpoint.package.version.java.lang"));
+ assertTrue(Arrays.equals(new String[] {"org.apache.cxf.ws"}, (String[]) props.get("service.imported.configs")));
+ assertTrue(Arrays.equals(new String[] {"java.lang.Runnable"}, (String[]) props.get("objectClass")));
+ assertNotNull(props.get("endpoint.framework.uuid"));
+ }
+
+ public void testCreateServerException() {
+ BundleContext dswContext = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.replay(dswContext);
+
+ IntentManager intentManager = EasyMock.createNiceMock(IntentManager.class);
+ EasyMock.replay(intentManager);
+
+ PojoConfigurationTypeHandler handler = new PojoConfigurationTypeHandler(dswContext,
+ intentManager,
+ dummyHttpServiceManager()) {
+ @Override
+ protected ExportResult createServerFromFactory(ServerFactoryBean factory,
+ Map<String, Object> endpointProps) {
+ throw new TestException();
+ }
+ };
+
+ ServiceReference sref = EasyMock.createNiceMock(ServiceReference.class);
+ EasyMock.replay(sref);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+
+ Runnable myService = EasyMock.createMock(Runnable.class);
+ EasyMock.replay(myService);
+ ExportResult result = handler.createServer(sref, null, null, props, Runnable.class, myService);
+ Assert.assertTrue(result.getException() instanceof TestException);
+ Assert.assertEquals(props, result.getEndpointProps());
+ }
+
+ private ServerFactoryBean createMockServerFactoryBean() {
+ ReflectionServiceFactoryBean sf = EasyMock.createNiceMock(ReflectionServiceFactoryBean.class);
+ EasyMock.replay(sf);
+
+ final StringBuilder serverURI = new StringBuilder();
+
+ ServerFactoryBean sfb = EasyMock.createNiceMock(ServerFactoryBean.class);
+ Server server = createMockServer(sfb);
+
+ EasyMock.expect(sfb.getServiceFactory()).andReturn(sf).anyTimes();
+ EasyMock.expect(sfb.create()).andReturn(server);
+ sfb.setAddress((String) EasyMock.anyObject());
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ serverURI.setLength(0);
+ serverURI.append(EasyMock.getCurrentArguments()[0]);
+ return null;
+ }
+ });
+ EasyMock.expect(sfb.getAddress()).andAnswer(new IAnswer<String>() {
+ public String answer() throws Throwable {
+ return serverURI.toString();
+ }
+ });
+ EasyMock.replay(sfb);
+ return sfb;
+ }
+
+ private Server createMockServer(final ServerFactoryBean sfb) {
+ AttributedURIType addr = EasyMock.createMock(AttributedURIType.class);
+ EasyMock.expect(addr.getValue()).andAnswer(new IAnswer<String>() {
+ public String answer() throws Throwable {
+ return sfb.getAddress();
+ }
+ });
+ EasyMock.replay(addr);
+
+ EndpointReferenceType er = EasyMock.createMock(EndpointReferenceType.class);
+ EasyMock.expect(er.getAddress()).andReturn(addr);
+ EasyMock.replay(er);
+
+ Destination destination = EasyMock.createMock(Destination.class);
+ EasyMock.expect(destination.getAddress()).andReturn(er);
+ EasyMock.replay(destination);
+
+ Server server = EasyMock.createNiceMock(Server.class);
+ EasyMock.expect(server.getDestination()).andReturn(destination);
+ EasyMock.replay(server);
+ return server;
+ }
+
+ public void testCreateEndpointProps() {
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.getProperty("org.osgi.framework.uuid")).andReturn("some_uuid1");
+ EasyMock.replay(bc);
+
+ IntentManager intentManager = new IntentManagerImpl(new IntentMap());
+ PojoConfigurationTypeHandler pch = new PojoConfigurationTypeHandler(bc,
+ intentManager,
+ dummyHttpServiceManager());
+
+ Map<String, Object> sd = new HashMap<String, Object>();
+ sd.put(org.osgi.framework.Constants.SERVICE_ID, 42);
+ Map<String, Object> props = pch.createEndpointProps(sd, String.class, new String[] {"org.apache.cxf.ws"},
+ "http://localhost:12345", new String[] {"my_intent", "your_intent"});
+
+ assertFalse(props.containsKey(org.osgi.framework.Constants.SERVICE_ID));
+ assertEquals(42, props.get(RemoteConstants.ENDPOINT_SERVICE_ID));
+ assertEquals("some_uuid1", props.get(RemoteConstants.ENDPOINT_FRAMEWORK_UUID));
+ assertEquals("http://localhost:12345", props.get(RemoteConstants.ENDPOINT_ID));
+ assertEquals(Arrays.asList("java.lang.String"),
+ Arrays.asList((Object[]) props.get(org.osgi.framework.Constants.OBJECTCLASS)));
+ assertEquals(Arrays.asList("org.apache.cxf.ws"),
+ Arrays.asList((Object[]) props.get(RemoteConstants.SERVICE_IMPORTED_CONFIGS)));
+ assertEquals(Arrays.asList("my_intent", "your_intent"),
+ Arrays.asList((Object[]) props.get(RemoteConstants.SERVICE_INTENTS)));
+ assertEquals("0.0.0", props.get("endpoint.package.version.java.lang"));
+ }
+
+ public void testCreateJaxWsEndpointWithoutIntents() {
+ IMocksControl c = EasyMock.createNiceControl();
+ BundleContext dswBC = c.createMock(BundleContext.class);
+ IntentManager intentManager = new DummyIntentManager();
+ PojoConfigurationTypeHandler handler = new PojoConfigurationTypeHandler(dswBC,
+ intentManager,
+ dummyHttpServiceManager());
+
+ Object serviceBean = new MyJaxWsEchoServiceImpl();
+ ServiceReference sref = c.createMock(ServiceReference.class);
+
+ Map<String, Object> sd = new HashMap<String, Object>();
+ sd.put(Constants.WS_ADDRESS_PROPERTY, "/somewhere");
+
+ c.replay();
+ ExportResult exportResult = handler.createServer(sref, dswBC, null, sd, MyJaxWsEchoService.class, serviceBean);
+ c.verify();
+
+ Server server = exportResult.getServer();
+ Endpoint ep = server.getEndpoint();
+ QName bindingName = ep.getEndpointInfo().getBinding().getName();
+ Assert.assertEquals(JaxWsEndpointImpl.class, ep.getClass());
+ Assert.assertEquals(new QName("http://jaxws.handlers.dsw.dosgi.cxf.apache.org/",
+ "MyJaxWsEchoServiceServiceSoapBinding"),
+ bindingName);
+ }
+
+ public void testCreateSimpleEndpointWithoutIntents() {
+ IMocksControl c = EasyMock.createNiceControl();
+ BundleContext dswBC = c.createMock(BundleContext.class);
+ IntentManager intentManager = new DummyIntentManager();
+ PojoConfigurationTypeHandler handler
+ = new PojoConfigurationTypeHandler(dswBC, intentManager, dummyHttpServiceManager());
+ Object serviceBean = new MySimpleEchoServiceImpl();
+ ServiceReference sref = c.createMock(ServiceReference.class);
+ Map<String, Object> sd = new HashMap<String, Object>();
+ sd.put(Constants.WS_ADDRESS_PROPERTY, "/somewhere_else");
+
+ c.replay();
+ ExportResult exportResult = handler.createServer(sref, dswBC, null, sd, MySimpleEchoService.class, serviceBean);
+ Server server = exportResult.getServer();
+ c.verify();
+
+ Endpoint ep = server.getEndpoint();
+ QName bindingName = ep.getEndpointInfo().getBinding().getName();
+ Assert.assertEquals(EndpointImpl.class, ep.getClass());
+ Assert.assertEquals(new QName("http://simple.handlers.dsw.dosgi.cxf.apache.org/",
+ "MySimpleEchoServiceSoapBinding"),
+ bindingName);
+ }
+
+ public static class DummyIntentManager implements IntentManager {
+
+ @Override
+ public String[] applyIntents(List<Feature> features,
+ AbstractEndpointFactory factory,
+ Map<String, Object> props) {
+ return new String[]{};
+ }
+
+ @Override
+ public void assertAllIntentsSupported(Map<String, Object> serviceProperties) {
+ }
+ }
+
+ @SuppressWarnings("serial")
+ public static class TestException extends RuntimeException {
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/SecurityDelegatingHttpContextTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/SecurityDelegatingHttpContextTest.java
new file mode 100644
index 0000000..22b3291
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/SecurityDelegatingHttpContextTest.java
@@ -0,0 +1,264 @@
+/**
+ * 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.handlers;
+
+import java.io.PrintWriter;
+import java.net.URL;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import junit.framework.TestCase;
+
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpContext;
+
+public class SecurityDelegatingHttpContextTest extends TestCase {
+
+ protected HttpContext defaultHttpContext;
+ protected SecurityDelegatingHttpContext httpContext;
+ protected CommitResponseFilter commitFilter;
+ protected DoNothingFilter doNothingFilter;
+ protected AccessDeniedFilter accessDeniedFilter;
+ protected String mimeType;
+ protected URL url; // does not need to exist
+
+ public void setUp() throws Exception {
+ mimeType = "text/xml";
+ url = new URL("file:test.xml"); // does not need to exist
+
+ // Sample filters
+ commitFilter = new CommitResponseFilter();
+ doNothingFilter = new DoNothingFilter();
+ accessDeniedFilter = new AccessDeniedFilter();
+
+ // Mock up the default http context
+ defaultHttpContext = EasyMock.createNiceMock(HttpContext.class);
+ EasyMock.expect(defaultHttpContext.getMimeType((String)EasyMock.anyObject())).andReturn(mimeType);
+ EasyMock.expect(defaultHttpContext.getResource((String)EasyMock.anyObject())).andReturn(url);
+ EasyMock.replay(defaultHttpContext);
+ }
+
+ public void testFilterRequired() throws Exception {
+ // Mock up the service references
+ ServiceReference[] serviceReferences = new ServiceReference[] {};
+
+ // Mock up the bundle context
+ BundleContext bundleContext = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bundleContext.getServiceReferences(Filter.class.getName(),
+ "(org.apache.cxf.httpservice.filter=true)"))
+ .andReturn(serviceReferences);
+ EasyMock.replay(bundleContext);
+
+ // Set up the secure http context
+ httpContext = new SecurityDelegatingHttpContext(bundleContext, defaultHttpContext);
+ httpContext.requireFilter = true;
+
+ // Ensure that the httpContext doesn't allow the request to be processed, since there are no registered servlet
+ // filters
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.replay(request);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.replay(response);
+ boolean requestAllowed = httpContext.handleSecurity(request, response);
+ Assert.assertFalse(requestAllowed);
+
+ // Ensure that the httpContext returns true if there is no requirement for registered servlet filters
+ httpContext.requireFilter = false;
+ requestAllowed = httpContext.handleSecurity(request, response);
+ Assert.assertTrue(requestAllowed);
+ }
+
+ public void testSingleCommitFilter() throws Exception {
+ // Mock up the service references
+ ServiceReference filterReference = EasyMock.createNiceMock(ServiceReference.class);
+ EasyMock.replay(filterReference);
+ ServiceReference[] serviceReferences = new ServiceReference[] {
+ filterReference
+ };
+
+ // Mock up the bundle context
+ BundleContext bundleContext = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bundleContext.getServiceReferences((String)EasyMock.anyObject(), (String)EasyMock.anyObject()))
+ .andReturn(serviceReferences);
+ EasyMock.expect(bundleContext.getService((ServiceReference)EasyMock.anyObject())).andReturn(commitFilter);
+ EasyMock.replay(bundleContext);
+
+ // Set up the secure http context
+ httpContext = new SecurityDelegatingHttpContext(bundleContext, defaultHttpContext);
+
+ // Ensure that the httpContext returns false, since the filter has committed the response
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.replay(request);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.isCommitted()).andReturn(false); // the first call checks to see whether to invoke the
+ // filter
+ EasyMock.expect(response.isCommitted()).andReturn(true); // the second is called to determine the handleSecurity
+ // return value
+ EasyMock.expect(response.getWriter()).andReturn(new PrintWriter(System.out));
+ EasyMock.replay(response);
+ Assert.assertFalse(httpContext.handleSecurity(request, response));
+
+ // Ensure that the appropriate filters were called
+ Assert.assertTrue(commitFilter.called);
+ Assert.assertFalse(doNothingFilter.called);
+ Assert.assertFalse(accessDeniedFilter.called);
+ }
+
+ public void testFilterChain() throws Exception {
+ // Mock up the service references
+ ServiceReference filterReference = EasyMock.createNiceMock(ServiceReference.class);
+ EasyMock.replay(filterReference);
+ ServiceReference[] serviceReferences = new ServiceReference[] {
+ filterReference, filterReference
+ };
+
+ // Mock up the bundle context
+ BundleContext bundleContext = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bundleContext.getServiceReferences((String)EasyMock.anyObject(), (String)EasyMock.anyObject()))
+ .andReturn(serviceReferences);
+ EasyMock.expect(bundleContext.getService((ServiceReference)EasyMock.anyObject())).andReturn(doNothingFilter);
+ EasyMock.expect(bundleContext.getService((ServiceReference)EasyMock.anyObject())).andReturn(commitFilter);
+ EasyMock.replay(bundleContext);
+
+ // Set up the secure http context
+ httpContext = new SecurityDelegatingHttpContext(bundleContext, defaultHttpContext);
+
+ // Ensure that the httpContext returns false, since the filter has committed the response
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.replay(request);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.isCommitted()).andReturn(false); // doNothingFilter should not commit the response
+ EasyMock.expect(response.getWriter()).andReturn(new PrintWriter(System.out));
+ EasyMock.expect(response.isCommitted()).andReturn(false);
+ EasyMock.expect(response.isCommitted()).andReturn(true); // the commit filter indicating that it committed the
+ // response
+ EasyMock.replay(response);
+ Assert.assertFalse(httpContext.handleSecurity(request, response));
+
+ // Ensure that the appropriate filters were called
+ Assert.assertTrue(doNothingFilter.called);
+ Assert.assertTrue(commitFilter.called);
+ Assert.assertFalse(accessDeniedFilter.called);
+ }
+
+ public void testAllowRequest() throws Exception {
+ // Mock up the service references
+ ServiceReference filterReference = EasyMock.createNiceMock(ServiceReference.class);
+ EasyMock.replay(filterReference);
+ ServiceReference[] serviceReferences = new ServiceReference[] {
+ filterReference
+ };
+
+ // Mock up the bundle context
+ BundleContext bundleContext = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bundleContext.getServiceReferences((String)EasyMock.anyObject(), (String)EasyMock.anyObject()))
+ .andReturn(serviceReferences);
+ EasyMock.expect(bundleContext.getService((ServiceReference)EasyMock.anyObject())).andReturn(doNothingFilter);
+ EasyMock.replay(bundleContext);
+
+ // Set up the secure http context
+ httpContext = new SecurityDelegatingHttpContext(bundleContext, defaultHttpContext);
+
+ // Ensure that the httpContext returns true, since the filter has not committed the response
+ HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+ EasyMock.replay(request);
+ HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+ EasyMock.expect(response.isCommitted()).andReturn(false);
+ EasyMock.replay(response);
+ Assert.assertTrue(httpContext.handleSecurity(request, response));
+
+ // Ensure that the appropriate filters were called
+ Assert.assertTrue(doNothingFilter.called);
+ Assert.assertFalse(commitFilter.called);
+ Assert.assertFalse(accessDeniedFilter.called);
+ }
+
+ public void testDelegation() throws Exception {
+ BundleContext bundleContext = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.replay(bundleContext);
+
+ // Set up the secure http context
+ httpContext = new SecurityDelegatingHttpContext(bundleContext, defaultHttpContext);
+
+ // Ensure that it delegates non-security calls to the wrapped implementation (in this case, the mock)
+ Assert.assertEquals(mimeType, httpContext.getMimeType(""));
+ Assert.assertEquals(url, httpContext.getResource(""));
+ }
+}
+
+class CommitResponseFilter implements Filter {
+
+ boolean called;
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ public void destroy() {
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws java.io.IOException, javax.servlet.ServletException {
+ called = true;
+ response.getWriter().write("committing the response");
+ }
+}
+
+class DoNothingFilter implements Filter {
+
+ boolean called;
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ public void destroy() {
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws java.io.IOException, javax.servlet.ServletException {
+ called = true;
+ chain.doFilter(request, response);
+ }
+}
+
+class AccessDeniedFilter implements Filter {
+
+ boolean called;
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ public void destroy() {
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws java.io.IOException, javax.servlet.ServletException {
+ called = true;
+ ((HttpServletResponse)response).sendError(HttpServletResponse.SC_FORBIDDEN);
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/ServiceInvocationHandlerTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/ServiceInvocationHandlerTest.java
new file mode 100644
index 0000000..64beeac
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/ServiceInvocationHandlerTest.java
@@ -0,0 +1,75 @@
+/**
+ * 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.handlers;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+public class ServiceInvocationHandlerTest extends TestCase {
+
+ private static final Map<String, Method> OBJECT_METHODS = new HashMap<String, Method>(); {
+ for (Method m : Object.class.getMethods()) {
+ OBJECT_METHODS.put(m.getName(), m);
+ }
+ }
+
+ public void testInvoke() throws Throwable {
+ ServiceInvocationHandler sih = new ServiceInvocationHandler("hello", String.class);
+ Method m = String.class.getMethod("length", new Class[] {});
+ assertEquals(5, sih.invoke(null, m, new Object[] {}));
+ }
+
+ public void testInvokeObjectMethod() throws Throwable {
+ final List<String> called = new ArrayList<String>();
+ ServiceInvocationHandler sih = new ServiceInvocationHandler("hi", String.class) {
+ public boolean equals(Object obj) {
+ called.add("equals");
+ return super.equals(obj);
+ }
+
+ public int hashCode() {
+ called.add("hashCode");
+ return super.hashCode();
+ }
+
+ public String toString() {
+ called.add("toString");
+ return "somestring";
+ }
+ };
+
+ Object proxy = Proxy.newProxyInstance(
+ getClass().getClassLoader(), new Class[] {Runnable.class}, sih);
+
+ assertEquals(true,
+ sih.invoke(null, OBJECT_METHODS.get("equals"), new Object[] {proxy}));
+ assertEquals(System.identityHashCode(sih),
+ sih.invoke(null, OBJECT_METHODS.get("hashCode"), new Object[] {}));
+ assertEquals("somestring",
+ sih.invoke(null, OBJECT_METHODS.get("toString"), new Object[] {}));
+ assertEquals(Arrays.asList("equals", "hashCode", "toString"), called);
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/WsdlConfigurationTypeHandlerTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/WsdlConfigurationTypeHandlerTest.java
new file mode 100644
index 0000000..df90758
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/WsdlConfigurationTypeHandlerTest.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.cxf.dosgi.dsw.handlers;
+
+import junit.framework.TestCase;
+
+public class WsdlConfigurationTypeHandlerTest extends TestCase {
+
+ public void testDUMMY() {
+ assertTrue(true);
+ }
+
+// private Map<String, Object> handlerProps;
+//
+// @Override
+// protected void setUp() throws Exception {
+// super.setUp();
+//
+// handlerProps = new HashMap<String, Object>();
+// handlerProps.put(Constants.DEFAULT_HOST_CONFIG, "somehost");
+// handlerProps.put(Constants.DEFAULT_PORT_CONFIG, "54321");
+// }
+//
+// public void testCreateProxyPopulatesDistributionProvider() {
+// ServiceReference sr = EasyMock.createNiceMock(ServiceReference.class);
+// BundleContext dswContext = EasyMock.createNiceMock(BundleContext.class);
+// BundleContext callingContext = EasyMock.createNiceMock(BundleContext.class);
+// ServiceEndpointDescription sd = TestUtils.mockServiceDescription("Foo");
+// EasyMock.replay(sr);
+// EasyMock.replay(dswContext);
+// EasyMock.replay(callingContext);
+// EasyMock.replay(sd);
+//
+// RemoteServiceAdminCore dp = new RemoteServiceAdminCore(dswContext);
+// WsdlConfigurationTypeHandler w = new WsdlConfigurationTypeHandler(dswContext, dp, handlerProps) {
+// @Override
+// Service createWebService(URL wsdlAddress, QName serviceQname) {
+// Service svc = EasyMock.createMock(Service.class);
+// EasyMock.expect(svc.getPort(CharSequence.class)).andReturn("Hi").anyTimes();
+// EasyMock.replay(svc);
+// return svc;
+// }
+// };
+//
+// assertEquals("Precondition failed", 0, dp.getRemoteServices().size());
+// w.createProxy(sr, dswContext, callingContext, CharSequence.class, sd);
+// assertEquals(1, dp.getRemoteServices().size());
+// assertSame(sr, dp.getRemoteServices().iterator().next());
+//
+// }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/jaxws/MyJaxWsEchoService.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/jaxws/MyJaxWsEchoService.java
new file mode 100644
index 0000000..7814267
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/jaxws/MyJaxWsEchoService.java
@@ -0,0 +1,26 @@
+/**
+ * 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.handlers.jaxws;
+
+import javax.jws.WebService;
+
+@WebService
+public interface MyJaxWsEchoService {
+ String echo(String message);
+}
\ No newline at end of file
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/jaxws/MyJaxWsEchoServiceImpl.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/jaxws/MyJaxWsEchoServiceImpl.java
new file mode 100644
index 0000000..699c9ae
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/jaxws/MyJaxWsEchoServiceImpl.java
@@ -0,0 +1,27 @@
+/**
+ * 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.handlers.jaxws;
+
+public class MyJaxWsEchoServiceImpl implements MyJaxWsEchoService {
+
+ @Override
+ public String echo(String message) {
+ return message;
+ }
+}
\ No newline at end of file
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/simple/MySimpleEchoService.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/simple/MySimpleEchoService.java
new file mode 100644
index 0000000..7d574ca
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/simple/MySimpleEchoService.java
@@ -0,0 +1,23 @@
+/**
+ * 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.handlers.simple;
+
+public interface MySimpleEchoService {
+ String echo(String message);
+}
\ No newline at end of file
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/simple/MySimpleEchoServiceImpl.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/simple/MySimpleEchoServiceImpl.java
new file mode 100644
index 0000000..19dda4b
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/handlers/simple/MySimpleEchoServiceImpl.java
@@ -0,0 +1,27 @@
+/**
+ * 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.handlers.simple;
+
+public class MySimpleEchoServiceImpl implements MySimpleEchoService {
+
+ @Override
+ public String echo(String message) {
+ return message;
+ }
+}
\ No newline at end of file
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/hooks/CxfFindListenerHookTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/hooks/CxfFindListenerHookTest.java
new file mode 100644
index 0000000..fde48d2
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/hooks/CxfFindListenerHookTest.java
@@ -0,0 +1,341 @@
+/**
+ * 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.hooks;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CxfFindListenerHookTest extends Assert {
+
+ @Test
+ public void testDUMMY() throws Exception {
+ }
+
+// private IMocksControl control;
+//
+// @Before
+// public void setUp() {
+// control = EasyMock.createNiceControl();
+// }
+
+ /* Todo this test doesn't apply at the moment since the ListenerHook doesn't
+ * have a serviceReferencesRequested() API (yet).
+ @Test
+ public void testSyncListenerHook() throws Exception {
+ Bundle bundle = control.createMock(Bundle.class);
+ bundle.findEntries(EasyMock.eq("OSGI-INF/remote-service"),
+ EasyMock.eq("*.xml"), EasyMock.anyBoolean());
+ EasyMock.expectLastCall().andReturn(Collections.enumeration(
+ Arrays.asList(getClass().getResource("/OSGI-INF/remote-service/remote-services.xml"))));
+ Dictionary<String, String> bundleHeaders = new Hashtable<String, String>();
+ bundleHeaders.put(org.osgi.framework.Constants.BUNDLE_NAME,
+ "Test Bundle");
+ bundleHeaders.put(org.osgi.framework.Constants.BUNDLE_VERSION,
+ "1.0.0");
+ bundle.getHeaders();
+ EasyMock.expectLastCall().andReturn(bundleHeaders).anyTimes();
+ bundle.loadClass(TestService.class.getName());
+ EasyMock.expectLastCall().andReturn(TestService.class).anyTimes();
+ final BundleContext requestingContext = control.createMock(BundleContext.class);
+ requestingContext.getBundle();
+ EasyMock.expectLastCall().andReturn(bundle).anyTimes();
+
+ BundleTestContext dswContext = new BundleTestContext(bundle);
+ dswContext.addServiceReference(TestService.class.getName(),
+ control.createMock(ServiceReference.class));
+ control.replay();
+
+ CxfListenerHook hook = new CxfListenerHook(dswContext, null);
+
+ // TODO : if the next call ends up being executed in a thread of its own then
+ // update the test accordingly, use Futures for ex
+
+ hook.serviceReferencesRequested(requestingContext,
+ TestService.class.getName(), null, true);
+
+ List<ServiceReference> registeredRefs = dswContext.getRegisteredReferences();
+ assertNotNull(registeredRefs);
+ assertEquals(1, registeredRefs.size());
+ } */
+
+// @Test
+// public void testTrackerPropertiesOnlyClassInFilterWithMatchingInterface() throws Exception {
+// String filter = "(objectClass=" + TestService.class.getName() + ")";
+// doTestTrackerPropertiesSet(filter,
+// "osgi.remote.discovery.interest.interfaces",
+// TestService.class.getName(),
+// asList(TestService.class.getName()),
+// Collections.EMPTY_SET);
+// }
+//
+// @Test
+// public void testTrackerPropertiesGenericFilterWithMatchingInterface() throws Exception {
+// String filter = "(&(objectClass=" + TestService.class.getName()
+// + ")(colour=blue))";
+// doTestTrackerPropertiesSet(filter,
+// "osgi.remote.discovery.interest.filters",
+// replacePredicate(filter),
+// asList(TestService.class.getName()),
+// Collections.EMPTY_SET);
+// }
+//
+// @Test
+// public void testTrackerPropertiesOnlyClassInFilterWithMatchingFilter() throws Exception {
+// String filter = "(objectClass=" + TestService.class.getName() + ")";
+// doTestTrackerPropertiesSet(filter,
+// "osgi.remote.discovery.interest.interfaces",
+// TestService.class.getName(),
+// Collections.EMPTY_SET,
+// asList(replacePredicate(filter)));
+// }
+//
+// @Test
+// public void testTrackerPropertiesGenericFilterWithMatchingFilter() throws Exception {
+// String filter = "(&(objectClass=" + TestService.class.getName()
+// + ")(colour=blue))";
+// doTestTrackerPropertiesSet(filter,
+// "osgi.remote.discovery.interest.filters",
+// replacePredicate(filter),
+// Collections.EMPTY_SET,
+// asList(replacePredicate(filter)));
+// }
+//
+// @Test
+// public void testTrackerPropertiesOnlyClassInFilterWithMatchingBoth() throws Exception {
+// String filter = "(objectClass=" + TestService.class.getName() + ")";
+// doTestTrackerPropertiesSet(filter,
+// "osgi.remote.discovery.interest.interfaces",
+// TestService.class.getName(),
+// asList(TestService.class.getName()),
+// asList(replacePredicate(filter)));
+// }
+//
+// @Test
+// public void testTrackerPropertiesGenericFilterWithMatchingBoth() throws Exception {
+// String filter = "(&(objectClass=" + TestService.class.getName()
+// + ")(colour=blue))";
+// doTestTrackerPropertiesSet(filter,
+// "osgi.remote.discovery.interest.filters",
+// replacePredicate(filter),
+// Collections.EMPTY_SET,
+// asList(replacePredicate(filter)));
+// }
+//
+// private void doTestTrackerPropertiesSet(final String filter,
+// String propKey,
+// String propValue,
+// Collection matchingInterfaces,
+// Collection matchingFilters) throws Exception {
+// Bundle bundle = control.createMock(Bundle.class);
+// Dictionary<String, String> bundleHeaders = new Hashtable<String, String>();
+// bundleHeaders.put(org.osgi.framework.Constants.BUNDLE_NAME,
+// "Test Bundle");
+// bundleHeaders.put(org.osgi.framework.Constants.BUNDLE_VERSION,
+// "1.0.0");
+// bundle.getHeaders();
+// EasyMock.expectLastCall().andReturn(bundleHeaders).times(2);
+// final String serviceClass = TestService.class.getName();
+// bundle.loadClass(serviceClass);
+// EasyMock.expectLastCall().andReturn(TestService.class).times(2);
+// final BundleContext requestingContext = control.createMock(BundleContext.class);
+//
+// BundleTestContext dswContext = new BundleTestContext(bundle);
+// ServiceRegistration serviceRegistration = control.createMock(ServiceRegistration.class);
+// dswContext.addServiceRegistration(serviceClass, serviceRegistration);
+// serviceRegistration.unregister();
+// EasyMock.expectLastCall().times(1);
+// ServiceReference serviceReference = control.createMock(ServiceReference.class);
+// dswContext.addServiceReference(serviceClass, serviceReference);
+//
+// final String trackerClass = DiscoveredServiceTracker.class.getName();
+// ServiceRegistration trackerRegistration = control.createMock(ServiceRegistration.class);
+// dswContext.addServiceRegistration(trackerClass, trackerRegistration);
+// ServiceReference trackerReference = control.createMock(ServiceReference.class);
+// dswContext.addServiceReference(trackerClass, trackerReference);
+//
+// List property = asList(propValue);
+// Dictionary properties = new Hashtable();
+// properties.put(propKey, property);
+// trackerRegistration.setProperties(properties);
+// EasyMock.expectLastCall();
+//
+// if (matchingInterfaces.size() == 0 && matchingFilters.size() > 0) {
+// Iterator filters = matchingFilters.iterator();
+// while (filters.hasNext()) {
+// Filter f = control.createMock(Filter.class);
+// dswContext.addFilter((String)filters.next(), f);
+// f.match(EasyMock.isA(Dictionary.class));
+// EasyMock.expectLastCall().andReturn(true);
+// }
+// }
+//
+// control.replay();
+//
+// CxfFindListenerHook hook = new CxfFindListenerHook(dswContext, null);
+//
+// ListenerHook.ListenerInfo info = new ListenerHook.ListenerInfo() {
+// public BundleContext getBundleContext() {
+// return requestingContext;
+// }
+//
+// public String getFilter() {
+// return filter;
+// }
+//
+// public boolean isRemoved() {
+// return false;
+// }
+// };
+// hook.added(Collections.singleton(info));
+//
+// DiscoveredServiceTracker tracker = (DiscoveredServiceTracker)
+// dswContext.getService(trackerReference);
+// assertNotNull(tracker);
+//
+// Collection interfaces = asList(serviceClass);
+//
+// notifyAvailable(tracker, matchingInterfaces, matchingFilters, "1234");
+// notifyAvailable(tracker, matchingInterfaces, matchingFilters, "5678");
+// notifyAvailable(tracker, matchingInterfaces, matchingFilters, "1234");
+//
+// notifyUnAvailable(tracker, "1234");
+// notifyUnAvailable(tracker, "5678");
+//
+// notifyAvailable(tracker, matchingInterfaces, matchingFilters , "1234");
+//
+// control.verify();
+//
+// Map<String, ServiceReference> registeredRefs = dswContext.getRegisteredReferences();
+// assertNotNull(registeredRefs);
+// assertEquals(2, registeredRefs.size());
+// assertNotNull(registeredRefs.get(serviceClass));
+// assertSame(serviceReference, registeredRefs.get(serviceClass));
+//
+// Map<String, ServiceRegistration> registeredRegs = dswContext.getRegisteredRegistrations();
+// assertNotNull(registeredRegs);
+// assertEquals(2, registeredRegs.size());
+// assertNotNull(registeredRegs.get(trackerClass));
+// assertSame(trackerRegistration, registeredRegs.get(trackerClass));
+//
+// List<Object> registeredServices = dswContext.getRegisteredServices();
+// assertNotNull(registeredServices);
+// assertEquals(2, registeredServices.size());
+// }
+//
+// @Test
+// public void testConstructorAndGetters() {
+// BundleContext bc = control.createMock(BundleContext.class);
+// CxfRemoteServiceAdmin dp = control.createMock(CxfRemoteServiceAdmin.class);
+// control.replay();
+//
+// CxfFindListenerHook clh = new CxfFindListenerHook(bc, dp);
+// assertSame(bc, clh.getContext());
+// assertSame(dp, clh.getDistributionProvider());
+// }
+//
+// @Test
+// public void testFindHook() {
+// BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+//
+// final List<String> lookupCalls = new ArrayList<String>();
+// CxfFindListenerHook fh = new CxfFindListenerHook(bc, null) {
+// @Override
+// protected synchronized void lookupDiscoveryService(
+// String interfaceName, String filterValue) {
+// lookupCalls.add(interfaceName);
+// lookupCalls.add(filterValue);
+// }
+// };
+//
+// String clazz = "my.app.Class";
+// String filter = "&(A=B)(C=D)";
+// fh.find(null, clazz, filter, true, null);
+//
+// assertEquals(Arrays.asList(clazz, filter), lookupCalls);
+// }
+//
+// private void notifyAvailable(DiscoveredServiceTracker tracker,
+// Collection interfaces,
+// Collection filters,
+// String endpointId) {
+// Map<String, Object> props = new Hashtable<String, Object>();
+// props.put("osgi.remote.interfaces", "*");
+// props.put("osgi.remote.endpoint.id", endpointId);
+// tracker.serviceChanged(new Notification(AVAILABLE,
+// TestService.class.getName(),
+// interfaces,
+// filters,
+// props));
+// }
+//
+// private void notifyUnAvailable(DiscoveredServiceTracker tracker,
+// String endpointId) {
+// Map<String, Object> props = new Hashtable<String, Object>();
+// props.put("osgi.remote.endpoint.id", endpointId);
+// tracker.serviceChanged(new Notification(UNAVAILABLE,
+// TestService.class.getName(),
+// Collections.EMPTY_SET,
+// Collections.EMPTY_SET,
+// props));
+// }
+//
+// private List<String> asList(String s) {
+// List l = new ArrayList<String>();
+// l.add(s);
+// return l;
+// }
+//
+// private String replacePredicate(String filter) {
+// return filter.replace("objectClass", ServicePublication.SERVICE_INTERFACE_NAME);
+// }
+//
+// private class Notification implements DiscoveredServiceNotification {
+// private int type;
+// private ServiceEndpointDescription sed;
+// private Collection interfaces;
+// private Collection filters;
+//
+// Notification(int type,
+// String interfaceName,
+// Collection interfaces,
+// Collection filters,
+// Map<String, Object> props) {
+// this.type = type;
+// this.sed = new ServiceEndpointDescriptionImpl(interfaceName, props);
+// this.interfaces = interfaces;
+// this.filters = filters;
+// }
+//
+// public int getType() {
+// return type;
+// }
+//
+// public ServiceEndpointDescription getServiceEndpointDescription() {
+// return sed;
+// }
+//
+// public Collection getInterfaces() {
+// return interfaces;
+// }
+//
+// public Collection getFilters() {
+// return filters;
+// }
+// }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/hooks/CxfPublishHookTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/hooks/CxfPublishHookTest.java
new file mode 100644
index 0000000..40b40c5
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/hooks/CxfPublishHookTest.java
@@ -0,0 +1,299 @@
+/**
+ * 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.hooks;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CxfPublishHookTest extends Assert {
+
+ @Test
+ public void testDUMMY() throws Exception {
+ }
+
+ //
+ // private IMocksControl control;
+ //
+ // @Before
+ // public void setUp() {
+ // control = EasyMock.createNiceControl();
+ // }
+ //
+ // @Test
+ // public void testPublishSingleInterface() throws Exception {
+ // String[] serviceNames = new String[]{TestService.class.getName()};
+ // String[] addresses = new String[]{"http://localhost:9000/hello"};
+ // doTestPublishHook("remote-services.xml", serviceNames, addresses);
+ // }
+ //
+ // @Test
+ // public void testPublishSingleInterfaceAltFormat() throws Exception {
+ // String[] serviceNames = new String[]{TestService.class.getName()};
+ // String[] addresses = new String[]{"http://localhost:9000/hello"};
+ // doTestPublishHook("alt-remote-services.xml", serviceNames, addresses);
+ // }
+ //
+ // @Test
+ // public void testPublishMultiInterface() throws Exception {
+ // String[] serviceNames = new String[]{TestService.class.getName(),
+ // AdditionalInterface.class.getName()};
+ // String[] addresses = new String[]{"http://localhost:9001/hello",
+ // "http://localhost:9002/hello"};
+ // doTestPublishHook("multi-services.xml", serviceNames, addresses);
+ // }
+ //
+ // @SuppressWarnings("unchecked")
+ // private void doTestPublishHook(String remoteServices,
+ // String[] serviceNames,
+ // String[] addresses) throws Exception {
+ //
+ // Bundle bundle = control.createMock(Bundle.class);
+ // bundle.findEntries(EasyMock.eq("OSGI-INF/remote-service"),
+ // EasyMock.eq("*.xml"), EasyMock.anyBoolean());
+ // EasyMock.expectLastCall().andReturn(Collections.enumeration(
+ // Arrays.asList(getClass().getResource("/OSGI-INF/remote-service/" + remoteServices))));
+ // Dictionary<String, String> bundleHeaders = new Hashtable<String, String>();
+ // bundleHeaders.put(org.osgi.framework.Constants.BUNDLE_NAME,
+ // "Test Bundle");
+ // bundleHeaders.put(org.osgi.framework.Constants.BUNDLE_VERSION,
+ // "1.0.0");
+ // bundle.getHeaders();
+ // EasyMock.expectLastCall().andReturn(bundleHeaders).anyTimes();
+ // BundleContext requestingContext = control.createMock(BundleContext.class);
+ // bundle.getBundleContext();
+ // EasyMock.expectLastCall().andReturn(requestingContext).anyTimes();
+ //
+ // TestService serviceObject = new TestServiceImpl();
+ // Dictionary serviceProps = new Hashtable();
+ //
+ // ServiceReference sref = control.createMock(ServiceReference.class);
+ // sref.getBundle();
+ // EasyMock.expectLastCall().andReturn(bundle).anyTimes();
+ // sref.getProperty(Constants.OBJECTCLASS);
+ // EasyMock.expectLastCall().andReturn(serviceNames).anyTimes();
+ // sref.getPropertyKeys();
+ // EasyMock.expectLastCall().andReturn(new String[]{}).anyTimes();
+ //
+ // BundleTestContext dswContext = new BundleTestContext(bundle);
+ //
+ // ServiceRegistration[] serviceRegistrations =
+ // new ServiceRegistration[serviceNames.length];
+ //
+ // for (int i = 0; i < serviceNames.length ; i++) {
+ // serviceRegistrations[i] =
+ // control.createMock(ServiceRegistration.class);
+ // dswContext.addServiceRegistration(serviceNames[i],
+ // serviceRegistrations[i]);
+ // dswContext.addServiceReference(serviceNames[i], sref);
+ // }
+ // dswContext.registerService(serviceNames, serviceObject, serviceProps);
+ //
+ // Server server = control.createMock(Server.class);
+ //
+ // String publicationClass = ServicePublication.class.getName();
+ // ServiceRegistration publicationRegistration =
+ // control.createMock(ServiceRegistration.class);
+ // publicationRegistration.unregister();
+ // EasyMock.expectLastCall().times(serviceNames.length);
+ // dswContext.addServiceRegistration(publicationClass, publicationRegistration);
+ // ServiceReference publicationReference =
+ // control.createMock(ServiceReference.class);
+ // dswContext.addServiceReference(publicationClass, publicationReference);
+ // control.replay();
+ //
+ // TestPublishHook hook = new TestPublishHook(dswContext,
+ // serviceObject,
+ // server);
+ // hook.publishEndpoint(sref);
+ // hook.verify();
+ //
+ // assertEquals(1, hook.getEndpoints().size());
+ // List<EndpointInfo> list = hook.getEndpoints().get(sref);
+ // assertNotNull(list);
+ // assertEquals(serviceNames.length, list.size());
+ // for (int i = 0; i < serviceNames.length; i++) {
+ // assertNotNull(list.get(i));
+ // ServiceEndpointDescription sd = list.get(i).getServiceDescription();
+ // assertNotNull(sd);
+ // assertNotNull(sd.getProvidedInterfaces());
+ // assertEquals(1, sd.getProvidedInterfaces().size());
+ // Collection names = sd.getProvidedInterfaces();
+ // assertEquals(1, names.size());
+ // assertEquals(serviceNames[i], names.toArray()[0]);
+ // String excludeProp = "osgi.remote.interfaces";
+ // assertNull(sd.getProperties().get(excludeProp));
+ // String addrProp =
+ // org.apache.cxf.dosgi.dsw.Constants.WS_ADDRESS_PROPERTY_OLD;
+ // assertEquals(addresses[i], sd.getProperties().get(addrProp));
+ // }
+ //
+ // Map<String, ServiceRegistration> registeredRegs =
+ // dswContext.getRegisteredRegistrations();
+ // assertNotNull(registeredRegs);
+ // assertEquals(serviceNames.length + 1, registeredRegs.size());
+ // assertNotNull(registeredRegs.get(publicationClass));
+ // assertSame(publicationRegistration, registeredRegs.get(publicationClass));
+ //
+ // Map<String, List<Dictionary>> registeredProps =
+ // dswContext.getRegisteredProperties();
+ // assertNotNull(registeredProps);
+ // assertEquals(serviceNames.length + 1, registeredProps.size());
+ // assertNotNull(registeredProps.get(publicationClass));
+ // List<Dictionary> propsList = registeredProps.get(publicationClass);
+ // assertEquals(serviceNames.length, propsList.size());
+ // for (Dictionary props : propsList) {
+ // Collection interfaces =
+ // (Collection)props.get(SERVICE_INTERFACE_NAME);
+ // assertNotNull(interfaces);
+ // assertTrue(interfaces.contains(TestService.class.getName())
+ // || interfaces.contains(AdditionalInterface.class.getName()));
+ // }
+ //
+ // hook.removeEndpoints();
+ //
+ // control.verify();
+ // }
+ //
+ // @SuppressWarnings("unchecked")
+ // @Test
+ // public void testPublishMultipleTimes() {
+ // Bundle bundle = control.createMock(Bundle.class);
+ // bundle.findEntries(EasyMock.eq("OSGI-INF/remote-service"),
+ // EasyMock.eq("*.xml"), EasyMock.anyBoolean());
+ // EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+ // public Object answer() throws Throwable {
+ // return Collections.enumeration(Arrays.asList(
+ // getClass().getResource("/OSGI-INF/remote-service/remote-services.xml")));
+ // }
+ // }).anyTimes();
+ // Dictionary<String, String> bundleHeaders = new Hashtable<String, String>();
+ // bundleHeaders.put(org.osgi.framework.Constants.BUNDLE_NAME,
+ // "org.apache.cxf.example.bundle");
+ // bundleHeaders.put(org.osgi.framework.Constants.BUNDLE_VERSION,
+ // "1.0.0");
+ // bundle.getHeaders();
+ // EasyMock.expectLastCall().andReturn(bundleHeaders).anyTimes();
+ // BundleContext requestingContext = control.createMock(BundleContext.class);
+ // bundle.getBundleContext();
+ // EasyMock.expectLastCall().andReturn(requestingContext).anyTimes();
+ //
+ // TestService serviceObject = new TestServiceImpl();
+ // Dictionary serviceProps = new Hashtable();
+ //
+ // ServiceReference sref = control.createMock(ServiceReference.class);
+ // sref.getBundle();
+ // EasyMock.expectLastCall().andReturn(bundle).anyTimes();
+ // sref.getProperty(Constants.OBJECTCLASS);
+ // String[] serviceNames = {TestService.class.getName()};
+ // EasyMock.expectLastCall().andReturn(serviceNames).anyTimes();
+ // sref.getPropertyKeys();
+ // EasyMock.expectLastCall().andReturn(new String[]{}).anyTimes();
+ //
+ // BundleTestContext dswContext = new BundleTestContext(bundle);
+ // ServiceRegistration[] serviceRegistrations =
+ // new ServiceRegistration[serviceNames.length];
+ // for (int i = 0; i < serviceNames.length ; i++) {
+ // serviceRegistrations[i] =
+ // control.createMock(ServiceRegistration.class);
+ // dswContext.addServiceRegistration(serviceNames[i],
+ // serviceRegistrations[i]);
+ // dswContext.addServiceReference(serviceNames[i], sref);
+ // }
+ // dswContext.registerService(serviceNames, serviceObject, serviceProps);
+ //
+ // final Server server = control.createMock(Server.class);
+ // control.replay();
+ //
+ // CxfPublishHook hook = new CxfPublishHook(dswContext, null) {
+ // @Override
+ // Server createServer(ServiceReference sref, ServiceEndpointDescription sd) {
+ // return server;
+ // }
+ // };
+ // assertNull("Precondition not met", hook.getEndpoints().get(sref));
+ // hook.publishEndpoint(sref);
+ // assertEquals(1, hook.getEndpoints().get(sref).size());
+ //
+ // hook.endpoints.put(sref, new ArrayList<EndpointInfo>());
+ // assertEquals("Precondition failed", 0, hook.getEndpoints().get(sref).size());
+ // hook.publishEndpoint(sref);
+ // assertEquals(0, hook.getEndpoints().get(sref).size());
+ //
+ // control.verify();
+ // }
+ //
+ // private static class TestPublishHook extends CxfPublishHook {
+ //
+ // private boolean called;
+ // private TestService serviceObject;
+ // private Server server;
+ //
+ // public TestPublishHook(BundleContext bc, TestService serviceObject,
+ // Server s) {
+ // super(bc, null);
+ // this.serviceObject = serviceObject;
+ // this.server = s;
+ // }
+ //
+ // @Override
+ // protected ConfigurationTypeHandler getHandler(ServiceEndpointDescription sd,
+ // Map<String, Object> props) {
+ // return new ConfigurationTypeHandler() {
+ // public String getType() {
+ // return "test";
+ // }
+ //
+ // public Object createProxy(ServiceReference sr,
+ // BundleContext dswContext, BundleContext callingContext,
+ // Class<?> iClass, ServiceEndpointDescription sd) {
+ // throw new UnsupportedOperationException();
+ // }
+ //
+ // public Server createServer(ServiceReference sr,
+ // BundleContext dswContext, BundleContext callingContext,
+ // ServiceEndpointDescription sd, Class<?> iClass, Object serviceBean) {
+ // Assert.assertSame(serviceBean, serviceObject);
+ // TestPublishHook.this.setCalled();
+ // Map props = sd.getProperties();
+ // String address = (String)props.get(WS_ADDRESS_PROPERTY);
+ // if (address != null) {
+ // props.put(ENDPOINT_LOCATION, address);
+ // }
+ // return server;
+ // }
+ //
+ // };
+ // }
+ //
+ // public void setCalled() {
+ // called = true;
+ // }
+ //
+ // public void verify() {
+ // Assert.assertTrue(called);
+ // }
+ // }
+ //
+ // public interface AdditionalInterface {
+ // }
+ //
+ // private static class TestServiceImpl implements TestService, AdditionalInterface {
+ //
+ // }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/hooks/ServiceHookUtilsTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/hooks/ServiceHookUtilsTest.java
new file mode 100644
index 0000000..c004702
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/hooks/ServiceHookUtilsTest.java
@@ -0,0 +1,116 @@
+/**
+ * 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.hooks;
+
+import junit.framework.TestCase;
+
+public class ServiceHookUtilsTest extends TestCase {
+
+ public void testDUMMY() {
+ assertTrue(true);
+ }
+
+/*
+ public void testCreateServer() {
+ IMocksControl control = EasyMock.createNiceControl();
+
+ Server srvr = control.createMock(Server.class);
+ ServiceReference serviceReference = control.createMock(ServiceReference.class);
+ BundleContext dswContext = control.createMock(BundleContext.class);
+ BundleContext callingContext = control.createMock(BundleContext.class);
+ ServiceEndpointDescription sd = new ServiceEndpointDescriptionImpl("java.lang.String");
+ Object service = "hi";
+
+ ConfigurationTypeHandler handler = control.createMock(ConfigurationTypeHandler.class);
+ handler.createServer(serviceReference, dswContext, callingContext, sd, String.class, service);
+ EasyMock.expectLastCall().andReturn(srvr);
+ control.replay();
+
+ assertSame(srvr,
+ ServiceHookUtils.createServer(handler, serviceReference, dswContext, callingContext, sd, service));
+ }
+
+ public void testNoServerWhenNoInterfaceSpecified() {
+ IMocksControl control = EasyMock.createNiceControl();
+
+ Server srvr = control.createMock(Server.class);
+ ServiceReference serviceReference = control.createMock(ServiceReference.class);
+ BundleContext dswContext = control.createMock(BundleContext.class);
+ BundleContext callingContext = control.createMock(BundleContext.class);
+ ServiceEndpointDescription sd = mockServiceDescription(control, "Foo");
+ Object service = "hi";
+
+ ConfigurationTypeHandler handler = control.createMock(ConfigurationTypeHandler.class);
+ handler.createServer(serviceReference, dswContext, callingContext, sd, String.class, service);
+ EasyMock.expectLastCall().andReturn(srvr);
+ control.replay();
+
+ assertNull(ServiceHookUtils.createServer(handler, serviceReference, dswContext,
+ callingContext, sd, service));
+ }
+
+ public void testPublish() throws Exception {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put("foo", "bar");
+ props.put(ServicePublication.ENDPOINT_LOCATION, "http:localhost/xyz");
+ ServiceEndpointDescriptionImpl sed = new ServiceEndpointDescriptionImpl(String.class.getName(), props);
+ assertEquals(new URI("http:localhost/xyz"), sed.getLocation());
+
+ final Dictionary<String, Object> expectedProps = new Hashtable<String, Object>();
+ expectedProps.put(ServicePublication.SERVICE_PROPERTIES, props);
+ expectedProps.put(ServicePublication.SERVICE_INTERFACE_NAME, Collections.singleton(String.class.getName()));
+ expectedProps.put(ServicePublication.ENDPOINT_LOCATION, new URI("http:localhost/xyz"));
+
+ BundleContext bc = EasyMock.createMock(BundleContext.class);
+ EasyMock.expect(bc.registerService(
+ EasyMock.eq(ServicePublication.class.getName()),
+ EasyMock.anyObject(),
+ (Dictionary<?, ?>) EasyMock.anyObject()))
+ .andAnswer(new IAnswer<ServiceRegistration>() {
+ public ServiceRegistration answer() throws Throwable {
+ assertTrue(EasyMock.getCurrentArguments()[1] instanceof ServicePublication);
+ Dictionary<?, ?> actualProps =
+ (Dictionary<?, ?>) EasyMock.getCurrentArguments()[2];
+ UUID uuid = UUID.fromString(actualProps.get(ServicePublication.ENDPOINT_SERVICE_ID)
+ .toString());
+ expectedProps.put(ServicePublication.ENDPOINT_SERVICE_ID, uuid.toString());
+ assertEquals(expectedProps, actualProps);
+ return EasyMock.createMock(ServiceRegistration.class);
+ }
+ });
+ EasyMock.replay(bc);
+
+ ServiceHookUtils.publish(bc, null, sed);
+ EasyMock.verify(bc);
+ }
+
+ private ServiceEndpointDescription mockServiceDescription(IMocksControl control,
+ String... interfaceNames) {
+ List<String> iList = new ArrayList<String>();
+ for (String iName : interfaceNames) {
+ iList.add(iName);
+ }
+ ServiceEndpointDescription sd = control.createMock(ServiceEndpointDescription.class);
+ sd.getProvidedInterfaces();
+ EasyMock.expectLastCall().andReturn(iList);
+ return sd;
+ }
+*/
+}
+
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/hooks/TestService.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/hooks/TestService.java
new file mode 100644
index 0000000..07b5088
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/hooks/TestService.java
@@ -0,0 +1,22 @@
+/**
+ * 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.hooks;
+
+public interface TestService {
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/qos/IntentManagerImplTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/qos/IntentManagerImplTest.java
new file mode 100644
index 0000000..d7dad6f
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/qos/IntentManagerImplTest.java
@@ -0,0 +1,278 @@
+/**
+ * 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.qos;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.Assert;
+
+import org.apache.cxf.binding.BindingConfiguration;
+import org.apache.cxf.endpoint.AbstractEndpointFactory;
+import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.feature.Feature;
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+import org.junit.Test;
+
+public class IntentManagerImplTest extends Assert {
+
+ @Test
+ public void testIntents() throws Exception {
+ Map<String, Object> intents = new HashMap<String, Object>();
+ intents.put("A", new TestFeature("A"));
+ intents.put("SOAP", new TestFeature("SOAP"));
+ final IntentMap intentMap = new IntentMap(intents);
+
+ IMocksControl control = EasyMock.createNiceControl();
+ List<Feature> features = new ArrayList<Feature>();
+ AbstractEndpointFactory factory = control.createMock(AbstractEndpointFactory.class);
+ control.replay();
+
+ IntentManager intentManager = new IntentManagerImpl(intentMap, 10000);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put("osgi.remote.requires.intents", "A");
+
+ List<String> effectiveIntents = Arrays.asList(intentManager.applyIntents(features, factory, props));
+ assertEquals(Arrays.asList("A", "SOAP"), effectiveIntents);
+ }
+
+ @Test
+ public void testMultiIntents() {
+ final IntentMap intentMap = new IntentMap(new DefaultIntentMapFactory().create());
+ intentMap.put("confidentiality.message", new TestFeature("confidentiality.message"));
+ intentMap.put("transactionality", new TestFeature("transactionality"));
+
+ IMocksControl control = EasyMock.createNiceControl();
+ List<Feature> features = new ArrayList<Feature>();
+ AbstractEndpointFactory factory = control.createMock(AbstractEndpointFactory.class);
+ control.replay();
+
+ IntentManager intentManager = new IntentManagerImpl(intentMap);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put("osgi.remote.requires.intents", "transactionality confidentiality.message");
+
+ List<String> effectiveIntents = Arrays.asList(intentManager.applyIntents(features, factory, props));
+ assertTrue(effectiveIntents.contains("transactionality"));
+ assertTrue(effectiveIntents.contains("confidentiality.message"));
+ }
+
+ @Test
+ public void testFailedIntent() {
+ Map<String, Object> intents = new HashMap<String, Object>();
+ intents.put("A", new TestFeature("A"));
+ final IntentMap intentMap = new IntentMap(intents);
+
+ IMocksControl control = EasyMock.createNiceControl();
+ List<Feature> features = new ArrayList<Feature>();
+ AbstractEndpointFactory factory = control.createMock(AbstractEndpointFactory.class);
+ control.replay();
+
+ IntentManager intentManager = new IntentManagerImpl(intentMap);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put("osgi.remote.requires.intents", "A B");
+ // ServiceEndpointDescription sd =
+ // new ServiceEndpointDescriptionImpl(Arrays.asList(String.class.getName()), props);
+
+ try {
+ intentManager.applyIntents(features, factory, props);
+ Assert.fail("applyIntents() should have thrown an exception as there was an unsatisfiable intent");
+ } catch (IntentUnsatisfiedException iue) {
+ assertEquals("B", iue.getIntent());
+ }
+ }
+
+ @Test
+ public void testInferIntents() {
+ Map<String, Object> intents = new HashMap<String, Object>();
+ intents.put("SOAP", new TestFeature("SOAP"));
+ intents.put("Prov", "PROVIDED");
+ AbstractFeature feat1 = new TestFeature("feat1");
+ intents.put("A", feat1);
+ intents.put("A_alt", feat1);
+ intents.put("B", new TestFeature("B"));
+ final IntentMap intentMap = new IntentMap(intents);
+
+ IMocksControl control = EasyMock.createNiceControl();
+ List<Feature> features = new ArrayList<Feature>();
+ AbstractEndpointFactory factory = control.createMock(AbstractEndpointFactory.class);
+ control.replay();
+
+ IntentManager intentManager = new IntentManagerImpl(intentMap);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put("osgi.remote.requires.intents", "A");
+ // ServiceEndpointDescription sd =
+ // new ServiceEndpointDescriptionImpl(Arrays.asList(String.class.getName()), props);
+
+ List<String> effectiveIntents = Arrays.asList(intentManager.applyIntents(features, factory, props));
+ assertEquals(4, effectiveIntents.size());
+ assertTrue(effectiveIntents.contains("Prov"));
+ assertTrue(effectiveIntents.contains("A"));
+ assertTrue(effectiveIntents.contains("A_alt"));
+ }
+
+ @Test
+ public void testDefaultBindingIntent() {
+ IMocksControl control = EasyMock.createNiceControl();
+
+ Map<String, Object> intents = new HashMap<String, Object>();
+ BindingConfiguration feat1 = control.createMock(BindingConfiguration.class);
+ intents.put("A", new AbstractFeature() {
+ });
+ intents.put("SOAP", feat1);
+ intents.put("SOAP.1_1", feat1);
+ intents.put("SOAP.1_2", control.createMock(BindingConfiguration.class));
+ final IntentMap intentMap = new IntentMap(intents);
+
+ List<Feature> features = new ArrayList<Feature>();
+ AbstractEndpointFactory factory = control.createMock(AbstractEndpointFactory.class);
+ control.replay();
+ IntentManager intentManager = new IntentManagerImpl(intentMap);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put("osgi.remote.requires.intents", "A");
+ // ServiceEndpointDescription sd =
+ // new ServiceEndpointDescriptionImpl(Arrays.asList(String.class.getName()), props);
+
+ List<String> effectiveIntents = Arrays.asList(intentManager.applyIntents(features, factory, props));
+ assertEquals(3, effectiveIntents.size());
+ assertTrue(effectiveIntents.contains("A"));
+ assertTrue(effectiveIntents.contains("SOAP"));
+ assertTrue(effectiveIntents.contains("SOAP.1_1"));
+ }
+
+ @Test
+ public void testExplicitBindingIntent() {
+ IMocksControl control = EasyMock.createNiceControl();
+
+ Map<String, Object> intents = new HashMap<String, Object>();
+ BindingConfiguration feat1 = control.createMock(BindingConfiguration.class);
+ intents.put("A", new AbstractFeature() {
+ });
+ intents.put("SOAP", feat1);
+ intents.put("SOAP.1_1", feat1);
+ intents.put("SOAP.1_2", control.createMock(BindingConfiguration.class));
+ final IntentMap intentMap = new IntentMap(intents);
+
+ List<Feature> features = new ArrayList<Feature>();
+ AbstractEndpointFactory factory = control.createMock(AbstractEndpointFactory.class);
+ control.replay();
+
+ IntentManager intentManager = new IntentManagerImpl(intentMap);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put("osgi.remote.requires.intents", "A SOAP.1_2");
+ // ServiceEndpointDescription sd =
+ // new ServiceEndpointDescriptionImpl(Arrays.asList(String.class.getName()), props);
+
+ List<String> effectiveIntents = Arrays.asList(intentManager.applyIntents(features, factory, props));
+ assertEquals(2, effectiveIntents.size());
+ assertTrue(effectiveIntents.contains("A"));
+ assertTrue(effectiveIntents.contains("SOAP.1_2"));
+ }
+
+ public void testInheritMasterIntentMapDefault() {
+ List<String> features = runTestInheritMasterIntentMap("A B");
+
+ assertEquals(2, features.size());
+ assertTrue(features.contains("appFeatureA"));
+ assertTrue(features.contains("masterFeatureB"));
+ }
+
+ public void testInheritMasterIntentMap() {
+ List<String> features = runTestInheritMasterIntentMap("A B");
+
+ assertEquals(2, features.size());
+ assertTrue(features.contains("appFeatureA"));
+ assertTrue(features.contains("masterFeatureB"));
+ }
+
+ private List<String> runTestInheritMasterIntentMap(String requestedIntents) {
+ Map<String, Object> masterIntents = new HashMap<String, Object>();
+ masterIntents.put("A", new TestFeature("masterFeatureA"));
+ masterIntents.put("B", new TestFeature("masterFeatureB"));
+ final IntentMap intentMap = new IntentMap(masterIntents);
+ intentMap.put("A", new TestFeature("appFeatureA"));
+
+ IMocksControl control = EasyMock.createNiceControl();
+ List<Feature> features = new ArrayList<Feature>();
+ AbstractEndpointFactory factory = control.createMock(AbstractEndpointFactory.class);
+ control.replay();
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put("osgi.remote.requires.intents", requestedIntents);
+
+ IntentManagerImpl intentManager = new IntentManagerImpl(intentMap);
+ intentManager.applyIntents(features, factory, props);
+
+ List<String> featureNames = new ArrayList<String>();
+ for (Feature f : features) {
+ featureNames.add(f.toString());
+ }
+ return featureNames;
+ }
+
+ @Test
+ public void testProvidedIntents() {
+ Map<String, Object> masterIntents = new HashMap<String, Object>();
+ masterIntents.put("SOAP", "SOAP");
+ masterIntents.put("A", "Provided");
+ masterIntents.put("B", "PROVIDED");
+ final IntentMap intentMap = new IntentMap(masterIntents);
+
+ IMocksControl control = EasyMock.createNiceControl();
+ List<Feature> features = new ArrayList<Feature>();
+ AbstractEndpointFactory factory = control.createMock(AbstractEndpointFactory.class);
+ control.replay();
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put("osgi.remote.requires.intents", "B A");
+
+ IntentManager intentManager = new IntentManagerImpl(intentMap);
+
+ Set<String> effectiveIntents = new HashSet<String>(Arrays.asList(intentManager.applyIntents(features,
+ factory,
+ props)));
+ Set<String> expectedIntents = new HashSet<String>(Arrays.asList(new String[] {"A", "B", "SOAP"}));
+ assertEquals(expectedIntents, effectiveIntents);
+ }
+
+ private static final class TestFeature extends AbstractFeature {
+
+ private final String name;
+
+ private TestFeature(String n) {
+ name = n;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/qos/IntentMapTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/qos/IntentMapTest.java
new file mode 100644
index 0000000..aaa3f0e
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/qos/IntentMapTest.java
@@ -0,0 +1,42 @@
+/**
+ * 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.qos;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+public class IntentMapTest {
+
+ @Test
+ public void inheritanceTest() {
+ Map<String, Object> defaultMap = new HashMap<String, Object>();
+ defaultMap.put("key1", "defaultValue");
+ IntentMap intentMap = new IntentMap(defaultMap);
+ Assert.assertEquals("defaultValue", intentMap.get("key1"));
+ intentMap.put("key1", "overridden");
+ Assert.assertEquals("overridden", intentMap.get("key1"));
+ Object curValue = intentMap.remove("key1");
+ Assert.assertEquals("overridden", curValue);
+ Assert.assertEquals("defaultValue", intentMap.get("key1"));
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/qos/IntentTrackerTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/qos/IntentTrackerTest.java
new file mode 100644
index 0000000..74ecd21
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/qos/IntentTrackerTest.java
@@ -0,0 +1,81 @@
+/**
+ * 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.qos;
+
+import junit.framework.Assert;
+
+import org.apache.cxf.dosgi.dsw.Constants;
+import org.apache.cxf.feature.AbstractFeature;
+import org.easymock.Capture;
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+import static org.easymock.EasyMock.expect;
+
+public class IntentTrackerTest {
+
+ private static final String MY_INTENT_NAME = "myIntent";
+
+ @Test
+ public void testIntentAsService() throws InvalidSyntaxException {
+ IMocksControl c = EasyMock.createControl();
+ BundleContext bc = c.createMock(BundleContext.class);
+ Filter filter = c.createMock(Filter.class);
+ expect(bc.createFilter(EasyMock.<String>anyObject())).andReturn(filter);
+ final Capture<ServiceListener> capturedListener = new Capture<ServiceListener>();
+ bc.addServiceListener(EasyMock.capture(capturedListener), EasyMock.<String>anyObject());
+ EasyMock.expectLastCall().atLeastOnce();
+ expect(bc.getServiceReferences(EasyMock.<String>anyObject(),
+ EasyMock.<String>anyObject())).andReturn(new ServiceReference[]{});
+ IntentMap intentMap = new IntentMap();
+
+ // Create a custom intent
+ @SuppressWarnings("unchecked")
+ ServiceReference<AbstractFeature> reference = c.createMock(ServiceReference.class);
+ expect(reference.getProperty(Constants.INTENT_NAME_PROP)).andReturn(MY_INTENT_NAME);
+ AbstractFeature testIntent = new AbstractFeature() {
+ };
+ expect(bc.getService(reference)).andReturn(testIntent).atLeastOnce();
+
+ c.replay();
+
+ IntentTracker tracker = new IntentTracker(bc, intentMap);
+ tracker.open();
+
+ Assert.assertFalse("IntentMap should not contain " + MY_INTENT_NAME, intentMap.containsKey(MY_INTENT_NAME));
+ ServiceListener listener = capturedListener.getValue();
+
+ // Simulate adding custom intent service
+ ServiceEvent event = new ServiceEvent(ServiceEvent.REGISTERED, reference);
+ listener.serviceChanged(event);
+
+ // our custom intent should now be available
+ Assert.assertTrue("IntentMap should contain " + MY_INTENT_NAME, intentMap.containsKey(MY_INTENT_NAME));
+ Assert.assertEquals(testIntent, intentMap.get(MY_INTENT_NAME));
+
+ c.verify();
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/qos/IntentUtilsTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/qos/IntentUtilsTest.java
new file mode 100644
index 0000000..92c157a
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/qos/IntentUtilsTest.java
@@ -0,0 +1,69 @@
+/**
+ * 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.qos;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cxf.dosgi.dsw.Constants;
+import org.junit.Assert;
+import org.junit.Test;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+public class IntentUtilsTest {
+
+ @Test
+ public void testMergeArrays() {
+ Assert.assertNull(IntentUtils.mergeArrays(null, null));
+
+ String[] sa1 = {};
+ Assert.assertEquals(0, IntentUtils.mergeArrays(sa1, null).length);
+
+ String[] sa2 = {"X"};
+ Assert.assertEquals(1, IntentUtils.mergeArrays(null, sa2).length);
+ Assert.assertEquals("X", IntentUtils.mergeArrays(null, sa2)[0]);
+
+ String[] sa3 = {"Y", "Z"};
+ String[] sa4 = {"A", "Z"};
+ Assert.assertEquals(3, IntentUtils.mergeArrays(sa3, sa4).length);
+ Assert.assertEquals(new HashSet<String>(Arrays.asList("A", "Y", "Z")),
+ new HashSet<String>(Arrays.asList(IntentUtils.mergeArrays(sa3, sa4))));
+ }
+
+ @Test
+ public void testRequestedIntents() {
+ Map<String, Object> props = new HashMap<String, Object>();
+ Assert.assertEquals(0, IntentUtils.getRequestedIntents(props).size());
+
+ props.put(RemoteConstants.SERVICE_EXPORTED_INTENTS, "one");
+ Assert.assertEquals(Collections.singleton("one"), IntentUtils.getRequestedIntents(props));
+
+ props.put(RemoteConstants.SERVICE_EXPORTED_INTENTS_EXTRA, new String[] {"two", "three"});
+ Set<String> expected1 = new HashSet<String>(Arrays.asList("one", "two", "three"));
+ Assert.assertEquals(expected1, IntentUtils.getRequestedIntents(props));
+
+ props.put(Constants.EXPORTED_INTENTS_OLD, "A B C");
+ Set<String> expected2 = new HashSet<String>(Arrays.asList("one", "two", "three", "A", "B", "C"));
+ Assert.assertEquals(expected2, IntentUtils.getRequestedIntents(props));
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderImplTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderImplTest.java
new file mode 100644
index 0000000..f3cf46d
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/DistributionProviderImplTest.java
@@ -0,0 +1,124 @@
+/**
+ * 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 junit.framework.TestCase;
+
+public class DistributionProviderImplTest extends TestCase {
+
+ public void testDUMMY() {
+ assertTrue(true);
+ }
+
+// public void testExposedServices() {
+// BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+// EasyMock.replay(bc);
+// RemoteServiceAdminCore dp = new RemoteServiceAdminCore(bc);
+//
+// assertEquals(0, dp.getExposedServices().size());
+// assertEquals(0, dp.getRemoteServices().size());
+// ServiceReference sr = new TestServiceReference();
+// ServiceReference sr2 = new TestServiceReference();
+//
+// dp.addExposedService(sr, null);
+// assertEquals(1, dp.getExposedServices().size());
+// assertEquals(0, dp.getRemoteServices().size());
+// assertSame(sr, dp.getExposedServices().iterator().next());
+//
+// dp.addExposedService(sr, null);
+// assertEquals(1, dp.getExposedServices().size());
+// assertEquals(0, dp.getRemoteServices().size());
+// assertSame(sr, dp.getExposedServices().iterator().next());
+//
+// dp.addExposedService(sr2, null);
+// assertEquals(2, dp.getExposedServices().size());
+// assertEquals(0, dp.getRemoteServices().size());
+// }
+//
+// public void testRemoteServices() {
+// BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+// EasyMock.replay(bc);
+// RemoteServiceAdminCore dp = new RemoteServiceAdminCore(bc);
+//
+// assertEquals(0, dp.getExposedServices().size());
+// assertEquals(0, dp.getRemoteServices().size());
+// ServiceReference sr = new TestServiceReference();
+// ServiceReference sr2 = new TestServiceReference();
+//
+// dp.addRemoteService(sr);
+// assertEquals(0, dp.getExposedServices().size());
+// assertEquals(1, dp.getRemoteServices().size());
+// assertSame(sr, dp.getRemoteServices().iterator().next());
+//
+// dp.addRemoteService(sr);
+// assertEquals(0, dp.getExposedServices().size());
+// assertEquals(1, dp.getRemoteServices().size());
+// assertSame(sr, dp.getRemoteServices().iterator().next());
+//
+// dp.addRemoteService(sr2);
+// assertEquals(0, dp.getExposedServices().size());
+// assertEquals(2, dp.getRemoteServices().size());
+// }
+//
+// public void testPublicationProperties() {
+// BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+// EasyMock.replay(bc);
+//
+// RemoteServiceAdminCore dp = new RemoteServiceAdminCore(bc);
+// ServiceReference sr = new TestServiceReference();
+// ServiceReference sr2 = new TestServiceReference();
+//
+// assertNull(dp.getExposedProperties(sr));
+//
+// dp.addExposedService(sr, null);
+// Map<String, String> pp = new HashMap<String, String>();
+// pp.put("a", "b");
+// dp.addExposedService(sr2, pp);
+//
+// assertEquals(0, dp.getExposedProperties(sr).size());
+// assertEquals(pp, dp.getExposedProperties(sr2));
+// }
+//
+// private static class TestServiceReference implements ServiceReference {
+
+// public Bundle getBundle() {
+// return null;
+// }
+//
+// public Object getProperty(String arg0) {
+// return null;
+// }
+//
+// public String[] getPropertyKeys() {
+// return null;
+// }
+//
+// public Bundle[] getUsingBundles() {
+// return null;
+// }
+//
+// public boolean isAssignableTo(Bundle arg0, String arg1) {
+// return false;
+// }
+//
+// public int compareTo(Object o) {
+// return 0;
+// }
+// }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/EventProducerTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/EventProducerTest.java
new file mode 100644
index 0000000..d1c21e8
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/EventProducerTest.java
@@ -0,0 +1,184 @@
+/**
+ * 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.Arrays;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.cxf.endpoint.Server;
+import org.easymock.IAnswer;
+import org.easymock.classextension.EasyMock;
+import org.junit.Assert;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+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.ExportReference;
+import org.osgi.service.remoteserviceadmin.ExportRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent;
+
+public class EventProducerTest {
+
+ @Test
+ public void testPublishNotification() throws Exception {
+ RemoteServiceAdminCore remoteServiceAdminCore = EasyMock.createNiceMock(RemoteServiceAdminCore.class);
+ EasyMock.replay(remoteServiceAdminCore);
+
+ final EndpointDescription endpoint = EasyMock.createNiceMock(EndpointDescription.class);
+ EasyMock.expect(endpoint.getServiceId()).andReturn(Long.MAX_VALUE).anyTimes();
+ final String uuid = UUID.randomUUID().toString();
+ EasyMock.expect(endpoint.getFrameworkUUID()).andReturn(uuid).anyTimes();
+ EasyMock.expect(endpoint.getId()).andReturn("foo://bar").anyTimes();
+ final List<String> interfaces = Arrays.asList("org.foo.Bar", "org.boo.Far");
+ EasyMock.expect(endpoint.getInterfaces()).andReturn(interfaces).anyTimes();
+ EasyMock.expect(endpoint.getConfigurationTypes()).andReturn(Arrays.asList("org.apache.cxf.ws")).anyTimes();
+ EasyMock.replay(endpoint);
+ final ServiceReference sref = EasyMock.createNiceMock(ServiceReference.class);
+ EasyMock.replay(sref);
+
+ final Bundle bundle = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(bundle.getBundleId()).andReturn(42L).anyTimes();
+ EasyMock.expect(bundle.getSymbolicName()).andReturn("test.bundle").anyTimes();
+ Dictionary<String, String> headers = new Hashtable<String, String>();
+ headers.put("Bundle-Version", "1.2.3.test");
+ EasyMock.expect(bundle.getHeaders()).andReturn(headers).anyTimes();
+ EasyMock.replay(bundle);
+
+ EventAdmin ea = EasyMock.createNiceMock(EventAdmin.class);
+ ea.postEvent((Event) EasyMock.anyObject());
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+ @Override
+ public Object answer() throws Throwable {
+ Event event = (Event) EasyMock.getCurrentArguments()[0];
+
+ Assert.assertEquals("org/osgi/service/remoteserviceadmin/EXPORT_REGISTRATION", event.getTopic());
+ Assert.assertSame(bundle, event.getProperty("bundle"));
+ Assert.assertEquals(42L, event.getProperty("bundle.id"));
+ Assert.assertEquals("test.bundle", event.getProperty("bundle.symbolicname"));
+ Assert.assertEquals(new Version(1, 2, 3, "test"), event.getProperty("bundle.version"));
+ Assert.assertNull(event.getProperty("cause"));
+ Assert.assertEquals(endpoint, event.getProperty("export.registration"));
+
+ Assert.assertEquals(Long.MAX_VALUE, event.getProperty("service.remote.id"));
+ Assert.assertEquals(uuid, event.getProperty("service.remote.uuid"));
+ Assert.assertEquals("foo://bar", event.getProperty("service.remote.uri"));
+ Assert.assertTrue(Arrays.equals(interfaces.toArray(new String[] {}),
+ (String[]) event.getProperty("objectClass")));
+
+ Assert.assertNotNull(event.getProperty("timestamp"));
+
+ RemoteServiceAdminEvent rsae = (RemoteServiceAdminEvent) event.getProperty("event");
+ Assert.assertNull(rsae.getException());
+ Assert.assertEquals(RemoteServiceAdminEvent.EXPORT_REGISTRATION, rsae.getType());
+ Assert.assertSame(bundle, rsae.getSource());
+ ExportReference er = rsae.getExportReference();
+ Assert.assertSame(endpoint, er.getExportedEndpoint());
+ Assert.assertSame(sref, er.getExportedService());
+
+ return null;
+ }
+ });
+ EasyMock.replay(ea);
+
+ ServiceReference eaSref = EasyMock.createNiceMock(ServiceReference.class);
+ EasyMock.replay(eaSref);
+
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.getBundle()).andReturn(bundle).anyTimes();
+ EasyMock.expect(bc.getAllServiceReferences(EventAdmin.class.getName(), null))
+ .andReturn(new ServiceReference[] {eaSref}).anyTimes();
+ EasyMock.expect(bc.getService(eaSref)).andReturn(ea).anyTimes();
+ EasyMock.replay(bc);
+ EventProducer eventProducer = new EventProducer(bc);
+
+ ExportRegistrationImpl ereg = new ExportRegistrationImpl(sref, endpoint, remoteServiceAdminCore, (Server)null);
+ eventProducer.publishNotification(ereg);
+ }
+
+ @Test
+ public void testPublishErrorNotification() throws Exception {
+ RemoteServiceAdminCore rsaCore = EasyMock.createNiceMock(RemoteServiceAdminCore.class);
+ EasyMock.replay(rsaCore);
+
+ final EndpointDescription endpoint = EasyMock.createNiceMock(EndpointDescription.class);
+ EasyMock.expect(endpoint.getInterfaces()).andReturn(Arrays.asList("org.foo.Bar")).anyTimes();
+ EasyMock.replay(endpoint);
+ final ServiceReference sref = EasyMock.createNiceMock(ServiceReference.class);
+ EasyMock.replay(sref);
+
+ final Bundle bundle = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(bundle.getBundleId()).andReturn(42L).anyTimes();
+ EasyMock.expect(bundle.getSymbolicName()).andReturn("test.bundle").anyTimes();
+ EasyMock.expect(bundle.getHeaders()).andReturn(new Hashtable<String, String>()).anyTimes();
+ EasyMock.replay(bundle);
+
+ final Exception exportException = new Exception();
+
+ EventAdmin ea = EasyMock.createNiceMock(EventAdmin.class);
+ ea.postEvent((Event) EasyMock.anyObject());
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+ @Override
+ public Object answer() throws Throwable {
+ Event event = (Event) EasyMock.getCurrentArguments()[0];
+
+ Assert.assertEquals("org/osgi/service/remoteserviceadmin/EXPORT_ERROR", event.getTopic());
+ Assert.assertSame(bundle, event.getProperty("bundle"));
+ Assert.assertEquals(42L, event.getProperty("bundle.id"));
+ Assert.assertEquals("test.bundle", event.getProperty("bundle.symbolicname"));
+ Assert.assertEquals(new Version("0"), event.getProperty("bundle.version"));
+ Assert.assertSame(exportException, event.getProperty("cause"));
+ Assert.assertEquals(endpoint, event.getProperty("export.registration"));
+ Assert.assertTrue(Arrays.equals(new String[] {"org.foo.Bar"},
+ (String[]) event.getProperty("objectClass")));
+
+ RemoteServiceAdminEvent rsae = (RemoteServiceAdminEvent) event.getProperty("event");
+ Assert.assertSame(exportException, rsae.getException());
+ Assert.assertEquals(RemoteServiceAdminEvent.EXPORT_ERROR, rsae.getType());
+ Assert.assertSame(bundle, rsae.getSource());
+ ExportReference er = rsae.getExportReference();
+ Assert.assertSame(endpoint, er.getExportedEndpoint());
+ Assert.assertSame(sref, er.getExportedService());
+
+ return null;
+ }
+ });
+ EasyMock.replay(ea);
+
+ ServiceReference eaSref = EasyMock.createNiceMock(ServiceReference.class);
+ EasyMock.replay(eaSref);
+
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.getBundle()).andReturn(bundle).anyTimes();
+ EasyMock.expect(bc.getAllServiceReferences(EventAdmin.class.getName(), null))
+ .andReturn(new ServiceReference[] {eaSref}).anyTimes();
+ EasyMock.expect(bc.getService(eaSref)).andReturn(ea).anyTimes();
+ EasyMock.replay(bc);
+ EventProducer eventProducer = new EventProducer(bc);
+
+ ExportRegistrationImpl ereg = new ExportRegistrationImpl(sref, endpoint, rsaCore, exportException);
+ eventProducer.publishNotification(Arrays.<ExportRegistration>asList(ereg));
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImplTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImplTest.java
new file mode 100644
index 0000000..95dfe9a
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/ImportRegistrationImplTest.java
@@ -0,0 +1,177 @@
+/**
+ * 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.easymock.IMocksControl;
+import org.easymock.classextension.EasyMock;
+import org.junit.Test;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class ImportRegistrationImplTest {
+
+ @Test
+ public void testException() {
+ IMocksControl c = EasyMock.createNiceControl();
+ Exception e = c.createMock(Exception.class);
+ c.replay();
+
+ ImportRegistrationImpl i = new ImportRegistrationImpl(e);
+
+ assertEquals(e, i.getException());
+ assertNull(i.getImportedEndpointDescription());
+ assertNull(i.getImportedService());
+ assertEquals(i, i.getParent());
+ }
+
+ @Test
+ public void testDefaultCtor() {
+ IMocksControl c = EasyMock.createNiceControl();
+ EndpointDescription endpoint = c.createMock(EndpointDescription.class);
+ RemoteServiceAdminCore rsac = c.createMock(RemoteServiceAdminCore.class);
+
+ c.replay();
+
+ ImportRegistrationImpl i = new ImportRegistrationImpl(endpoint, rsac);
+
+ assertNull(i.getException());
+ assertEquals(i, i.getParent());
+ assertEquals(endpoint, i.getImportedEndpointDescription());
+ }
+
+ @Test
+ public void testCloneAndClose() {
+ IMocksControl c = EasyMock.createControl();
+ EndpointDescription endpoint = c.createMock(EndpointDescription.class);
+ RemoteServiceAdminCore rsac = c.createMock(RemoteServiceAdminCore.class);
+
+ ServiceRegistration sr = c.createMock(ServiceRegistration.class);
+ ServiceReference sref = c.createMock(ServiceReference.class);
+ EasyMock.expect(sr.getReference()).andReturn(sref).anyTimes();
+
+ c.replay();
+
+ ImportRegistrationImpl i1 = new ImportRegistrationImpl(endpoint, rsac);
+
+ ImportRegistrationImpl i2 = new ImportRegistrationImpl(i1);
+
+ ImportRegistrationImpl i3 = new ImportRegistrationImpl(i2);
+
+ try {
+ i2.setImportedServiceRegistration(sr);
+ assertTrue("An exception should be thrown here !", false);
+ } catch (IllegalStateException e) {
+ // must be thrown here
+ }
+
+ i1.setImportedServiceRegistration(sr);
+
+ assertEquals(i1, i1.getParent());
+ assertEquals(i1, i2.getParent());
+ assertEquals(i1, i3.getParent());
+
+ assertEquals(endpoint, i1.getImportedEndpointDescription());
+ assertEquals(endpoint, i2.getImportedEndpointDescription());
+ assertEquals(endpoint, i3.getImportedEndpointDescription());
+
+ c.verify();
+ c.reset();
+
+ rsac.removeImportRegistration(EasyMock.eq(i3));
+ EasyMock.expectLastCall().once();
+
+ c.replay();
+
+ i3.close();
+ i3.close(); // shouldn't change anything
+
+ assertNull(i3.getImportedEndpointDescription());
+
+ c.verify();
+ c.reset();
+
+ rsac.removeImportRegistration(EasyMock.eq(i1));
+ EasyMock.expectLastCall().once();
+
+ c.replay();
+
+ i1.close();
+
+ c.verify();
+ c.reset();
+
+ rsac.removeImportRegistration(EasyMock.eq(i2));
+ EasyMock.expectLastCall().once();
+
+ sr.unregister();
+ EasyMock.expectLastCall().once();
+
+ c.replay();
+
+ i2.close();
+
+ c.verify();
+ }
+
+ @Test
+ public void testCloseAll() {
+ IMocksControl c = EasyMock.createControl();
+ EndpointDescription endpoint = c.createMock(EndpointDescription.class);
+ RemoteServiceAdminCore rsac = c.createMock(RemoteServiceAdminCore.class);
+
+ c.replay();
+
+ ImportRegistrationImpl i1 = new ImportRegistrationImpl(endpoint, rsac);
+
+ ImportRegistrationImpl i2 = new ImportRegistrationImpl(i1);
+
+ ImportRegistrationImpl i3 = new ImportRegistrationImpl(i2);
+
+ assertEquals(i1, i1.getParent());
+ assertEquals(i1, i2.getParent());
+ assertEquals(i1, i3.getParent());
+
+ c.verify();
+ c.reset();
+
+ rsac.removeImportRegistration(EasyMock.eq(i2));
+ EasyMock.expectLastCall().once();
+
+ c.replay();
+
+ i2.close();
+
+ c.verify();
+ c.reset();
+
+ rsac.removeImportRegistration(EasyMock.eq(i1));
+ EasyMock.expectLastCall().once();
+ rsac.removeImportRegistration(EasyMock.eq(i3));
+ EasyMock.expectLastCall().once();
+
+ c.replay();
+ i3.closeAll();
+ c.verify();
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCoreTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCoreTest.java
new file mode 100644
index 0000000..dcc6002
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/RemoteServiceAdminCoreTest.java
@@ -0,0 +1,366 @@
+/**
+ * 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.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.dsw.handlers.ConfigTypeHandlerFactory;
+import org.apache.cxf.dosgi.dsw.handlers.ConfigurationTypeHandler;
+import org.apache.cxf.dosgi.dsw.handlers.ExportResult;
+import org.apache.cxf.dosgi.dsw.handlers.HttpServiceManager;
+import org.apache.cxf.dosgi.dsw.qos.DefaultIntentMapFactory;
+import org.apache.cxf.dosgi.dsw.qos.IntentManager;
+import org.apache.cxf.dosgi.dsw.qos.IntentManagerImpl;
+import org.apache.cxf.dosgi.dsw.qos.IntentMap;
+import org.apache.cxf.endpoint.Server;
+import org.easymock.IAnswer;
+import org.easymock.IMocksControl;
+import org.easymock.classextension.EasyMock;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+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;
+import org.osgi.service.remoteserviceadmin.ExportRegistration;
+import org.osgi.service.remoteserviceadmin.ImportRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+public class RemoteServiceAdminCoreTest {
+
+ @Test
+ public void testDontExportOwnServiceProxies() throws InvalidSyntaxException {
+ IMocksControl c = EasyMock.createControl();
+ Bundle b = c.createMock(Bundle.class);
+ BundleContext bc = c.createMock(BundleContext.class);
+
+ EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes();
+ bc.addServiceListener(EasyMock.<ServiceListener>anyObject(), EasyMock.<String>anyObject());
+ EasyMock.expectLastCall().anyTimes();
+ bc.removeServiceListener(EasyMock.<ServiceListener>anyObject());
+ EasyMock.expectLastCall().anyTimes();
+
+ Dictionary<String, String> d = new Hashtable<String, String>();
+ EasyMock.expect(b.getHeaders()).andReturn(d).anyTimes();
+
+ ServiceReference sref = c.createMock(ServiceReference.class);
+ EasyMock.expect(sref.getBundle()).andReturn(b).anyTimes();
+ EasyMock.expect(sref.getPropertyKeys())
+ .andReturn(new String[]{"objectClass", "service.exported.interfaces"}).anyTimes();
+ EasyMock.expect(sref.getProperty("objectClass")).andReturn(new String[] {"a.b.C"}).anyTimes();
+ EasyMock.expect(sref.getProperty("service.exported.interfaces")).andReturn("*").anyTimes();
+
+ ConfigTypeHandlerFactory configTypeHandlerFactory = c.createMock(ConfigTypeHandlerFactory.class);
+
+ c.replay();
+
+ RemoteServiceAdminCore rsaCore = new RemoteServiceAdminCore(bc, configTypeHandlerFactory);
+
+ // must return an empty List as sref if from the same bundle
+ List<ExportRegistration> exRefs = rsaCore.exportService(sref, null);
+
+ assertNotNull(exRefs);
+ assertEquals(0, exRefs.size());
+
+ // must be empty
+ assertEquals(rsaCore.getExportedServices().size(), 0);
+
+ c.verify();
+ }
+
+ @Test
+ public void testImport() {
+ IMocksControl c = EasyMock.createNiceControl();
+ Bundle b = c.createMock(Bundle.class);
+ BundleContext bc = c.createMock(BundleContext.class);
+
+ Dictionary<String, String> d = new Hashtable<String, String>();
+ EasyMock.expect(b.getHeaders()).andReturn(d).anyTimes();
+
+ EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes();
+ EasyMock.expect(b.getSymbolicName()).andReturn("BundleName").anyTimes();
+
+ Map<String, Object> p = new HashMap<String, Object>();
+ p.put(RemoteConstants.ENDPOINT_ID, "http://google.de");
+ p.put(Constants.OBJECTCLASS, new String[] {
+ "es.schaaf.my.class"
+ });
+ p.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, "unsupportedConfiguration");
+ EndpointDescription endpoint = new EndpointDescription(p);
+ IntentMap intentMap = new IntentMap(new DefaultIntentMapFactory().create());
+ IntentManager intentManager = new IntentManagerImpl(intentMap, 10000);
+ HttpServiceManager httpServiceManager = c.createMock(HttpServiceManager.class);
+ ConfigTypeHandlerFactory configTypeHandlerFactory
+ = new ConfigTypeHandlerFactory(bc, intentManager, httpServiceManager);
+
+ c.replay();
+
+ RemoteServiceAdminCore rsaCore = new RemoteServiceAdminCore(bc, configTypeHandlerFactory) {
+ @Override
+ protected void proxifyMatchingInterface(String interfaceName, ImportRegistrationImpl imReg,
+ ConfigurationTypeHandler handler,
+ BundleContext requestingContext) {
+ }
+ };
+
+ // must be null as the endpoint doesn't contain any usable configurations
+ assertNull(rsaCore.importService(endpoint));
+ // must be empty
+ assertEquals(0, rsaCore.getImportedEndpoints().size());
+
+ p.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, org.apache.cxf.dosgi.dsw.Constants.WS_CONFIG_TYPE);
+ endpoint = new EndpointDescription(p);
+
+ ImportRegistration ireg = rsaCore.importService(endpoint);
+ assertNotNull(ireg);
+
+ assertEquals(1, rsaCore.getImportedEndpoints().size());
+
+ // lets import the same endpoint once more -> should get a copy of the ImportRegistration
+ ImportRegistration ireg2 = rsaCore.importService(endpoint);
+ assertNotNull(ireg2);
+ assertEquals(2, rsaCore.getImportedEndpoints().size());
+
+ assertEquals(ireg.getImportReference(), (rsaCore.getImportedEndpoints().toArray())[0]);
+
+ assertEquals(ireg.getImportReference().getImportedEndpoint(), ireg2.getImportReference()
+ .getImportedEndpoint());
+
+ // remove the registration
+
+ // first call shouldn't remove the import
+ ireg2.close();
+ assertEquals(1, rsaCore.getImportedEndpoints().size());
+
+ // second call should really close and remove the import
+ ireg.close();
+ assertEquals(0, rsaCore.getImportedEndpoints().size());
+
+ c.verify();
+ }
+
+ @Test
+ public void testExport() throws Exception {
+ BundleContext bc = EasyMock.createMock(BundleContext.class);
+ EasyMock.expect(bc.getProperty(Constants.FRAMEWORK_VERSION)).andReturn(null).anyTimes();
+ bc.addServiceListener(EasyMock.<ServiceListener>anyObject(), EasyMock.<String>anyObject());
+ EasyMock.expectLastCall().anyTimes();
+ bc.removeServiceListener(EasyMock.<ServiceListener>anyObject());
+ EasyMock.expectLastCall().anyTimes();
+ EasyMock.expect(bc.getServiceReferences(EasyMock.<String>anyObject(),
+ EasyMock.<String>anyObject())).andReturn(null).anyTimes();
+ EasyMock.expect(bc.getAllServiceReferences(EasyMock.<String>anyObject(),
+ EasyMock.<String>anyObject())).andReturn(null).anyTimes();
+
+ Bundle b = createDummyRsaBundle(bc);
+
+ final Map<String, Object> sProps = new HashMap<String, Object>();
+ sProps.put("objectClass", new String[] {"java.lang.Runnable"});
+ sProps.put("service.id", 51L);
+ sProps.put("myProp", "myVal");
+ sProps.put("service.exported.interfaces", "*");
+ ServiceReference sref = mockServiceReference(sProps);
+
+ Runnable svcObject = EasyMock.createNiceMock(Runnable.class);
+ EasyMock.replay(svcObject);
+
+ EasyMock.expect(bc.getService(sref)).andReturn(svcObject).anyTimes();
+ EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes();
+ EasyMock.expect(bc.createFilter("(service.id=51)"))
+ .andReturn(FrameworkUtil.createFilter("(service.id=51)")).anyTimes();
+ EasyMock.replay(bc);
+
+ Map<String, Object> eProps = new HashMap<String, Object>(sProps);
+ eProps.put("endpoint.id", "http://something");
+ eProps.put("service.imported.configs", new String[] {"org.apache.cxf.ws"});
+ ExportResult er = new ExportResult(eProps, (Server) null);
+
+ ConfigurationTypeHandler handler = EasyMock.createNiceMock(ConfigurationTypeHandler.class);
+ EasyMock.expect(handler.createServer(sref,
+ bc,
+ sref.getBundle().getBundleContext(),
+ sProps, Runnable.class, svcObject)).andReturn(er).once();
+ EasyMock.replay(handler);
+
+ ConfigTypeHandlerFactory handlerFactory = EasyMock.createNiceMock(ConfigTypeHandlerFactory.class);
+ EasyMock.expect(handlerFactory.getHandler(bc, sProps))
+ .andReturn(handler).once(); // Second time shouldn't get there because it should simply copy
+ EasyMock.replay(handlerFactory);
+ RemoteServiceAdminCore rsaCore = new RemoteServiceAdminCore(bc, handlerFactory);
+
+ // Export the service for the first time
+ List<ExportRegistration> ereg = rsaCore.exportService(sref, null);
+ assertEquals(1, ereg.size());
+ assertNull(ereg.get(0).getException());
+ assertSame(sref, ereg.get(0).getExportReference().getExportedService());
+ EndpointDescription endpoint = ereg.get(0).getExportReference().getExportedEndpoint();
+
+ Map<String, Object> edProps = endpoint.getProperties();
+ assertEquals("http://something", edProps.get("endpoint.id"));
+ assertNotNull(edProps.get("service.imported"));
+ assertTrue(Arrays.equals(new String[] {"java.lang.Runnable"},
+ (Object[]) edProps.get("objectClass")));
+ assertTrue(Arrays.equals(new String[] {"org.apache.cxf.ws"},
+ (Object[]) edProps.get("service.imported.configs")));
+
+ // Ask to export the same service again, this should not go through the whole process again but simply return
+ // a copy of the first instance.
+ final Map<String, Object> sProps2 = new HashMap<String, Object>();
+ sProps2.put("objectClass", new String[] {"java.lang.Runnable"});
+ sProps2.put("service.id", 51L);
+ sProps2.put("service.exported.interfaces", "*");
+ ServiceReference sref2 = mockServiceReference(sProps2);
+ Map<String, Object> props2 = new HashMap<String, Object>();
+ props2.put("myProp", "myVal");
+ List<ExportRegistration> ereg2 = rsaCore.exportService(sref2, props2);
+
+ assertEquals(1, ereg2.size());
+ assertNull(ereg2.get(0).getException());
+ assertEquals(ereg.get(0).getExportReference().getExportedEndpoint().getProperties(),
+ ereg2.get(0).getExportReference().getExportedEndpoint().getProperties());
+
+ // Look at the exportedServices data structure
+ Field field = RemoteServiceAdminCore.class.getDeclaredField("exportedServices");
+ field.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Map<Map<String, Object>, Collection<ExportRegistration>> exportedServices =
+ (Map<Map<String, Object>, Collection<ExportRegistration>>) field.get(rsaCore);
+
+ assertEquals("One service was exported", 1, exportedServices.size());
+ assertEquals("There are 2 export registrations (identical copies)",
+ 2, exportedServices.values().iterator().next().size());
+
+ // Unregister one of the exports
+ rsaCore.removeExportRegistration((ExportRegistrationImpl) ereg.get(0));
+ assertEquals("One service was exported", 1, exportedServices.size());
+ assertEquals("There 1 export registrations left",
+ 1, exportedServices.values().iterator().next().size());
+
+ // Unregister the other export
+ rsaCore.removeExportRegistration((ExportRegistrationImpl) ereg2.get(0));
+ assertEquals("No more exported services", 0, exportedServices.size());
+ }
+
+ private Bundle createDummyRsaBundle(BundleContext bc) {
+ Bundle b = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(b.getBundleContext()).andReturn(bc).anyTimes();
+ EasyMock.expect(b.getSymbolicName()).andReturn("rsabundle").anyTimes();
+ EasyMock.expect(b.getHeaders()).andReturn(new Hashtable<String, String>()).anyTimes();
+ EasyMock.replay(b);
+ return b;
+ }
+
+ @Test
+ public void testExportException() throws Exception {
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+
+ Bundle b = createDummyRsaBundle(bc);
+
+ final Map<String, Object> sProps = new HashMap<String, Object>();
+ sProps.put("objectClass", new String[] {"java.lang.Runnable"});
+ sProps.put("service.id", 51L);
+ sProps.put("service.exported.interfaces", "*");
+ ServiceReference sref = mockServiceReference(sProps);
+
+ Runnable svcObject = EasyMock.createNiceMock(Runnable.class);
+ EasyMock.replay(svcObject);
+
+ EasyMock.expect(bc.getService(sref)).andReturn(svcObject).anyTimes();
+ EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes();
+ EasyMock.replay(bc);
+
+ Map<String, Object> eProps = new HashMap<String, Object>(sProps);
+ eProps.put("endpoint.id", "http://something");
+ eProps.put("service.imported.configs", new String[] {"org.apache.cxf.ws"});
+ ExportResult er = new ExportResult(eProps, new TestException());
+
+ ConfigurationTypeHandler handler = EasyMock.createNiceMock(ConfigurationTypeHandler.class);
+ EasyMock.expect(handler.createServer(sref, bc, sref.getBundle().getBundleContext(),
+ sProps, Runnable.class, svcObject)).andReturn(er);
+ EasyMock.replay(handler);
+
+ ConfigTypeHandlerFactory handlerFactory = EasyMock.createNiceMock(ConfigTypeHandlerFactory.class);
+ EasyMock.expect(handlerFactory.getHandler(bc, sProps)).andReturn(handler).anyTimes();
+ EasyMock.replay(handlerFactory);
+ RemoteServiceAdminCore rsaCore = new RemoteServiceAdminCore(bc, handlerFactory);
+
+ List<ExportRegistration> ereg = rsaCore.exportService(sref, null);
+ assertEquals(1, ereg.size());
+ assertTrue(ereg.get(0).getException() instanceof TestException);
+ assertSame(sref, ereg.get(0).getExportReference().getExportedService());
+
+ // Look at the exportedServices data structure
+ Field field = RemoteServiceAdminCore.class.getDeclaredField("exportedServices");
+ field.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Map<Map<String, Object>, Collection<ExportRegistration>> exportedServices =
+ (Map<Map<String, Object>, Collection<ExportRegistration>>) field.get(rsaCore);
+
+ assertEquals("One service was exported", 1, exportedServices.size());
+ assertEquals("There is 1 export registration",
+ 1, exportedServices.values().iterator().next().size());
+
+ // Remove all export registrations from the service bundle
+ rsaCore.removeExportRegistrations(sref.getBundle());
+ assertEquals("No more exported services", 0, exportedServices.size());
+ }
+
+ private ServiceReference mockServiceReference(final Map<String, Object> sProps) {
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+
+ Bundle b = EasyMock.createNiceMock(Bundle.class);
+ EasyMock.expect(b.getBundleContext()).andReturn(bc).anyTimes();
+ EasyMock.replay(b);
+
+ EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes();
+ EasyMock.replay(bc);
+
+ ServiceReference sref = EasyMock.createNiceMock(ServiceReference.class);
+ EasyMock.expect(sref.getBundle()).andReturn(b).anyTimes();
+ EasyMock.expect(sref.getPropertyKeys()).andReturn(sProps.keySet().toArray(new String[] {})).anyTimes();
+ EasyMock.expect(sref.getProperty((String) EasyMock.anyObject())).andAnswer(new IAnswer<Object>() {
+ @Override
+ public Object answer() throws Throwable {
+ return sProps.get(EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.replay(sref);
+ return sref;
+ }
+
+ @SuppressWarnings("serial")
+ private static class TestException extends Exception {
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/UtilsTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/UtilsTest.java
new file mode 100644
index 0000000..1ceebe8
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/service/UtilsTest.java
@@ -0,0 +1,167 @@
+/**
+ * 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.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.dsw.util.OsgiUtils;
+import org.apache.cxf.dosgi.dsw.util.Utils;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class UtilsTest {
+
+ @Test
+ public void testSplitString() {
+ String[] values = Utils.normalizeStringPlus("1, 2");
+ assertEquals(2, values.length);
+ assertEquals(values[0], "1");
+ assertEquals(values[1], "2");
+ }
+
+ @Test
+ public void testNormalizeStringPlus() {
+ String s1 = "s1";
+ String s2 = "s2";
+ String s3 = "s3";
+
+ String[] sa = new String[] {
+ s1, s2, s3
+ };
+
+ Collection<Object> sl = new ArrayList<Object>(4);
+ sl.add(s1);
+ sl.add(s2);
+ sl.add(s3);
+ sl.add(new Object()); // must be skipped
+
+ assertArrayEquals(null, Utils.normalizeStringPlus(new Object()));
+ assertArrayEquals(new String[] {
+ s1
+ }, Utils.normalizeStringPlus(s1));
+ assertArrayEquals(sa, Utils.normalizeStringPlus(sa));
+ assertArrayEquals(sa, Utils.normalizeStringPlus(sl));
+ }
+
+ @Test
+ public void testOverlayProperties() {
+ Map<String, Object> original = new HashMap<String, Object>();
+
+ original.put("MyProp", "my value");
+ original.put(Constants.OBJECTCLASS, "myClass");
+
+ Map<String, Object> copy = new HashMap<String, Object>();
+ copy.putAll(original);
+
+ // nothing should change here
+ Map<String, Object> overload = new HashMap<String, Object>();
+ OsgiUtils.overlayProperties(copy, overload);
+
+ assertEquals(original.size(), copy.size());
+ for (Object key : original.keySet()) {
+ assertEquals(original.get(key), copy.get(key));
+ }
+
+ copy.clear();
+ copy.putAll(original);
+
+ // a property should be added
+ overload = new HashMap<String, Object>();
+ overload.put("new", "prop");
+
+ OsgiUtils.overlayProperties(copy, overload);
+
+ assertEquals(original.size() + 1, copy.size());
+ for (Object key : original.keySet()) {
+ assertEquals(original.get(key), copy.get(key));
+ }
+ assertNotNull(overload.get("new"));
+ assertEquals("prop", overload.get("new"));
+
+ copy.clear();
+ copy.putAll(original);
+
+ // only one property should be added
+ overload = new HashMap<String, Object>();
+ overload.put("new", "prop");
+ overload.put("NEW", "prop");
+
+ OsgiUtils.overlayProperties(copy, overload);
+
+ assertEquals(original.size() + 1, copy.size());
+ for (Object key : original.keySet()) {
+ assertEquals(original.get(key), copy.get(key));
+ }
+ assertNotNull(overload.get("new"));
+ assertEquals("prop", overload.get("new"));
+
+ copy.clear();
+ copy.putAll(original);
+
+ // nothing should change here
+ overload = new HashMap<String, Object>();
+ overload.put(Constants.OBJECTCLASS, "assd");
+ overload.put(Constants.SERVICE_ID, "asasdasd");
+ OsgiUtils.overlayProperties(copy, overload);
+
+ assertEquals(original.size(), copy.size());
+ for (Object key : original.keySet()) {
+ assertEquals(original.get(key), copy.get(key));
+ }
+
+ copy.clear();
+ copy.putAll(original);
+
+ // overwrite own prop
+ overload = new HashMap<String, Object>();
+ overload.put("MyProp", "newValue");
+ OsgiUtils.overlayProperties(copy, overload);
+
+ assertEquals(original.size(), copy.size());
+ for (Object key : original.keySet()) {
+ if (!"MyProp".equals(key)) {
+ assertEquals(original.get(key), copy.get(key));
+ }
+ }
+ assertEquals("newValue", copy.get("MyProp"));
+
+ copy.clear();
+ copy.putAll(original);
+
+ // overwrite own prop in different case
+ overload = new HashMap<String, Object>();
+ overload.put("MYPROP", "newValue");
+ OsgiUtils.overlayProperties(copy, overload);
+
+ assertEquals(original.size(), copy.size());
+ for (Object key : original.keySet()) {
+ if (!"MyProp".equals(key)) {
+ assertEquals(original.get(key), copy.get(key));
+ }
+ }
+ assertEquals("newValue", copy.get("MyProp"));
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/util/ClassUtilsTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/util/ClassUtilsTest.java
new file mode 100644
index 0000000..e920d75
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/util/ClassUtilsTest.java
@@ -0,0 +1,133 @@
+/**
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.easymock.classextension.EasyMock;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+public class ClassUtilsTest extends TestCase {
+
+ public void testGetInterfaceClass() {
+ assertEquals(String.class,
+ ClassUtils.getInterfaceClass("Hello", "java.lang.String"));
+ assertNull(ClassUtils.getInterfaceClass("Hello", "java.lang.Integer"));
+ assertEquals(List.class, ClassUtils.getInterfaceClass(
+ new ArrayList<String>(), "java.util.List"));
+ assertEquals(Collection.class, ClassUtils.getInterfaceClass(
+ new ArrayList<String>(), "java.util.Collection"));
+ }
+
+ public void testGetInterfaceClassFromSubclass() {
+ assertEquals(Map.class, ClassUtils.getInterfaceClass(
+ new MySubclassFour(), "java.util.Map"));
+ assertNull(ClassUtils.getInterfaceClass(new MySubclassFour(),
+ "java.util.UnknownType"));
+ }
+
+ public void testLoadProvidersAsString() throws Exception {
+ BundleContext bc = mockBundleContext();
+ Map<String, Object> sd = Collections.<String, Object>singletonMap("providers", Provider.class.getName());
+ List<Object> providers = ClassUtils.loadProviderClasses(bc, sd, "providers");
+ assertEquals(1, providers.size());
+ assertTrue(providers.get(0) instanceof Provider);
+ }
+
+ public void testLoadProvidersAsStringArray() throws Exception {
+ BundleContext bc = mockBundleContext();
+ Map<String, Object> sd = Collections.<String, Object>singletonMap("providers",
+ new String[]{Provider.class.getName()});
+ List<Object> providers = ClassUtils.loadProviderClasses(bc, sd, "providers");
+ assertEquals(1, providers.size());
+ assertTrue(providers.get(0) instanceof Provider);
+ }
+
+ public void testLoadProvidersAsObject() throws Exception {
+ Map<String, Object> sd = Collections.<String, Object>singletonMap("providers", new Provider());
+ List<Object> providers = ClassUtils.loadProviderClasses(null, sd, "providers");
+ assertEquals(1, providers.size());
+ assertTrue(providers.get(0) instanceof Provider);
+ }
+
+ public void testLoadProvidersAsObjectArray() throws Exception {
+ Map<String, Object> sd = Collections.<String, Object>singletonMap("providers", new Object[]{new Provider()});
+ List<Object> providers = ClassUtils.loadProviderClasses(null, sd, "providers");
+ assertEquals(1, providers.size());
+ assertTrue(providers.get(0) instanceof Provider);
+ }
+
+ public void testLoadProvidersAsObjectList() throws Exception {
+ List<Object> list = new LinkedList<Object>();
+ list.add(new Provider());
+ Map<String, Object> sd = Collections.<String, Object>singletonMap("providers", list);
+ List<Object> providers = ClassUtils.loadProviderClasses(null, sd, "providers");
+ assertEquals(1, providers.size());
+ assertTrue(providers.get(0) instanceof Provider);
+ }
+
+ public void testLoadProvidersAsStringList() throws Exception {
+ List<Object> list = new LinkedList<Object>();
+ list.add(Provider.class.getName());
+ Map<String, Object> sd = Collections.<String, Object>singletonMap("providers", list);
+ List<Object> providers = ClassUtils.loadProviderClasses(mockBundleContext(), sd, "providers");
+ assertEquals(1, providers.size());
+ assertTrue(providers.get(0) instanceof Provider);
+ }
+
+ private BundleContext mockBundleContext() throws Exception {
+ BundleContext bc = EasyMock.createMock(BundleContext.class);
+ Bundle bundle = EasyMock.createMock(Bundle.class);
+ bc.getBundle();
+ EasyMock.expectLastCall().andReturn(bundle);
+ bundle.loadClass(Provider.class.getName());
+ EasyMock.expectLastCall().andReturn(Provider.class);
+ EasyMock.replay(bc, bundle);
+ return bc;
+ }
+
+ @SuppressWarnings({ "serial", "rawtypes" })
+ private static class MyMapSubclass extends HashMap {
+ }
+
+ @SuppressWarnings("serial")
+ static class MySubclassOne extends MyMapSubclass {
+ }
+
+ @SuppressWarnings("serial")
+ static class MySubclassTwo extends MySubclassOne {
+ }
+
+ @SuppressWarnings("serial")
+ static class MySubclassThree extends MySubclassTwo {
+ }
+
+ @SuppressWarnings("serial")
+ static class MySubclassFour extends MySubclassThree {
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/util/OsgiUtilsTest.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/util/OsgiUtilsTest.java
new file mode 100644
index 0000000..3bcb096
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/util/OsgiUtilsTest.java
@@ -0,0 +1,192 @@
+/**
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+public class OsgiUtilsTest extends TestCase {
+
+ public void testMultiValuePropertyAsString() {
+ assertEquals(Collections.singleton("hi"),
+ OsgiUtils.getMultiValueProperty("hi"));
+ }
+
+ public void testMultiValuePropertyAsArray() {
+ assertEquals(Arrays.asList("a", "b"),
+ OsgiUtils.getMultiValueProperty(new String[] {"a", "b"}));
+ }
+
+ public void testMultiValuePropertyAsCollection() {
+ List<String> list = new ArrayList<String>();
+ list.add("1");
+ list.add("2");
+ list.add("3");
+ assertEquals(list, OsgiUtils.getMultiValueProperty(list));
+ }
+
+ public void testMultiValuePropertyNull() {
+ assertNull(OsgiUtils.getMultiValueProperty(null));
+ }
+
+ public void testGetUUID() {
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.getProperty(EasyMock.eq("org.osgi.framework.uuid"))).andReturn(null).atLeastOnce();
+ EasyMock.replay(bc);
+ String uuid = OsgiUtils.getUUID(bc);
+ assertNotNull(uuid);
+
+ assertEquals(System.getProperty("org.osgi.framework.uuid"), uuid);
+
+ EasyMock.verify(bc);
+ }
+
+ public void testGetVersion() {
+ IMocksControl c = EasyMock.createNiceControl();
+ BundleContext bc = c.createMock(BundleContext.class);
+ ServiceReference sref = c.createMock(ServiceReference.class);
+ PackageAdmin pa = c.createMock(PackageAdmin.class);
+ Bundle b = c.createMock(Bundle.class);
+
+ EasyMock.expect(bc.getServiceReference(EasyMock.eq(PackageAdmin.class.getName()))).andReturn(sref);
+ EasyMock.expect(bc.getService(EasyMock.eq(sref))).andReturn(pa);
+
+ Class<?> iClass = CharSequence.class;
+
+ c.replay();
+ // version 0.0.0 because of missing bundle
+
+ assertEquals("0.0.0", OsgiUtils.getVersion(iClass, bc));
+
+ c.verify();
+ c.reset();
+ // version 1.2.3
+
+ EasyMock.expect(bc.getServiceReference(EasyMock.eq(PackageAdmin.class.getName()))).andReturn(sref);
+ EasyMock.expect(bc.getService(EasyMock.eq(sref))).andReturn(pa);
+ EasyMock.expect(pa.getBundle(EasyMock.eq(iClass))).andReturn(b);
+
+ ExportedPackage[] exP = new ExportedPackage[] {new MyExportedPackage(iClass.getPackage(), "1.2.3"),
+ new MyExportedPackage(String.class.getPackage(), "4.5.6") };
+
+ EasyMock.expect(pa.getExportedPackages(EasyMock.eq(b))).andReturn(exP).atLeastOnce();
+
+ c.replay();
+ assertEquals("1.2.3", OsgiUtils.getVersion(iClass, bc));
+ c.verify();
+ }
+
+ public void testOverlayProperties() {
+ Map<String, Object> sProps = new HashMap<String, Object>();
+ Map<String, Object> aProps = new HashMap<String, Object>();
+
+ OsgiUtils.overlayProperties(sProps, aProps);
+ assertEquals(0, sProps.size());
+
+ sProps.put("aaa", "aval");
+ sProps.put("bbb", "bval");
+ sProps.put(Constants.OBJECTCLASS, new String[] {"X"});
+ sProps.put(Constants.SERVICE_ID, 17L);
+
+ aProps.put("AAA", "achanged");
+ aProps.put("CCC", "CVAL");
+ aProps.put(Constants.OBJECTCLASS, new String[] {"Y"});
+ aProps.put(Constants.SERVICE_ID.toUpperCase(), 51L);
+
+ Map<String, Object> aPropsOrg = new HashMap<String, Object>(aProps);
+ OsgiUtils.overlayProperties(sProps, aProps);
+ assertEquals("The additional properties should not be modified", aPropsOrg, aProps);
+
+ assertEquals(5, sProps.size());
+ assertEquals("achanged", sProps.get("aaa"));
+ assertEquals("bval", sProps.get("bbb"));
+ assertEquals("CVAL", sProps.get("CCC"));
+ assertTrue("Should not be possible to override the objectClass property",
+ Arrays.equals(new String[] {"X"}, (Object[]) sProps.get(Constants.OBJECTCLASS)));
+ assertEquals("Should not be possible to override the service.id property",
+ 17L, sProps.get(Constants.SERVICE_ID));
+ }
+
+ private static class MyExportedPackage implements ExportedPackage {
+
+ Package package1;
+ String version;
+
+ public MyExportedPackage(Package package1, String version) {
+ this.package1 = package1;
+ this.version = version;
+ }
+
+ public Bundle getExportingBundle() {
+ return null;
+ }
+
+ public Bundle[] getImportingBundles() {
+ return null;
+ }
+
+ public String getName() {
+ return package1.getName();
+ }
+
+ public String getSpecificationVersion() {
+ return null;
+ }
+
+ public Version getVersion() {
+ return new Version(version);
+ }
+
+ public boolean isRemovalPending() {
+ return false;
+ }
+ }
+
+ public void testGetProperty() {
+ Map<String, Object> p = new HashMap<String, Object>();
+ p.put(RemoteConstants.ENDPOINT_ID, "http://google.de");
+ p.put("notAString", new Object());
+ p.put(org.osgi.framework.Constants.OBJECTCLASS, new String[]{"my.class"});
+ p.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, new String[]{"my.config"});
+
+ EndpointDescription endpoint = new EndpointDescription(p);
+
+ assertNull(OsgiUtils.getProperty(endpoint, "unknownProp"));
+ assertEquals(p.get(RemoteConstants.ENDPOINT_ID), OsgiUtils.getProperty(endpoint, RemoteConstants.ENDPOINT_ID));
+ assertEquals(null, OsgiUtils.getProperty(endpoint, "notAString"));
+ }
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/util/Provider.java b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/util/Provider.java
new file mode 100644
index 0000000..ecd7c78
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/java/org/apache/cxf/dosgi/dsw/util/Provider.java
@@ -0,0 +1,23 @@
+/**
+ * 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.util;
+
+
+public class Provider {
+}
diff --git a/trunk/dsw/cxf-dsw/src/test/resources/OSGI-INF/remote-service/alt-remote-services.xml b/trunk/dsw/cxf-dsw/src/test/resources/OSGI-INF/remote-service/alt-remote-services.xml
new file mode 100644
index 0000000..36409ce
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/resources/OSGI-INF/remote-service/alt-remote-services.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
+ <service-description>
+ <provide interface="org.apache.cxf.dosgi.dsw.hooks.TestService" />
+ <provide interface="org.apache.cxf.dosgi.dsw.hooks.CxfPublishHookTest$AdditionalInterface" />
+
+ <property name="osgi.remote.interfaces" value="*" />
+ <property name="osgi.remote.requires.intents" value="SOAP HTTP" />
+ <property name="osgi.remote.configuration.type" value="pojo" />
+ <property name="osgi.remote.configuration.pojo.address" value="http://localhost:9000/hello" />
+ </service-description>
+</service-descriptions>
diff --git a/trunk/dsw/cxf-dsw/src/test/resources/OSGI-INF/remote-service/multi-services.xml b/trunk/dsw/cxf-dsw/src/test/resources/OSGI-INF/remote-service/multi-services.xml
new file mode 100644
index 0000000..3d20c08
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/resources/OSGI-INF/remote-service/multi-services.xml
@@ -0,0 +1,38 @@
+<?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.
+-->
+
+<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
+ <service-description>
+ <provide interface="org.apache.cxf.dosgi.dsw.hooks.TestService" />
+ <provide interface="org.apache.cxf.dosgi.dsw.hooks.CxfPublishHookTest$AdditionalInterface" />
+ <property name="osgi.remote.interfaces">org.apache.cxf.dosgi.dsw.hooks.TestService,org.apache.cxf.dosgi.dsw.hooks.CxfPublishHookTest$AdditionalInterface</property>
+ <property name="osgi.remote.requires.intents">SOAP HTTP</property>
+ <property name="osgi.remote.configuration.type">pojo</property>
+ <property name="osgi.remote.configuration.pojo.address"
+ interface="org.apache.cxf.dosgi.dsw.hooks.TestService">
+ http://localhost:9001/hello
+ </property>
+ <property name="osgi.remote.configuration.pojo.address"
+ interface="org.apache.cxf.dosgi.dsw.hooks.CxfPublishHookTest$AdditionalInterface">
+ http://localhost:9002/hello
+ </property>
+ </service-description>
+</service-descriptions>
diff --git a/trunk/dsw/cxf-dsw/src/test/resources/OSGI-INF/remote-service/remote-services.xml b/trunk/dsw/cxf-dsw/src/test/resources/OSGI-INF/remote-service/remote-services.xml
new file mode 100644
index 0000000..45b2a20
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/resources/OSGI-INF/remote-service/remote-services.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
+ <service-description>
+ <provide interface="org.apache.cxf.dosgi.dsw.hooks.TestService" />
+ <provide interface="org.apache.cxf.dosgi.dsw.hooks.CxfPublishHookTest$AdditionalInterface" />
+
+ <property name="osgi.remote.interfaces">*</property>
+ <property name="osgi.remote.requires.intents">SOAP HTTP</property>
+ <property name="osgi.remote.configuration.type">pojo</property>
+ <property name="osgi.remote.configuration.pojo.address">http://localhost:9000/hello</property>
+ </service-description>
+</service-descriptions>
diff --git a/trunk/dsw/cxf-dsw/src/test/resources/test-resources/rs1.xml b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/rs1.xml
new file mode 100644
index 0000000..f67a833
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/rs1.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
+ <service-description>
+ <provide interface="SomeService" />
+ <property name="osgi.remote.requires.intents">confidentiality</property>
+ </service-description>
+ <service-description>
+ <provide interface="SomeOtherService" />
+ <provide interface="WithSomeSecondInterface" />
+ </service-description>
+</service-descriptions>
diff --git a/trunk/dsw/cxf-dsw/src/test/resources/test-resources/rs2.xml b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/rs2.xml
new file mode 100644
index 0000000..098aa21
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/rs2.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
+ <service-description>
+ <provide interface="org.example.Service" />
+ <property name="deployment.intents">confidentiality.message integrity</property>
+ <property name="osgi.remote.interfaces">*</property>
+ </service-description>
+</service-descriptions>
diff --git a/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd-1.xml b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd-1.xml
new file mode 100644
index 0000000..483b196
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd-1.xml
@@ -0,0 +1,3 @@
+<test>
+ <some-other-tag/>
+</test>
\ No newline at end of file
diff --git a/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd.xml b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd.xml
new file mode 100644
index 0000000..c7cebfb
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd.xml
@@ -0,0 +1,8 @@
+<service-decorations xmlns="http://cxf.apache.org/xmlns/service-decoration/1.0.0">
+ <service-decoration>
+ <match interface="org.acme.foo.*">
+ <match-property name="test.prop" value="xyz"/>
+ <add-property name="test.too" value="ahaha" type="java.lang.String"/>
+ </match>
+ </service-decoration>
+</service-decorations>
\ No newline at end of file
diff --git a/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd0.xml b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd0.xml
new file mode 100644
index 0000000..0ad0ad1
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd0.xml
@@ -0,0 +1,2 @@
+<service-decorations xmlns="http://cxf.apache.org/xmlns/service-decoration/1.0.0">
+</service-decorations>
\ No newline at end of file
diff --git a/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd1.xml b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd1.xml
new file mode 100644
index 0000000..6a5e811
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd1.xml
@@ -0,0 +1,8 @@
+<service-decorations xmlns="http://cxf.apache.org/xmlns/service-decoration/1.0.0">
+ <service-decoration>
+ <match interface="org.test.A">
+ <add-property name="A" value="B"/>
+ <add-property name="C" value="2" type="java.lang.Integer"/>
+ </match>
+ </service-decoration>
+</service-decorations>
\ No newline at end of file
diff --git a/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd2.xml b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd2.xml
new file mode 100644
index 0000000..fb6a93a
--- /dev/null
+++ b/trunk/dsw/cxf-dsw/src/test/resources/test-resources/sd2.xml
@@ -0,0 +1,14 @@
+<service-decorations xmlns="http://cxf.apache.org/xmlns/service-decoration/1.0.0">
+ <service-decoration>
+ <match interface="org.test.(B|C)">
+ <match-property name="x" value="y"/>
+ <add-property name="bool" value="true" type="java.lang.Boolean"/>
+ </match>
+ </service-decoration>
+ <service-decoration>
+ <match interface="org.test.(B|C)">
+ <match-property name="x" value="z"/>
+ <add-property name="bool" value="false" type="java.lang.Boolean"/>
+ </match>
+ </service-decoration>
+</service-decorations>
\ No newline at end of file
diff --git a/trunk/dsw/cxf-topology-manager/pom.xml b/trunk/dsw/cxf-topology-manager/pom.xml
new file mode 100644
index 0000000..ad22f04
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/pom.xml
@@ -0,0 +1,94 @@
+<?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>
+ <artifactId>cxf-dosgi-ri-topology-manager</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGi Topology Manager implementation</name>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>../..</topDirectoryLocation>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>CXF dOSGi Topology Manager</Bundle-Name>
+ <Bundle-Description>The default CXF Topology Manager as described in the OSGi Remote Service Admin specification</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
+ <Bundle-Activator>org.apache.cxf.dosgi.topologymanager.Activator</Bundle-Activator>
+ <Import-Package>
+ org.osgi.service.remoteserviceadmin;version="[${remote.service.admin.interfaces.version},2)",
+ *
+ </Import-Package>
+ <Export-Package>
+ !*
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/Activator.java b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/Activator.java
new file mode 100644
index 0000000..5cf8479
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/Activator.java
@@ -0,0 +1,55 @@
+/**
+ * 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.topologymanager;
+
+import org.apache.cxf.dosgi.topologymanager.exporter.TopologyManagerExport;
+import org.apache.cxf.dosgi.topologymanager.importer.TopologyManagerImport;
+import org.apache.cxf.dosgi.topologymanager.util.SimpleServiceTracker;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Activator implements BundleActivator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Activator.class);
+
+ private TopologyManagerExport topologyManagerExport;
+ private TopologyManagerImport topologyManagerImport;
+ private SimpleServiceTracker<RemoteServiceAdmin> rsaTracker;
+
+ public void start(BundleContext bc) throws Exception {
+ LOG.debug("TopologyManager: start()");
+ rsaTracker = new SimpleServiceTracker<RemoteServiceAdmin>(bc, RemoteServiceAdmin.class);
+ topologyManagerExport = new TopologyManagerExport(bc, rsaTracker);
+ topologyManagerImport = new TopologyManagerImport(bc, rsaTracker);
+
+ rsaTracker.open();
+ topologyManagerExport.start();
+ topologyManagerImport.start();
+ }
+
+ public void stop(BundleContext bc) throws Exception {
+ LOG.debug("TopologyManager: stop()");
+ topologyManagerExport.stop();
+ topologyManagerImport.stop();
+ rsaTracker.close();
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/exporter/EndpointListenerNotifier.java b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/exporter/EndpointListenerNotifier.java
new file mode 100644
index 0000000..7050814
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/exporter/EndpointListenerNotifier.java
@@ -0,0 +1,161 @@
+/**
+ * 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.topologymanager.exporter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.cxf.dosgi.topologymanager.util.Utils;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tracks EndpointListeners and allows to notify them of endpoints.
+ */
+public class EndpointListenerNotifier {
+
+ private static final String ENDPOINT_LISTENER_FILTER =
+ "(&(" + Constants.OBJECTCLASS + "=" + EndpointListener.class.getName() + ")"
+ + "(" + EndpointListener.ENDPOINT_LISTENER_SCOPE + "=*))";
+ private static final Logger LOG = LoggerFactory.getLogger(EndpointListenerNotifier.class);
+ private BundleContext bctx;
+ private ServiceTracker stEndpointListeners;
+
+ public EndpointListenerNotifier(BundleContext bctx, final EndpointRepository endpointRepository) {
+ this.bctx = bctx;
+ Filter filter;
+ try {
+ filter = bctx.createFilter(ENDPOINT_LISTENER_FILTER);
+ } catch (InvalidSyntaxException e) {
+ throw new RuntimeException("Unexpected exception creating filter", e);
+ }
+ this.stEndpointListeners = new ServiceTracker(bctx, filter, null) {
+ @Override
+ public Object addingService(ServiceReference epListenerRef) {
+ LOG.debug("new EndpointListener detected");
+ // the super.addingService call must come before notifyListener, since we need
+ // to keep at least one service reference alive when ungetService is called
+ Object service = super.addingService(epListenerRef);
+ notifyListener(true, epListenerRef, endpointRepository.getAllEndpoints());
+ return service;
+ }
+
+ @Override
+ public void modifiedService(ServiceReference epListenerRef, Object service) {
+ LOG.debug("EndpointListener modified");
+ notifyListener(true, epListenerRef, endpointRepository.getAllEndpoints());
+ super.modifiedService(epListenerRef, service);
+ }
+ };
+ }
+
+ public void start() {
+ stEndpointListeners.open();
+ }
+
+ public void stop() {
+ stEndpointListeners.close();
+ }
+
+ /**
+ * Notifies all endpoint listeners about endpoints being added or removed.
+ *
+ * @param added specifies whether endpoints were added (true) or removed (false)
+ * @param endpoints the endpoints the listeners should be notified about
+ */
+ void notifyListeners(boolean added, Collection<EndpointDescription> endpoints) {
+ if (endpoints.isEmpty()) { // a little optimization to prevent unnecessary processing
+ return;
+ }
+ ServiceReference[] listeners = stEndpointListeners.getServiceReferences();
+ if (listeners != null) {
+ for (ServiceReference eplReference : listeners) {
+ notifyListener(added, eplReference, endpoints);
+ }
+ }
+ }
+
+ /**
+ * Notifies an endpoint listener about endpoints being added or removed.
+ *
+ * @param added specifies whether endpoints were added (true) or removed (false)
+ * @param endpointListenerRef the ServiceReference of an EndpointListener to notify
+ * @param endpoints the endpoints the listener should be notified about
+ */
+ void notifyListener(boolean added, ServiceReference endpointListenerRef,
+ Collection<EndpointDescription> endpoints) {
+ List<Filter> filters = getFiltersFromEndpointListenerScope(endpointListenerRef, bctx);
+ EndpointListener endpointListener = (EndpointListener)bctx.getService(endpointListenerRef);
+ try {
+ LOG.debug("notifyListener (added={})", added);
+ for (EndpointDescription endpoint : endpoints) {
+ List<Filter> matchingFilters = getMatchingFilters(filters, endpoint);
+ for (Filter filter : matchingFilters) {
+ if (added) {
+ endpointListener.endpointAdded(endpoint, filter.toString());
+ } else {
+ endpointListener.endpointRemoved(endpoint, filter.toString());
+ }
+ }
+ }
+ } finally {
+ if (endpointListener != null) {
+ bctx.ungetService(endpointListenerRef);
+ }
+ }
+ }
+
+ static List<Filter> getFiltersFromEndpointListenerScope(ServiceReference sref, BundleContext bctx) {
+ List<Filter> filters = new ArrayList<Filter>();
+ String[] scopes = Utils.getStringPlusProperty(sref.getProperty(EndpointListener.ENDPOINT_LISTENER_SCOPE));
+ for (String scope : scopes) {
+ try {
+ filters.add(bctx.createFilter(scope));
+ } catch (InvalidSyntaxException e) {
+ LOG.error("invalid endpoint listener scope: {}", scope, e);
+ }
+ }
+ return filters;
+ }
+
+ private static List<Filter> getMatchingFilters(List<Filter> filters, EndpointDescription endpoint) {
+ List<Filter> matchingFilters = new ArrayList<Filter>();
+ Dictionary<String, Object> dict = Utils.getEndpointProperties(endpoint);
+
+ for (Filter filter : filters) {
+ if (filter.match(dict)) {
+ LOG.debug("Filter {} matches endpoint {}", filter, dict);
+ matchingFilters.add(filter);
+ } else {
+ LOG.trace("Filter {} does not match endpoint {}", filter, dict);
+ }
+ }
+ return matchingFilters;
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/exporter/EndpointRepository.java b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/exporter/EndpointRepository.java
new file mode 100644
index 0000000..489b051
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/exporter/EndpointRepository.java
@@ -0,0 +1,120 @@
+/**
+ * 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.topologymanager.exporter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cxf.dosgi.topologymanager.util.Utils;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Holds all endpoints that are exported by a TopologyManager. For each ServiceReference that is exported a
+ * map is maintained which contains information on the endpoints for each RemoteAdminService that created the
+ * endpoints.
+ */
+class EndpointRepository {
+
+ private static final Logger LOG = LoggerFactory.getLogger(EndpointRepository.class);
+
+ private final Map<ServiceReference, Map<RemoteServiceAdmin, Collection<EndpointDescription>>> exportedServices
+ = new LinkedHashMap<ServiceReference, Map<RemoteServiceAdmin, Collection<EndpointDescription>>>();
+
+ /**
+ * Remove all services exported by the given rsa.
+ *
+ * @param rsa the RemoteServiceAdmin to remove
+ * @return list of removed endpoints
+ */
+ synchronized List<EndpointDescription> removeRemoteServiceAdmin(RemoteServiceAdmin rsa) {
+ List<EndpointDescription> removedEndpoints = new ArrayList<EndpointDescription>();
+ for (Map<RemoteServiceAdmin, Collection<EndpointDescription>> exports : exportedServices.values()) {
+ Collection<EndpointDescription> endpoints = exports.get(rsa);
+ if (endpoints != null) {
+ removedEndpoints.addAll(endpoints);
+ exports.remove(rsa);
+ }
+ }
+ return removedEndpoints;
+ }
+
+ synchronized List<EndpointDescription> removeService(ServiceReference sref) {
+ List<EndpointDescription> removedEndpoints = new ArrayList<EndpointDescription>();
+ Map<RemoteServiceAdmin, Collection<EndpointDescription>> rsas = exportedServices.get(sref);
+ if (rsas != null) {
+ for (Collection<EndpointDescription> endpoints : rsas.values()) {
+ removedEndpoints.addAll(endpoints);
+ }
+ exportedServices.remove(sref);
+ }
+ return removedEndpoints;
+ }
+
+ synchronized void addService(ServiceReference sref) {
+ if (!exportedServices.containsKey(sref)) {
+ LOG.info("Marking service from bundle {} for export", Utils.getBundleName(sref));
+ exportedServices.put(sref, new LinkedHashMap<RemoteServiceAdmin, Collection<EndpointDescription>>());
+ }
+ }
+
+ synchronized void addEndpoints(ServiceReference sref, RemoteServiceAdmin rsa,
+ List<EndpointDescription> endpoints) {
+ if (endpoints == null) {
+ throw new NullPointerException();
+ }
+ addService(sref);
+ Map<RemoteServiceAdmin, Collection<EndpointDescription>> exports = exportedServices.get(sref);
+ exports.put(rsa, endpoints);
+ }
+
+ synchronized boolean isAlreadyExportedForRsa(ServiceReference sref, RemoteServiceAdmin rsa) {
+ Map<RemoteServiceAdmin, Collection<EndpointDescription>> exports = exportedServices.get(sref);
+ return exports != null && exports.containsKey(rsa);
+ }
+
+ synchronized Collection<EndpointDescription> getAllEndpoints() {
+ List<EndpointDescription> allEndpoints = new ArrayList<EndpointDescription>();
+ for (Map<RemoteServiceAdmin, Collection<EndpointDescription>> exports : exportedServices.values()) {
+ for (Collection<EndpointDescription> endpoints : exports.values()) {
+ allEndpoints.addAll(endpoints);
+ }
+ }
+ return allEndpoints;
+ }
+
+ synchronized Set<ServiceReference> getServicesToBeExportedFor(RemoteServiceAdmin rsa) {
+ Set<ServiceReference> servicesToBeExported = new HashSet<ServiceReference>();
+ for (Map.Entry<ServiceReference, Map<RemoteServiceAdmin, Collection<EndpointDescription>>> entry
+ : exportedServices.entrySet()) {
+ if (!entry.getValue().containsKey(rsa)) {
+ servicesToBeExported.add(entry.getKey());
+ }
+ }
+ return servicesToBeExported;
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/exporter/TopologyManagerExport.java b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/exporter/TopologyManagerExport.java
new file mode 100644
index 0000000..767376e
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/exporter/TopologyManagerExport.java
@@ -0,0 +1,245 @@
+/**
+ * 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.topologymanager.exporter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.cxf.dosgi.topologymanager.util.SimpleServiceTracker;
+import org.apache.cxf.dosgi.topologymanager.util.SimpleServiceTrackerListener;
+import org.apache.cxf.dosgi.topologymanager.util.Utils;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.ExportReference;
+import org.osgi.service.remoteserviceadmin.ExportRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Manages exported endpoints of DOSGi services and notifies EndpointListeners of changes.
+ *
+ * <li> Tracks local RemoteServiceAdmin instances by using a ServiceTracker
+ * <li> Uses a ServiceListener to track local OSGi services
+ * <li> When a service is published that is supported by DOSGi the
+ * known RemoteServiceAdmins are instructed to export the service and
+ * the EndpointListeners are notified
+ * <li> When a service is unpublished the EndpointListeners are notified.
+ * The endpoints are not closed as the ExportRegistration takes care of this
+ */
+public class TopologyManagerExport {
+
+ private static final String DOSGI_SERVICES = "(" + RemoteConstants.SERVICE_EXPORTED_INTERFACES + "=*)";
+
+ private static final Logger LOG = LoggerFactory.getLogger(TopologyManagerExport.class);
+
+ private final BundleContext bctx;
+ private final EndpointListenerNotifier epListenerNotifier;
+ private final ExecutorService execService;
+ private final SimpleServiceTracker<RemoteServiceAdmin> remoteServiceAdminTracker;
+ private final ServiceListener serviceListener;
+ private final EndpointRepository endpointRepo;
+
+ public TopologyManagerExport(BundleContext ctx, SimpleServiceTracker<RemoteServiceAdmin> rsaTracker) {
+ this(ctx, rsaTracker, null);
+ }
+
+ public TopologyManagerExport(BundleContext ctx, SimpleServiceTracker<RemoteServiceAdmin> rsaTracker,
+ EndpointListenerNotifier notif) {
+ endpointRepo = new EndpointRepository();
+ epListenerNotifier = notif == null ? new EndpointListenerNotifier(ctx, endpointRepo) : notif;
+ execService = new ThreadPoolExecutor(5, 10, 50, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
+ bctx = ctx;
+ remoteServiceAdminTracker = rsaTracker;
+
+ // track RemoteServiceAdmins through which we can export services
+ remoteServiceAdminTracker.addListener(new SimpleServiceTrackerListener<RemoteServiceAdmin>() {
+
+ public void added(RemoteServiceAdmin rsa) {
+ LOG.debug("RemoteServiceAdmin added: {}, total {}",
+ rsa, remoteServiceAdminTracker.getAllServices().size());
+ for (ServiceReference serviceRef : endpointRepo.getServicesToBeExportedFor(rsa)) {
+ triggerExport(serviceRef);
+ }
+ }
+
+ public void removed(RemoteServiceAdmin rsa) {
+ LOG.debug("RemoteServiceAdmin removed: {}, total {}", rsa,
+ remoteServiceAdminTracker.getAllServices().size());
+ List<EndpointDescription> endpoints = endpointRepo.removeRemoteServiceAdmin(rsa);
+ epListenerNotifier.notifyListeners(false, endpoints);
+ }
+ });
+
+ // track all service registrations so we can export any services that are configured to be exported
+ // ServiceListener events may be delivered out of order, concurrently, re-entrant, etc. (see spec or docs)
+ serviceListener = new ServiceListener() {
+
+ public void serviceChanged(ServiceEvent event) {
+ ServiceReference sref = event.getServiceReference();
+ if (event.getType() == ServiceEvent.REGISTERED) {
+ LOG.debug("Received REGISTERED ServiceEvent: {}", event);
+ if (shouldExportService(sref)) {
+ triggerExport(sref);
+ }
+ } else if (event.getType() == ServiceEvent.UNREGISTERING) {
+ LOG.debug("Received UNREGISTERING ServiceEvent: {}", event);
+ List<EndpointDescription> endpoints = endpointRepo.removeService(sref);
+ epListenerNotifier.notifyListeners(false, endpoints);
+ }
+ }
+ };
+ }
+
+ /**
+ * checks if a Service is intended to be exported
+ */
+ private boolean shouldExportService(ServiceReference sref) {
+ return sref.getProperty(RemoteConstants.SERVICE_EXPORTED_INTERFACES) != null;
+ }
+
+ public void start() {
+ epListenerNotifier.start();
+ bctx.addServiceListener(serviceListener);
+ exportExistingServices();
+ }
+
+ public void stop() {
+ execService.shutdown();
+ bctx.removeServiceListener(serviceListener);
+ epListenerNotifier.stop();
+ }
+
+ protected void triggerExport(final ServiceReference sref) {
+ execService.execute(new Runnable() {
+ public void run() {
+ try {
+ doExportService(sref);
+ } catch (Throwable t) {
+ LOG.error("export failed", t);
+ }
+ }
+ });
+ }
+
+ protected void doExportService(final ServiceReference sref) {
+ LOG.debug("Exporting service {}", sref);
+ endpointRepo.addService(sref); // mark for future export even if there are currently no RSAs
+ List<RemoteServiceAdmin> rsaList = remoteServiceAdminTracker.getAllServices();
+ if (rsaList.isEmpty()) {
+ LOG.error("No RemoteServiceAdmin available! Unable to export service from bundle {}, interfaces: {}",
+ Utils.getBundleName(sref),
+ sref.getProperty(org.osgi.framework.Constants.OBJECTCLASS));
+ }
+
+ for (final RemoteServiceAdmin remoteServiceAdmin : rsaList) {
+ LOG.info("TopologyManager: handling remoteServiceAdmin " + remoteServiceAdmin);
+
+ if (endpointRepo.isAlreadyExportedForRsa(sref, remoteServiceAdmin)) {
+ // already handled by this remoteServiceAdmin
+ LOG.debug("already handled by this remoteServiceAdmin -> skipping");
+ } else {
+ exportServiceUsingRemoteServiceAdmin(sref, remoteServiceAdmin);
+ }
+ }
+ }
+
+ private void exportServiceUsingRemoteServiceAdmin(final ServiceReference sref,
+ final RemoteServiceAdmin remoteServiceAdmin) {
+ // abort if the service was unregistered by the time we got here
+ // (we check again at the end, but this optimization saves unnecessary heavy processing)
+ if (sref.getBundle() == null) {
+ LOG.info("TopologyManager: export aborted for {} since it was unregistered", sref);
+ endpointRepo.removeService(sref);
+ return;
+ }
+ // do the export
+ LOG.debug("exporting {}...", sref);
+ // TODO: additional parameter Map?
+ Collection<ExportRegistration> exportRegs = remoteServiceAdmin.exportService(sref, null);
+ if (exportRegs.isEmpty()) {
+ LOG.warn("TopologyManager: nothing was exported for {}", sref);
+ return;
+ }
+ // process successful/failed registrations
+ List<EndpointDescription> endpoints = new ArrayList<EndpointDescription>();
+ for (ExportRegistration reg : exportRegs) {
+ if (reg.getException() == null) {
+ EndpointDescription endpoint = getExportedEndpoint(reg);
+ LOG.info("TopologyManager: export succeeded for {}, endpoint ", sref, endpoint);
+ endpoints.add(endpoint);
+ } else {
+ // TODO: what should we do with failed exports?
+ LOG.error("TopologyManager: export failed for {}", sref);
+ reg.close();
+ }
+ }
+ // abort export if service was unregistered in the meanwhile (since we have a race
+ // with the unregister event which may have already been handled, so we'll miss it)
+ if (sref.getBundle() == null) {
+ LOG.info("TopologyManager: export reverted for {} since service was unregistered", sref);
+ endpointRepo.removeService(sref);
+ for (ExportRegistration reg : exportRegs) {
+ reg.close();
+ }
+ return;
+ }
+ // add the new exported endpoints
+ if (!endpoints.isEmpty()) {
+ LOG.info("TopologyManager: export successful for {}, endpoints: {}", sref, endpoints);
+ epListenerNotifier.notifyListeners(true, endpoints);
+ endpointRepo.addEndpoints(sref, remoteServiceAdmin, endpoints);
+ }
+ }
+
+ /**
+ * Retrieves an exported Endpoint (while safely handling nulls).
+ *
+ * @param exReg an export registration
+ * @return exported Endpoint or null if not present
+ */
+ private EndpointDescription getExportedEndpoint(ExportRegistration exReg) {
+ ExportReference ref = (exReg == null) ? null : exReg.getExportReference();
+ return (ref == null) ? null : ref.getExportedEndpoint();
+ }
+
+ private void exportExistingServices() {
+ try {
+ // cast to String is necessary for compiling against OSGi core version >= 4.3
+ ServiceReference[] references = bctx.getServiceReferences((String)null, DOSGI_SERVICES);
+ if (references != null) {
+ for (ServiceReference sref : references) {
+ triggerExport(sref);
+ }
+ }
+ } catch (InvalidSyntaxException e) {
+ LOG.error("Error in filter {}. This should not occur!", DOSGI_SERVICES);
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/importer/EndpointListenerManager.java b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/importer/EndpointListenerManager.java
new file mode 100644
index 0000000..42a8bc4
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/importer/EndpointListenerManager.java
@@ -0,0 +1,98 @@
+/**
+ * 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.topologymanager.importer;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Manages an EndpointListener and adjusts its scope according to requested service filters.
+ */
+public class EndpointListenerManager {
+
+ private static final Logger LOG = LoggerFactory.getLogger(EndpointListenerManager.class);
+
+ private final BundleContext bctx;
+ private volatile ServiceRegistration serviceRegistration;
+ private final List<String> filters = new ArrayList<String>();
+ private final EndpointListener endpointListener;
+
+ protected EndpointListenerManager(BundleContext bc, EndpointListener endpointListener) {
+ this.bctx = bc;
+ this.endpointListener = endpointListener;
+ }
+
+ protected void start() {
+ serviceRegistration = bctx.registerService(EndpointListener.class.getName(), endpointListener,
+ getRegistrationProperties());
+ }
+
+ public void stop() {
+ if (serviceRegistration != null) {
+ serviceRegistration.unregister();
+ }
+ }
+
+ protected void extendScope(String filter) {
+ if (filter == null) {
+ return;
+ }
+ LOG.debug("EndpointListener: extending scope by {}", filter);
+ synchronized (filters) {
+ filters.add(filter);
+ }
+ updateRegistration();
+ }
+
+ protected void reduceScope(String filter) {
+ if (filter == null) {
+ return;
+ }
+ LOG.debug("EndpointListener: reducing scope by {}", filter);
+ synchronized (filters) {
+ filters.remove(filter);
+ }
+ updateRegistration();
+ }
+
+ private Dictionary<String, Object> getRegistrationProperties() {
+ Dictionary<String, Object> p = new Hashtable<String, Object>();
+
+ synchronized (filters) {
+ LOG.debug("Current filter: {}", filters);
+ p.put(EndpointListener.ENDPOINT_LISTENER_SCOPE, new ArrayList<String>(filters));
+ }
+
+ return p;
+ }
+
+ private void updateRegistration() {
+ if (serviceRegistration != null) {
+ serviceRegistration.setProperties(getRegistrationProperties());
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/importer/ListenerHookImpl.java b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/importer/ListenerHookImpl.java
new file mode 100644
index 0000000..0ab5e39
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/importer/ListenerHookImpl.java
@@ -0,0 +1,119 @@
+/**
+ * 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.topologymanager.importer;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.cxf.dosgi.topologymanager.util.Utils;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.hooks.service.ListenerHook;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Listens for service listeners and informs ServiceInterestListener about added and removed interest
+ * in services
+ */
+public class ListenerHookImpl implements ListenerHook {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ListenerHookImpl.class);
+
+ // From the old impl.
+ private static final Set<String> SYSTEM_PACKAGES;
+ static {
+ SYSTEM_PACKAGES = new HashSet<String>();
+ SYSTEM_PACKAGES.add("org.osgi.service");
+ SYSTEM_PACKAGES.add("org.apache.felix");
+ SYSTEM_PACKAGES.add("org.ops4j.pax.logging");
+ SYSTEM_PACKAGES.add("ch.ethz.iks.slp");
+ SYSTEM_PACKAGES.add("org.ungoverned.osgi.service");
+ SYSTEM_PACKAGES.add("org.springframework.osgi.context.event.OsgiBundleApplicationContextListener");
+ SYSTEM_PACKAGES.add("java.net.ContentHandler");
+ }
+
+ private final BundleContext bctx;
+ private final ServiceInterestListener serviceInterestListener;
+
+ public ListenerHookImpl(BundleContext bc, ServiceInterestListener serviceInterestListener) {
+ this.bctx = bc;
+ this.serviceInterestListener = serviceInterestListener;
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void added(Collection/* <ListenerInfo> */ listeners) {
+ LOG.debug("added listeners {}", listeners);
+ for (Object li : listeners) {
+ ListenerInfo listenerInfo = (ListenerInfo)li;
+ LOG.debug("Filter {}", listenerInfo.getFilter());
+
+ String className = Utils.getObjectClass(listenerInfo.getFilter());
+
+ if (listenerInfo.getBundleContext().getBundle().equals(bctx.getBundle())) {
+ LOG.debug("ListenerHookImpl: skipping request from myself");
+ continue;
+ }
+
+ if (listenerInfo.getFilter() == null) {
+ LOG.debug("skipping empty filter");
+ continue;
+ }
+
+ if (isClassExcluded(className)) {
+ LOG.debug("Skipping import request for excluded class [{}]", className);
+ continue;
+ }
+ String exFilter = extendFilter(listenerInfo.getFilter(), bctx);
+ serviceInterestListener.addServiceInterest(exFilter);
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ public void removed(Collection/* <ListenerInfo> */ listeners) {
+ LOG.debug("removed listeners {}", listeners);
+
+ for (Object li : listeners) {
+ ListenerInfo listenerInfo = (ListenerInfo)li;
+ LOG.debug("Filter {}", listenerInfo.getFilter());
+
+ // TODO: determine if service was handled?
+ String exFilter = extendFilter(listenerInfo.getFilter(), bctx);
+ serviceInterestListener.removeServiceInterest(exFilter);
+ }
+ }
+
+ private static boolean isClassExcluded(String className) {
+ if (className == null) {
+ return true;
+ }
+
+ for (String p : SYSTEM_PACKAGES) {
+ if (className.startsWith(p)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static String extendFilter(String filter, BundleContext bctx) {
+ return "(&" + filter + "(!(" + RemoteConstants.ENDPOINT_FRAMEWORK_UUID + "=" + Utils.getUUID(bctx) + ")))";
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/importer/ServiceInterestListener.java b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/importer/ServiceInterestListener.java
new file mode 100644
index 0000000..f4db92e
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/importer/ServiceInterestListener.java
@@ -0,0 +1,26 @@
+/**
+ * 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.topologymanager.importer;
+
+public interface ServiceInterestListener {
+
+ void addServiceInterest(String filter);
+
+ void removeServiceInterest(String filter);
+}
\ No newline at end of file
diff --git a/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/importer/TopologyManagerImport.java b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/importer/TopologyManagerImport.java
new file mode 100644
index 0000000..47f7840
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/importer/TopologyManagerImport.java
@@ -0,0 +1,349 @@
+/**
+ * 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.topologymanager.importer;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.cxf.dosgi.topologymanager.util.ReferenceCounter;
+import org.apache.cxf.dosgi.topologymanager.util.SimpleServiceTracker;
+import org.apache.cxf.dosgi.topologymanager.util.SimpleServiceTrackerListener;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.hooks.service.ListenerHook;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+import org.osgi.service.remoteserviceadmin.ImportReference;
+import org.osgi.service.remoteserviceadmin.ImportRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdminEvent;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdminListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Listens for remote endpoints using the EndpointListener interface and the EndpointListenerManager.
+ * Listens for local service interests using the ListenerHookImpl that calls back through the
+ * ServiceInterestListener interface.
+ * Manages local creation and destruction of service imports using the available RemoteServiceAdmin services.
+ */
+public class TopologyManagerImport implements EndpointListener, RemoteServiceAdminListener, ServiceInterestListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TopologyManagerImport.class);
+ private ExecutorService execService;
+
+ private final EndpointListenerManager endpointListenerManager;
+ private final BundleContext bctx;
+ private final SimpleServiceTracker<RemoteServiceAdmin> remoteServiceAdminTracker;
+ private final ListenerHookImpl listenerHook;
+
+ /**
+ * If set to false only one service is imported for each import interest even it multiple services are
+ * available. If set to true, all available services are imported.
+ *
+ * TODO: Make this available as a configuration option
+ */
+ private boolean importAllAvailable = true;
+
+ /**
+ * Contains an instance of the Class Import Interest for each distinct import request. If the same filter
+ * is requested multiple times the existing instance of the Object increments an internal reference
+ * counter. If an interest is removed, the related ServiceInterest object is used to reduce the reference
+ * counter until it reaches zero. in this case the interest is removed.
+ */
+ private final ReferenceCounter<String> importInterestsCounter = new ReferenceCounter<String>();
+
+ /**
+ * List of Endpoints by matched filter that were reported by the EndpointListener and can be imported
+ */
+ private final Map<String /* filter */, List<EndpointDescription>> importPossibilities
+ = new HashMap<String, List<EndpointDescription>>();
+
+ /**
+ * List of already imported Endpoints by their matched filter
+ */
+ private final Map<String /* filter */, List<ImportRegistration>> importedServices
+ = new HashMap<String, List<ImportRegistration>>();
+
+ public TopologyManagerImport(BundleContext bc, SimpleServiceTracker<RemoteServiceAdmin> rsaTracker) {
+ bctx = bc;
+ remoteServiceAdminTracker = rsaTracker;
+ remoteServiceAdminTracker.addListener(new SimpleServiceTrackerListener<RemoteServiceAdmin>() {
+
+ public void added(RemoteServiceAdmin rsa) {
+ triggerImportsForRemoteServiceAdmin(rsa);
+ }
+
+ public void removed(RemoteServiceAdmin rsa) {
+ // the RSA's imports will be closed by its shutdown, so nothing to do here
+ }
+ });
+ endpointListenerManager = new EndpointListenerManager(bctx, this);
+ execService = new ThreadPoolExecutor(5, 10, 50, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
+ listenerHook = new ListenerHookImpl(bc, this);
+ }
+
+ public void start() {
+ bctx.registerService(RemoteServiceAdminListener.class.getName(), this, null);
+ bctx.registerService(ListenerHook.class.getName(), listenerHook, null);
+ endpointListenerManager.start();
+ }
+
+ public void stop() {
+ endpointListenerManager.stop();
+ execService.shutdown();
+ // this is called from Activator.stop(), which implicitly unregisters our registered services
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.cxf.dosgi.topologymanager.ServiceInterestListener#addServiceInterest(java.lang.String)
+ */
+ public void addServiceInterest(String filter) {
+ if (importInterestsCounter.add(filter) == 1) {
+ endpointListenerManager.extendScope(filter);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.cxf.dosgi.topologymanager.ServiceInterestListener#removeServiceInterest(java.lang.String)
+ */
+ public void removeServiceInterest(String filter) {
+ if (importInterestsCounter.remove(filter) == 0) {
+ LOG.debug("last reference to import interest is gone -> removing interest filter: {}", filter);
+ endpointListenerManager.reduceScope(filter);
+ synchronized (importedServices) {
+ List<ImportRegistration> irs = importedServices.remove(filter);
+ if (irs != null) {
+ for (ImportRegistration ir : irs) {
+ ir.close();
+ }
+ }
+ }
+ }
+ }
+
+ public void endpointAdded(EndpointDescription endpoint, String filter) {
+ if (filter == null) {
+ LOG.error("Endpoint is not handled because no matching filter was provided!");
+ return;
+ }
+ LOG.debug("importable service added for filter {}, endpoint {}", filter, endpoint);
+ addImportPossibility(endpoint, filter);
+ triggerImport(filter);
+ }
+
+ public void endpointRemoved(EndpointDescription endpoint, String filter) {
+ LOG.debug("EndpointRemoved {}", endpoint);
+ removeImportPossibility(endpoint, filter);
+ triggerImport(filter);
+ }
+
+ private void addImportPossibility(EndpointDescription endpoint, String filter) {
+ synchronized (importPossibilities) {
+ List<EndpointDescription> endpoints = importPossibilities.get(filter);
+ if (endpoints == null) {
+ endpoints = new ArrayList<EndpointDescription>();
+ importPossibilities.put(filter, endpoints);
+ }
+ // prevent adding the same endpoint multiple times, which can happen sometimes,
+ // and which causes imports to remain available even when services are actually down
+ if (!endpoints.contains(endpoint)) {
+ endpoints.add(endpoint);
+ }
+ }
+ }
+
+ private void removeImportPossibility(EndpointDescription endpoint, String filter) {
+ synchronized (importPossibilities) {
+ List<EndpointDescription> endpoints = importPossibilities.get(filter);
+ if (endpoints != null) {
+ endpoints.remove(endpoint);
+ if (endpoints.isEmpty()) {
+ importPossibilities.remove(filter);
+ }
+ }
+ }
+ }
+
+ public void triggerImportsForRemoteServiceAdmin(RemoteServiceAdmin rsa) {
+ LOG.debug("New RemoteServiceAdmin {} detected, trying to import services with it", rsa);
+ synchronized (importPossibilities) {
+ for (String filter : importPossibilities.keySet()) {
+ triggerImport(filter);
+ }
+ }
+ }
+
+ private void triggerImport(final String filter) {
+ LOG.debug("Import of a service for filter {} was queued", filter);
+
+ execService.execute(new Runnable() {
+ public void run() {
+ try {
+ unexportNotAvailableServices(filter);
+ importServices(filter);
+ } catch (Exception e) {
+ LOG.error(e.getMessage(), e);
+ }
+ // Notify EndpointListeners? NO!
+ }
+ });
+ }
+
+ private void unexportNotAvailableServices(String filter) {
+ synchronized (importedServices) {
+ List<ImportRegistration> importRegistrations = importedServices.get(filter);
+ if (importRegistrations != null) {
+ // iterate over a copy
+ for (ImportRegistration ir : new ArrayList<ImportRegistration>(importRegistrations)) {
+ EndpointDescription endpoint = ir.getImportReference().getImportedEndpoint();
+ if (!isImportPossibilityAvailable(endpoint, filter)) {
+ removeImport(ir, null); // also unexports the service
+ }
+ }
+ }
+ }
+ }
+
+ private boolean isImportPossibilityAvailable(EndpointDescription endpoint, String filter) {
+ synchronized (importPossibilities) {
+ List<EndpointDescription> endpoints = importPossibilities.get(filter);
+ return endpoints != null && endpoints.contains(endpoint);
+ }
+ }
+
+ // return a copy to prevent sync issues
+ private List<EndpointDescription> getImportPossibilitiesCopy(String filter) {
+ synchronized (importPossibilities) {
+ List<EndpointDescription> possibilities = importPossibilities.get(filter);
+ return possibilities == null
+ ? Collections.<EndpointDescription>emptyList()
+ : new ArrayList<EndpointDescription>(possibilities);
+ }
+ }
+
+ private void importServices(String filter) {
+ synchronized (importedServices) {
+ List<ImportRegistration> importRegistrations = importedServices.get(filter);
+ for (EndpointDescription endpoint : getImportPossibilitiesCopy(filter)) {
+ // TODO but optional: if the service is already imported and the endpoint is still
+ // in the list of possible imports check if a "better" endpoint is now in the list
+ if (!alreadyImported(endpoint, importRegistrations)) {
+ // service not imported yet -> import it now
+ ImportRegistration ir = importService(endpoint);
+ if (ir != null) {
+ // import was successful
+ if (importRegistrations == null) {
+ importRegistrations = new ArrayList<ImportRegistration>();
+ importedServices.put(filter, importRegistrations);
+ }
+ importRegistrations.add(ir);
+ if (!importAllAvailable) {
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private boolean alreadyImported(EndpointDescription endpoint, List<ImportRegistration> importRegistrations) {
+ if (importRegistrations != null) {
+ for (ImportRegistration ir : importRegistrations) {
+ if (endpoint.equals(ir.getImportReference().getImportedEndpoint())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tries to import the service with each rsa until one import is successful
+ *
+ * @param endpoint endpoint to import
+ * @return import registration of the first successful import
+ */
+ private ImportRegistration importService(EndpointDescription endpoint) {
+ for (RemoteServiceAdmin rsa : remoteServiceAdminTracker.getAllServices()) {
+ ImportRegistration ir = rsa.importService(endpoint);
+ if (ir != null) {
+ if (ir.getException() == null) {
+ LOG.debug("Service import was successful {}", ir);
+ return ir;
+ } else {
+ LOG.info("Error importing service " + endpoint, ir.getException());
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Remove and close (unexport) the given import. The import is specified either
+ * by its ImportRegistration or by its ImportReference (only one of them must
+ * be specified).
+ * <p>
+ * If this method is called from within iterations on the underlying data structure,
+ * the iterations must be made on copies of the structures rather than the original
+ * references in order to prevent ConcurrentModificationExceptions.
+ *
+ * @param reg the import registration to remove
+ * @param ref the import reference to remove
+ */
+ private void removeImport(ImportRegistration reg, ImportReference ref) {
+ // this method may be called recursively by calling ImportRegistration.close()
+ // and receiving a RemoteServiceAdminEvent for its unregistration, which results
+ // in a ConcurrentModificationException. We avoid this by closing the registrations
+ // only after data structure manipulation is done, and being re-entrant.
+ synchronized (importedServices) {
+ List<ImportRegistration> removed = new ArrayList<ImportRegistration>();
+ for (Iterator<List<ImportRegistration>> it1 = importedServices.values().iterator(); it1.hasNext();) {
+ Collection<ImportRegistration> irs = it1.next();
+ for (Iterator<ImportRegistration> it2 = irs.iterator(); it2.hasNext();) {
+ ImportRegistration ir = it2.next();
+ if (ir.equals(reg) || ir.getImportReference().equals(ref)) {
+ removed.add(ir);
+ it2.remove();
+ }
+ }
+ if (irs.isEmpty()) {
+ it1.remove();
+ }
+ }
+ for (ImportRegistration ir : removed) {
+ ir.close();
+ }
+ }
+ }
+
+ public void remoteAdminEvent(RemoteServiceAdminEvent event) {
+ if (event.getType() == RemoteServiceAdminEvent.IMPORT_UNREGISTRATION) {
+ removeImport(null, event.getImportReference());
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/util/ReferenceCounter.java b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/util/ReferenceCounter.java
new file mode 100644
index 0000000..04d2e2c
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/util/ReferenceCounter.java
@@ -0,0 +1,76 @@
+/**
+ * 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.topologymanager.util;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Manages a reference count per key.
+ *
+ * @param <K> the key type
+ */
+public class ReferenceCounter<K> {
+
+ private final ConcurrentMap<K, Integer> counts = new ConcurrentHashMap<K, Integer>();
+
+ /**
+ * Increases the reference count for the given key,
+ * or sets it to 1 if the key has no existing count.
+ *
+ * @param key a key
+ * @return the updated reference count
+ */
+ public int add(K key) {
+ while (true) {
+ Integer count = counts.get(key);
+ if (count == null) {
+ if (counts.putIfAbsent(key, 1) == null) {
+ return 1;
+ }
+ } else if (counts.replace(key, count, count + 1)) {
+ return count + 1;
+ }
+ }
+ }
+
+ /**
+ * Decreases the reference count for the given key,
+ * and removes it if it reaches 0.
+ * If the key has no existing count, -1 is returned.
+ *
+ * @param key a key
+ * @return the updated reference count, or -1 if the key has no existing count
+ */
+ public int remove(K key) {
+ while (true) {
+ Integer count = counts.get(key);
+ if (count == null) {
+ return -1;
+ }
+ if (count == 1) {
+ if (counts.remove(key, 1)) {
+ return 0;
+ }
+ } else if (counts.replace(key, count, count - 1)) {
+ return count - 1;
+ }
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/util/SimpleServiceTracker.java b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/util/SimpleServiceTracker.java
new file mode 100644
index 0000000..a5339f8
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/util/SimpleServiceTracker.java
@@ -0,0 +1,117 @@
+/**
+ * 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.topologymanager.util;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * A {@link ServiceTracker} extension that simplifies its usage.
+ * <p>
+ * It enhances {@code ServiceTracker} by adding:
+ * <ul>
+ * <li>Multiple event listeners for service add/remove events
+ * <li>Simpler event callbacks that do not need to deal with getting/ungetting
+ * services, calling super methods or returning service objects
+ * <li>Generics support, which means the callback and {@code getList()} methods
+ * are type-safe and require no casting
+ * <li>A {@link #getAllServices()} method which returns all currently tracked services;
+ * Unlike {@link ServiceTracker#getServices()}, if it is called from within a service
+ * {@link SimpleServiceTrackerListener#added added} event handler, the returned list
+ * will include the newly added service (this is the source of several bugs when using
+ * the original {@code getServices()})
+ * </ul>
+ *
+ * @param <T> the service interface type
+ */
+public class SimpleServiceTracker<T> extends ServiceTracker<T, T> {
+
+ // we must use a map with references as keys, so as not to invoke equals remotely on service objects
+ private ConcurrentMap<ServiceReference<T>, T> services;
+ private List<SimpleServiceTrackerListener<T>> listeners;
+
+ /**
+ * Create a <code>SimpleServiceTracker</code> on the specified class' name.
+ * <p>
+ * Services registered under the specified class' name will be tracked by
+ * this <code>SimpleServiceTracker</code>.
+ *
+ * @param context the {@code BundleContext} against which the tracking is done
+ * @param clazz the class of the services to be tracked
+ */
+ public SimpleServiceTracker(BundleContext context, Class<T> clazz) {
+ super(context, clazz.getName(), null);
+ this.listeners = new CopyOnWriteArrayList<SimpleServiceTrackerListener<T>>();
+ this.services = new ConcurrentHashMap<ServiceReference<T>, T>();
+ }
+
+ /**
+ * Adds a listener to be notified of services added or removed.
+ *
+ * @param listener the listener to add
+ */
+ public void addListener(SimpleServiceTrackerListener<T> listener) {
+ listeners.add(listener);
+ }
+
+ @Override
+ public T addingService(ServiceReference<T> reference) {
+ T service = (T) super.addingService(reference);
+ services.put(reference, service);
+ for (SimpleServiceTrackerListener<T> listener : listeners) {
+ listener.added(service);
+ }
+ return service;
+ }
+
+ @Override
+ public void removedService(ServiceReference<T> reference, T service) {
+ services.remove(reference, service);
+ for (SimpleServiceTrackerListener<T> listener : listeners) {
+ listener.removed(service);
+ }
+ super.removedService(reference, service);
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ services.clear();
+ }
+
+ /**
+ * Returns all currently tracked services.
+ * <p>
+ * Unlike {@link ServiceTracker#getServices()}, if it is called from within a service
+ * {@link SimpleServiceTrackerListener#added added} event handler, the returned list
+ * will include the newly added service.
+ *
+ * @return all currently tracked services
+ */
+ public List<T> getAllServices() {
+ return new ArrayList<T>(services.values());
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/util/SimpleServiceTrackerListener.java b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/util/SimpleServiceTrackerListener.java
new file mode 100644
index 0000000..22375c4
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/util/SimpleServiceTrackerListener.java
@@ -0,0 +1,42 @@
+/**
+ * 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.topologymanager.util;
+
+/**
+ * Callback interface for notifications of services that are
+ * added to or removed from tracking by a {@link SimpleServiceTracker}.
+ *
+ * @param <T> the service interface type
+ */
+public interface SimpleServiceTrackerListener<T> {
+
+ /**
+ * Called when a new service is added to the tracked services.
+ *
+ * @param service the newly added service
+ */
+ void added(T service);
+
+ /**
+ * Called when a service is removed from the tracked services.
+ *
+ * @param service the removed service
+ */
+ void removed(T service);
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/util/Utils.java b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/util/Utils.java
new file mode 100644
index 0000000..7140cd1
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/main/java/org/apache/cxf/dosgi/topologymanager/util/Utils.java
@@ -0,0 +1,111 @@
+/**
+ * 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.topologymanager.util;
+
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+
+public final class Utils {
+
+ private static final String OBJECTCLASS_EXPRESSION = ".*\\(" + Constants.OBJECTCLASS + "=([a-zA-Z_0-9.]+)\\).*";
+ private static final Pattern OBJECTCLASS_PATTERN = Pattern.compile(OBJECTCLASS_EXPRESSION);
+
+ private Utils() {
+ // prevent instantiation
+ }
+
+ /**
+ * Retrieves an endpoint's properties as a Dictionary.
+ *
+ * @param endpoint an endpoint description
+ * @return endpoint properties (will never return null)
+ */
+ public static Dictionary<String, Object> getEndpointProperties(EndpointDescription endpoint) {
+ if (endpoint == null || endpoint.getProperties() == null) {
+ return new Hashtable<String, Object>();
+ } else {
+ return new Hashtable<String, Object>(endpoint.getProperties());
+ }
+ }
+
+ public static String getObjectClass(String filter) {
+ if (filter != null) {
+ Matcher matcher = OBJECTCLASS_PATTERN.matcher(filter);
+ if (matcher.matches() && matcher.groupCount() >= 1) {
+ return matcher.group(1);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the value of a "string+" property as an array of strings.
+ * <p>
+ * A "string+" property can have a value which is either a string,
+ * an array of strings, or a collection of strings.
+ * <p>
+ * If the given value is not of one of the valid types, or is null,
+ * an empty array is returned.
+ *
+ * @param property a "string+" property value
+ * @return the property value as an array of strings, or an empty array
+ */
+ public static String[] getStringPlusProperty(Object property) {
+ if (property instanceof String) {
+ return new String[] {(String)property};
+ } else if (property instanceof String[]) {
+ return (String[])property;
+ } else if (property instanceof Collection) {
+ try {
+ @SuppressWarnings("unchecked")
+ Collection<String> strings = (Collection<String>)property;
+ return strings.toArray(new String[strings.size()]);
+ } catch (ArrayStoreException ase) {
+ // ignore collections with wrong type
+ }
+ }
+ return new String[0];
+ }
+
+ public static String getUUID(BundleContext bctx) {
+ synchronized ("org.osgi.framework.uuid") {
+ String uuid = bctx.getProperty("org.osgi.framework.uuid");
+ if (uuid == null) {
+ uuid = UUID.randomUUID().toString();
+ System.setProperty("org.osgi.framework.uuid", uuid);
+ }
+ return uuid;
+ }
+ }
+
+ public static String getBundleName(ServiceReference sref) {
+ Bundle bundle = sref.getBundle();
+ return bundle == null ? "<unregistered>" : bundle.getSymbolicName();
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/exporter/EndpointListenerNotifierTest.java b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/exporter/EndpointListenerNotifierTest.java
new file mode 100644
index 0000000..b6fab7f
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/exporter/EndpointListenerNotifierTest.java
@@ -0,0 +1,183 @@
+/**
+ * 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.topologymanager.exporter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.easymock.IAnswer;
+import org.easymock.IMocksControl;
+import org.easymock.classextension.EasyMock;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+
+import static org.junit.Assert.assertEquals;
+
+public class EndpointListenerNotifierTest {
+
+ @Test
+ public void testNotifyListenersOfRemovalIfAppropriate() throws InvalidSyntaxException {
+ IMocksControl c = EasyMock.createNiceControl();
+
+ BundleContext bc = c.createMock(BundleContext.class);
+ ServiceReference sref = c.createMock(ServiceReference.class);
+ EndpointListener epl = EasyMock.createMock(EndpointListener.class);
+ EndpointDescription endpoint = c.createMock(EndpointDescription.class);
+ EndpointDescription endpoint2 = c.createMock(EndpointDescription.class);
+
+ Map<String, Object> props = new HashMap<String, Object>();
+ String[] oc = new String[1];
+ oc[0] = "myClass";
+ props.put("objectClass", oc);
+
+ Map<String, Object> props2 = new HashMap<String, Object>();
+ oc = new String[1];
+ oc[0] = "notMyClass";
+ props2.put("objectClass", oc);
+
+ EasyMock.expect(bc.getService(EasyMock.eq(sref))).andReturn(epl).anyTimes();
+ EasyMock.expect(bc.createFilter((String)EasyMock.anyObject())).andAnswer(new IAnswer<Filter>() {
+ public Filter answer() throws Throwable {
+ return FrameworkUtil.createFilter((String)EasyMock.getCurrentArguments()[0]);
+ }
+ }).anyTimes();
+ EasyMock.expect(sref.getProperty(EasyMock.eq(EndpointListener.ENDPOINT_LISTENER_SCOPE)))
+ .andReturn("(objectClass=myClass)").anyTimes();
+
+ EasyMock.expect(endpoint.getProperties()).andReturn(props).anyTimes();
+ EasyMock.expect(endpoint2.getProperties()).andReturn(props2).anyTimes();
+
+ // must only be called for the first EndpointDescription!
+ epl.endpointRemoved(EasyMock.eq(endpoint), EasyMock.eq("(objectClass=myClass)"));
+ EasyMock.expectLastCall().once();
+
+ EndpointRepository exportRepository = EasyMock.createMock(EndpointRepository.class);
+
+ c.replay();
+ EasyMock.replay(epl);
+
+ EndpointListenerNotifier tm = new EndpointListenerNotifier(bc, exportRepository);
+
+ List<EndpointDescription> endpoints = new ArrayList<EndpointDescription>();
+ endpoints.add(endpoint);
+ endpoints.add(endpoint2);
+
+ tm.notifyListener(false, sref, endpoints);
+
+ c.verify();
+ EasyMock.verify(epl);
+ }
+
+ @Test
+ public void testNormalizeScopeForSingleString() {
+ try {
+ ServiceReference sr = EasyMock.createMock(ServiceReference.class);
+ EasyMock.expect(sr.getProperty(EndpointListener.ENDPOINT_LISTENER_SCOPE))
+ .andReturn("Filterstring");
+
+ Filter f = EasyMock.createNiceMock(Filter.class);
+
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.createFilter((String)EasyMock.anyObject())).andReturn(f);
+
+ EasyMock.replay(sr);
+ EasyMock.replay(bc);
+
+ List<Filter> res = EndpointListenerNotifier.getFiltersFromEndpointListenerScope(sr, bc);
+
+ assertEquals(1, res.size());
+ assertEquals(f, res.get(0));
+
+ EasyMock.verify(sr);
+ EasyMock.verify(bc);
+ } catch (InvalidSyntaxException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void testNormalizeScopeForStringArray() {
+ try {
+ String[] filterStrings = {"f1", "f2", "f3"};
+
+ ServiceReference sr = EasyMock.createMock(ServiceReference.class);
+ EasyMock.expect(sr.getProperty(EndpointListener.ENDPOINT_LISTENER_SCOPE))
+ .andReturn(filterStrings);
+
+ Filter f = EasyMock.createNiceMock(Filter.class);
+
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.createFilter((String)EasyMock.anyObject())).andReturn(f).times(filterStrings.length);
+
+ EasyMock.replay(sr);
+ EasyMock.replay(bc);
+
+ List<Filter> res = EndpointListenerNotifier.getFiltersFromEndpointListenerScope(sr, bc);
+
+ assertEquals(filterStrings.length, res.size());
+ assertEquals(f, res.get(0));
+
+ EasyMock.verify(sr);
+ EasyMock.verify(bc);
+ } catch (InvalidSyntaxException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Test
+ public void testNormalizeScopeForCollection() {
+ try {
+ Collection<String> collection = new ArrayList<String>();
+ collection.add("f1");
+ collection.add("f2");
+ collection.add("f3");
+
+ ServiceReference sr = EasyMock.createMock(ServiceReference.class);
+ EasyMock.expect(sr.getProperty(EndpointListener.ENDPOINT_LISTENER_SCOPE))
+ .andReturn(collection);
+
+ Filter f = EasyMock.createNiceMock(Filter.class);
+
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.createFilter((String)EasyMock.anyObject())).andReturn(f).times(collection.size());
+
+ EasyMock.replay(sr);
+ EasyMock.replay(bc);
+
+ List<Filter> res = EndpointListenerNotifier.getFiltersFromEndpointListenerScope(sr, bc);
+
+ assertEquals(collection.size(), res.size());
+ assertEquals(f, res.get(0));
+
+ EasyMock.verify(sr);
+ EasyMock.verify(bc);
+ } catch (InvalidSyntaxException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/exporter/ExportServiceTest.java b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/exporter/ExportServiceTest.java
new file mode 100644
index 0000000..6d0140c
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/exporter/ExportServiceTest.java
@@ -0,0 +1,140 @@
+/**
+ * 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.topologymanager.exporter;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.topologymanager.util.SimpleServiceTracker;
+import org.apache.cxf.dosgi.topologymanager.util.SimpleServiceTrackerListener;
+import org.easymock.IAnswer;
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.EndpointDescription;
+import org.osgi.service.remoteserviceadmin.ExportReference;
+import org.osgi.service.remoteserviceadmin.ExportRegistration;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+
+public class ExportServiceTest {
+
+ /**
+ * This tests if the topology manager handles a service marked to be
+ * exported correctly by exporting it to an available RemoteServiceAdmin
+ * and notifying an EndpointListener afterwards.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testServiceExport() throws Exception {
+ IMocksControl c = EasyMock.createControl();
+
+ BundleContext bctx = c.createMock(BundleContext.class);
+ RemoteServiceAdmin rsa = c.createMock(RemoteServiceAdmin.class);
+ final EndpointListenerNotifier mockEpListenerNotifier = c.createMock(EndpointListenerNotifier.class);
+ mockEpListenerNotifier.start();
+ EasyMock.expectLastCall().once();
+
+ final ServiceReference sref = createUserServiceBundle(c);
+
+ EasyMock
+ .expect(bctx.getServiceReferences(EasyMock.<String> anyObject(), EasyMock.<String> anyObject()))
+ .andReturn(null).atLeastOnce();
+
+ SimpleServiceTracker<RemoteServiceAdmin> rsaTracker = createSingleRsaTracker(c, rsa);
+
+ EndpointDescription endpoint = createEndpoint(c);
+ ExportRegistration exportRegistration = createExportRegistration(c, endpoint);
+
+ // Main assertions
+ simulateUserServicePublished(bctx, sref);
+ EasyMock.expect(rsa.exportService(EasyMock.same(sref), (Map<String, Object>)EasyMock.anyObject()))
+ .andReturn(Collections.singletonList(exportRegistration)).once();
+ mockEpListenerNotifier.notifyListeners(EasyMock.eq(true), EasyMock.eq(Collections.singletonList(endpoint)));
+ EasyMock.expectLastCall().once();
+
+ c.replay();
+
+ TopologyManagerExport topManager = new TopologyManagerExport(bctx, rsaTracker, mockEpListenerNotifier) {
+ // override to perform export from the same thread rather than asynchronously
+ @Override
+ protected void triggerExport(ServiceReference sref) {
+ doExportService(sref);
+ }
+ };
+ topManager.start();
+ c.verify();
+ }
+
+ private void simulateUserServicePublished(BundleContext bctx, final ServiceReference sref) {
+ bctx.addServiceListener((ServiceListener)EasyMock.anyObject());
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+ public Object answer() throws Throwable {
+ System.out.println("Simulating publishing the user service");
+ ServiceListener sl = (ServiceListener)EasyMock.getCurrentArguments()[0];
+ ServiceEvent se = new ServiceEvent(ServiceEvent.REGISTERED, sref);
+ sl.serviceChanged(se);
+ return null;
+ }
+ }).once();
+ }
+
+ private SimpleServiceTracker<RemoteServiceAdmin> createSingleRsaTracker(IMocksControl c, RemoteServiceAdmin rsa) {
+ SimpleServiceTracker<RemoteServiceAdmin> rsaTracker = c.createMock(SimpleServiceTracker.class);
+ rsaTracker.addListener(EasyMock.<SimpleServiceTrackerListener> anyObject());
+ EasyMock.expectLastCall().once();
+ EasyMock.expect(rsaTracker.getAllServices()).andReturn(Collections.singletonList(rsa));
+ return rsaTracker;
+ }
+
+ private ExportRegistration createExportRegistration(IMocksControl c, EndpointDescription endpoint) {
+ ExportRegistration exportRegistration = c.createMock(ExportRegistration.class);
+ ExportReference exportReference = c.createMock(ExportReference.class);
+ EasyMock.expect(exportRegistration.getExportReference()).andReturn(exportReference).anyTimes();
+ EasyMock.expect(exportRegistration.getException()).andReturn(null).anyTimes();
+ EasyMock.expect(exportReference.getExportedEndpoint()).andReturn(endpoint).anyTimes();
+ return exportRegistration;
+ }
+
+ private EndpointDescription createEndpoint(IMocksControl c) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(RemoteConstants.ENDPOINT_ID, "1");
+ props.put(Constants.OBJECTCLASS, new String[] {"abc"});
+ props.put(RemoteConstants.SERVICE_IMPORTED_CONFIGS, "cxf");
+ return new EndpointDescription(props);
+ }
+
+ private ServiceReference createUserServiceBundle(IMocksControl c) {
+ final ServiceReference sref = c.createMock(ServiceReference.class);
+ EasyMock.expect(sref.getProperty(EasyMock.same(RemoteConstants.SERVICE_EXPORTED_INTERFACES)))
+ .andReturn("*").anyTimes();
+ Bundle srefBundle = c.createMock(Bundle.class);
+ EasyMock.expect(sref.getBundle()).andReturn(srefBundle).atLeastOnce();
+ EasyMock.expect(srefBundle.getSymbolicName()).andReturn("serviceBundleName").atLeastOnce();
+ return sref;
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/importer/EndpointListenerImplTest.java b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/importer/EndpointListenerImplTest.java
new file mode 100644
index 0000000..0e905ab
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/importer/EndpointListenerImplTest.java
@@ -0,0 +1,112 @@
+/**
+ * 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.topologymanager.importer;
+
+import java.util.Dictionary;
+import java.util.List;
+
+import org.easymock.IAnswer;
+import org.easymock.classextension.EasyMock;
+import org.junit.Assert;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.remoteserviceadmin.EndpointListener;
+
+public class EndpointListenerImplTest extends Assert {
+
+ int testCase;
+
+ @SuppressWarnings("rawtypes")
+ @Test
+ public void testScopeChange() {
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ TopologyManagerImport tm = EasyMock.createNiceMock(TopologyManagerImport.class);
+ ServiceRegistration sr = EasyMock.createNiceMock(ServiceRegistration.class);
+
+ // expect Listener registration
+ EasyMock.expect(bc.registerService((String)EasyMock.anyObject(),
+ EasyMock.anyObject(),
+ (Dictionary)EasyMock.anyObject())).andReturn(sr).atLeastOnce();
+
+ sr.setProperties((Dictionary)EasyMock.anyObject());
+
+ // expect property changes based on later calls
+ EasyMock.expectLastCall().andAnswer(new IAnswer<Object>() {
+
+ public Object answer() throws Throwable {
+ Object[] args = EasyMock.getCurrentArguments();
+ Dictionary props = (Dictionary)args[0];
+ @SuppressWarnings("unchecked")
+ List<String> scope = (List<String>)props.get(EndpointListener.ENDPOINT_LISTENER_SCOPE);
+ switch (testCase) {
+ case 1:
+ assertEquals(1, scope.size());
+ assertEquals("(a=b)", scope.get(0));
+ break;
+ case 2:
+ assertEquals(0, scope.size());
+ break;
+ case 3:
+ assertEquals("adding entry to empty list failed", 1, scope.size());
+ assertEquals("(a=b)", scope.get(0));
+ break;
+ case 4:
+ assertEquals("adding second entry failed", 2, scope.size());
+ assertNotNull(scope.contains("(a=b)"));
+ assertNotNull(scope.contains("(c=d)"));
+ break;
+ case 5:
+ assertEquals("remove failed", 1, scope.size());
+ assertEquals("(c=d)", scope.get(0));
+ break;
+ default:
+ assertTrue("This should not happen!", false);
+ }
+ return null;
+ }
+ }).atLeastOnce();
+
+ EasyMock.replay(bc);
+ EasyMock.replay(tm);
+ EasyMock.replay(sr);
+
+ EndpointListenerManager endpointListener = new EndpointListenerManager(bc, tm);
+
+ endpointListener.start();
+
+ testCase = 1;
+ endpointListener.extendScope("(a=b)");
+ testCase = 2;
+ endpointListener.reduceScope("(a=b)");
+
+ testCase = 3;
+ endpointListener.extendScope("(a=b)");
+ testCase = 4;
+ endpointListener.extendScope("(c=d)");
+ testCase = 5;
+ endpointListener.reduceScope("(a=b)");
+
+ endpointListener.stop();
+
+ EasyMock.verify(bc);
+ EasyMock.verify(tm);
+ EasyMock.verify(sr);
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/importer/ListenerHookImplTest.java b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/importer/ListenerHookImplTest.java
new file mode 100644
index 0000000..f2ff694
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/importer/ListenerHookImplTest.java
@@ -0,0 +1,56 @@
+/**
+ * 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.topologymanager.importer;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.easymock.classextension.EasyMock;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class ListenerHookImplTest {
+
+ @Test
+ public void testUUIDFilterExtension() throws InvalidSyntaxException {
+ String filter = "(a=b)";
+
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.getProperty(EasyMock.eq("org.osgi.framework.uuid"))).andReturn("MyUUID").atLeastOnce();
+ EasyMock.replay(bc);
+
+ filter = ListenerHookImpl.extendFilter(filter, bc);
+
+ Filter f = FrameworkUtil.createFilter(filter);
+
+ Dictionary<String, String> m = new Hashtable<String, String>();
+ m.put("a", "b");
+
+ assertTrue(filter + " filter must match as uuid is missing", f.match(m));
+ m.put(RemoteConstants.ENDPOINT_FRAMEWORK_UUID, "MyUUID");
+ assertFalse(filter + " filter must NOT match as uuid is the local one", f.match(m));
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/importer/TopologyManagerImportTest.java b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/importer/TopologyManagerImportTest.java
new file mode 100644
index 0000000..a43120a
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/importer/TopologyManagerImportTest.java
@@ -0,0 +1,91 @@
+/**
+ * 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.topologymanager.importer;
+
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.cxf.dosgi.topologymanager.util.SimpleServiceTracker;
+import org.apache.cxf.dosgi.topologymanager.util.SimpleServiceTrackerListener;
+import org.easymock.IAnswer;
+import org.easymock.IMocksControl;
+import org.easymock.classextension.EasyMock;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+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.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+
+import static org.junit.Assert.assertTrue;
+
+public class TopologyManagerImportTest {
+
+ @SuppressWarnings("rawtypes")
+ @Test
+ public void testImportForNewlyAddedRSA() throws InterruptedException {
+ IMocksControl c = EasyMock.createControl();
+
+ c.makeThreadSafe(true);
+
+ final Semaphore sema = new Semaphore(0);
+
+ BundleContext bc = c.createMock(BundleContext.class);
+ SimpleServiceTracker<RemoteServiceAdmin> rsaTracker = c.createMock(SimpleServiceTracker.class);
+ ServiceRegistration sreg = c.createMock(ServiceRegistration.class);
+ sreg.unregister();
+ EasyMock.expectLastCall().once();
+ EasyMock.expect(bc.registerService((String)EasyMock.anyObject(),
+ EasyMock.anyObject(),
+ (Dictionary)EasyMock.anyObject())).andReturn(sreg).anyTimes();
+
+ EndpointDescription endpoint = c.createMock(EndpointDescription.class);
+ RemoteServiceAdmin rsa = c.createMock(RemoteServiceAdmin.class);
+ final ImportRegistration ireg = c.createMock(ImportRegistration.class);
+ EasyMock.expect(ireg.getException()).andReturn(null).anyTimes();
+ ImportReference iref = c.createMock(ImportReference.class);
+
+ rsaTracker.addListener(EasyMock.<SimpleServiceTrackerListener>anyObject());
+ EasyMock.expect(rsaTracker.getAllServices()).andReturn(Arrays.asList(rsa)).anyTimes();
+
+ EasyMock.expect(rsa.importService(EasyMock.eq(endpoint))).andAnswer(new IAnswer<ImportRegistration>() {
+ public ImportRegistration answer() throws Throwable {
+ sema.release();
+ return ireg;
+ }
+ }).once();
+ EasyMock.expect(ireg.getImportReference()).andReturn(iref).anyTimes();
+ EasyMock.expect(iref.getImportedEndpoint()).andReturn(endpoint).anyTimes();
+ c.replay();
+
+ TopologyManagerImport tm = new TopologyManagerImport(bc, rsaTracker);
+
+ tm.start();
+ // no RSA available yet so no import...
+ tm.endpointAdded(endpoint, "myFilter");
+ tm.triggerImportsForRemoteServiceAdmin(rsa);
+ assertTrue("importService should have been called on RemoteServiceAdmin",
+ sema.tryAcquire(100, TimeUnit.SECONDS));
+ tm.stop();
+ c.verify();
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/util/ReferenceCounterTest.java b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/util/ReferenceCounterTest.java
new file mode 100644
index 0000000..8cdbb9b
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/util/ReferenceCounterTest.java
@@ -0,0 +1,42 @@
+/**
+ * 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.topologymanager.util;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class ReferenceCounterTest {
+
+ @Test
+ public void testCounter() {
+ ReferenceCounter<String> counter = new ReferenceCounter<String>();
+ assertEquals(-1, counter.remove("a"));
+ assertEquals(-1, counter.remove("a"));
+ assertEquals(1, counter.add("a"));
+ assertEquals(2, counter.add("a"));
+ assertEquals(3, counter.add("a"));
+ assertEquals(2, counter.remove("a"));
+ assertEquals(1, counter.remove("a"));
+ assertEquals(2, counter.add("a"));
+ assertEquals(1, counter.remove("a"));
+ assertEquals(0, counter.remove("a"));
+ assertEquals(-1, counter.remove("a"));
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/util/SimpleServiceTrackerTest.java b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/util/SimpleServiceTrackerTest.java
new file mode 100644
index 0000000..f73cdf0
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/util/SimpleServiceTrackerTest.java
@@ -0,0 +1,162 @@
+/**
+ * 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.topologymanager.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+import org.easymock.Capture;
+import org.easymock.IAnswer;
+import org.easymock.classextension.EasyMock;
+import org.easymock.classextension.IMocksControl;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.remoteserviceadmin.RemoteServiceAdmin;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+public class SimpleServiceTrackerTest {
+
+ private ServiceReference<RemoteServiceAdmin> createUserServiceBundle(IMocksControl c, BundleContext context) {
+ @SuppressWarnings("unchecked")
+ final ServiceReference<RemoteServiceAdmin> sref = c.createMock(ServiceReference.class);
+ Bundle srefBundle = c.createMock(Bundle.class);
+ expect(srefBundle.getBundleContext()).andReturn(context).anyTimes();
+ expect(sref.getBundle()).andReturn(srefBundle).anyTimes();
+ expect(srefBundle.getSymbolicName()).andReturn("serviceBundleName").anyTimes();
+ return sref;
+ }
+
+ private static <T> void assertEqualsUnordered(Collection<T> c1, Collection<T> c2) {
+ assertEquals(new HashSet<T>(c1), new HashSet<T>(c2));
+ }
+
+ @Test
+ public void testTracker() throws InvalidSyntaxException {
+ IMocksControl c = org.easymock.classextension.EasyMock.createControl();
+ // create context mock
+ BundleContext context = c.createMock(BundleContext.class);
+ // capture service listener so we can invoke it
+ Capture<ServiceListener> capturedListener = new Capture<ServiceListener>();
+ context.addServiceListener(EasyMock.<ServiceListener>capture(capturedListener), (String)anyObject());
+ expectLastCall().once();
+ context.removeServiceListener((ServiceListener)anyObject());
+ expectLastCall().once();
+ // support context.createFilter
+ Filter filter = c.createMock(Filter.class);
+ expect(context.createFilter("(objectClass=org.osgi.service.remoteserviceadmin.RemoteServiceAdmin)"))
+ .andReturn(filter).atLeastOnce();
+ // support context.getServiceReferences based on our list
+ final List<RemoteServiceAdmin> services = new ArrayList<RemoteServiceAdmin>();
+ final List<ServiceReference<RemoteServiceAdmin>> srefs = new ArrayList<ServiceReference<RemoteServiceAdmin>>();
+ expect(context.getServiceReferences((String)anyObject(), eq((String)null))).andAnswer(
+ new IAnswer<ServiceReference<?>[]>() {
+ @Override
+ public ServiceReference<?>[] answer() {
+ return srefs.toArray(new ServiceReference[srefs.size()]);
+ }
+ });
+ // create services
+ ServiceReference<RemoteServiceAdmin> sref1 = createUserServiceBundle(c, context);
+ ServiceReference<RemoteServiceAdmin> sref2 = createUserServiceBundle(c, context);
+ RemoteServiceAdmin service1 = c.createMock(RemoteServiceAdmin.class);
+ RemoteServiceAdmin service2 = c.createMock(RemoteServiceAdmin.class);
+ expect(context.getService(sref1)).andReturn(service1).atLeastOnce();
+ expect(context.getService(sref2)).andReturn(service2).atLeastOnce();
+ expect(context.ungetService(sref1)).andReturn(false).atLeastOnce();
+ expect(context.ungetService(sref2)).andReturn(false).atLeastOnce();
+
+ replay(context);
+
+ // start tracking
+ final SimpleServiceTracker<RemoteServiceAdmin> tracker =
+ new SimpleServiceTracker<RemoteServiceAdmin>(context, RemoteServiceAdmin.class);
+ tracker.open();
+ ServiceListener sl = capturedListener.getValue();
+ // add our listener
+ SimpleServiceTrackerListener<RemoteServiceAdmin> listener =
+ new SimpleServiceTrackerListener<RemoteServiceAdmin>() {
+ @SuppressWarnings({
+ "unchecked", "rawtypes"
+ })
+ @Override
+ public void added(RemoteServiceAdmin service) {
+ // prove that original ServiceTracker fails here
+ Object[] trackerServices = (Object[])
+ (tracker.getServices() != null ? tracker.getServices() : new Object[0]);
+ assertFalse(new HashSet(services)
+ .equals(new HashSet(Arrays.asList(trackerServices))));
+ // but we succeed
+ assertEqualsUnordered(services, tracker.getAllServices());
+ }
+
+ @Override
+ public void removed(RemoteServiceAdmin service) {
+ assertEqualsUnordered(services, tracker.getAllServices());
+ }
+ };
+ tracker.addListener(listener);
+
+ // add and remove services and verify that getAllServices() is up to date
+ srefs.add(sref1);
+ services.add(service1);
+ sl.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, sref1));
+ assertEqualsUnordered(services, tracker.getAllServices());
+
+ srefs.add(sref2);
+ services.add(service2);
+ sl.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, sref2));
+ assertEqualsUnordered(services, tracker.getAllServices());
+
+ srefs.remove(sref1);
+ services.remove(service1);
+ sl.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, sref1));
+ assertEqualsUnordered(services, tracker.getAllServices());
+
+ srefs.remove(sref2);
+ services.remove(service2);
+ sl.serviceChanged(new ServiceEvent(ServiceEvent.UNREGISTERING, sref2));
+ assertEqualsUnordered(services, tracker.getAllServices());
+
+ srefs.add(sref1);
+ services.add(service1);
+ sl.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, sref1));
+ services.remove(service1);
+ tracker.close();
+ assertEqualsUnordered(services, tracker.getAllServices());
+
+ verify(context);
+ }
+}
diff --git a/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/util/UtilsTest.java b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/util/UtilsTest.java
new file mode 100644
index 0000000..95b5dde
--- /dev/null
+++ b/trunk/dsw/cxf-topology-manager/src/test/java/org/apache/cxf/dosgi/topologymanager/util/UtilsTest.java
@@ -0,0 +1,54 @@
+/**
+ * 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.topologymanager.util;
+
+import org.easymock.classextension.EasyMock;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class UtilsTest {
+
+ @Test
+ public void testGetNewUUID() {
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.getProperty(EasyMock.eq("org.osgi.framework.uuid"))).andReturn(null).atLeastOnce();
+ EasyMock.replay(bc);
+ String uuid = Utils.getUUID(bc);
+ assertNotNull(uuid);
+
+ assertEquals(System.getProperty("org.osgi.framework.uuid"), uuid);
+
+ EasyMock.verify(bc);
+ }
+
+ @Test
+ public void testGetExistingUUID() {
+ BundleContext bc = EasyMock.createNiceMock(BundleContext.class);
+ EasyMock.expect(bc.getProperty(EasyMock.eq("org.osgi.framework.uuid"))).andReturn("MyUUID").atLeastOnce();
+ EasyMock.replay(bc);
+ String uuid = Utils.getUUID(bc);
+
+ assertEquals("MyUUID", uuid);
+
+ EasyMock.verify(bc);
+ }
+}
diff --git a/trunk/dsw/pom.xml b/trunk/dsw/pom.xml
new file mode 100644
index 0000000..4ab3dd9
--- /dev/null
+++ b/trunk/dsw/pom.xml
@@ -0,0 +1,45 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-dsw</artifactId>
+ <version>1.6.0</version>
+ <packaging>pom</packaging>
+ <name>Distributed OSGI Distributed Software Modules</name>
+ <url>http://cxf.apache.org</url>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ </properties>
+
+ <modules>
+ <module>cxf-dsw</module>
+ <module>cxf-topology-manager</module>
+ </modules>
+</project>
diff --git a/trunk/felix/pom.xml b/trunk/felix/pom.xml
new file mode 100644
index 0000000..0006a28
--- /dev/null
+++ b/trunk/felix/pom.xml
@@ -0,0 +1,42 @@
+<?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>
+ <artifactId>cxf-dosgi-ri-felix</artifactId>
+ <packaging>pom</packaging>
+ <name>Distributed OSGi Temporary Private Felix Framework Build</name>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>..</topDirectoryLocation>
+ </properties>
+
+ <modules>
+ <module>shell.tui</module>
+ <module>profiles</module>
+ </modules>
+
+</project>
diff --git a/trunk/felix/profiles/pom.xml b/trunk/felix/profiles/pom.xml
new file mode 100644
index 0000000..c862958
--- /dev/null
+++ b/trunk/felix/profiles/pom.xml
@@ -0,0 +1,201 @@
+<?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>
+ <artifactId>cxf-dosgi-ri-felix-profiles</artifactId>
+ <packaging>jar</packaging>
+ <name>Distributed OSGi Felix Profiles</name>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <topDirectoryLocation>../..</topDirectoryLocation>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <version>${jetty.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-util</artifactId>
+ <version>${jetty.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.stax-api-1.0</artifactId>
+ <version>${servicemix.specs.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.jaxb-api-2.1</artifactId>
+ <version>${servicemix.specs.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.jaxws-api-2.1</artifactId>
+ <version>${servicemix.specs.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.saaj-api-1.3</artifactId>
+ <version>${servicemix.specs.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-javamail_1.4_spec</artifactId>
+ <version>1.7.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-activation_1.1_spec</artifactId>
+ <version>1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-ws-metadata_2.0_spec</artifactId>
+ <version>1.1.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_3.0_spec</artifactId>
+ <version>1.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-annotation_1.0_spec</artifactId>
+ <version>1.1.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.ws.xmlschema</groupId>
+ <artifactId>xmlschema-core</artifactId>
+ <version>${xmlschema.bundle.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.xmlresolver</artifactId>
+ <version>${xmlresolver.bundle.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.neethi</groupId>
+ <artifactId>neethi</artifactId>
+ <version>${neethi.bundle.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.wsdl4j</artifactId>
+ <version>${wsdl4j.bundle.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.santuario</groupId>
+ <artifactId>xmlsec</artifactId>
+ <version>${xmlsec.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.jaxb-impl</artifactId>
+ <version>${jaxbimpl.bundle.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.asm</artifactId>
+ <version>${asm.bundle.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.codehaus.woodstox</groupId>
+ <artifactId>woodstox-core-asl</artifactId>
+ <version>${woodstox.bundle.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-beans</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ <version>${spring.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ <version>${spring.osgi.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-io</artifactId>
+ <version>${spring.osgi.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-extender</artifactId>
+ <version>${spring.osgi.version}</version>
+ </dependency>
+
+ <!-- for maven filtering to happen -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>src/main/resources/</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </build>
+
+</project>
diff --git a/trunk/felix/profiles/resources/OSGI-INF/remote-service/alt-remote-services.xml b/trunk/felix/profiles/resources/OSGI-INF/remote-service/alt-remote-services.xml
new file mode 100644
index 0000000..36409ce
--- /dev/null
+++ b/trunk/felix/profiles/resources/OSGI-INF/remote-service/alt-remote-services.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
+ <service-description>
+ <provide interface="org.apache.cxf.dosgi.dsw.hooks.TestService" />
+ <provide interface="org.apache.cxf.dosgi.dsw.hooks.CxfPublishHookTest$AdditionalInterface" />
+
+ <property name="osgi.remote.interfaces" value="*" />
+ <property name="osgi.remote.requires.intents" value="SOAP HTTP" />
+ <property name="osgi.remote.configuration.type" value="pojo" />
+ <property name="osgi.remote.configuration.pojo.address" value="http://localhost:9000/hello" />
+ </service-description>
+</service-descriptions>
diff --git a/trunk/felix/profiles/resources/OSGI-INF/remote-service/multi-services.xml b/trunk/felix/profiles/resources/OSGI-INF/remote-service/multi-services.xml
new file mode 100644
index 0000000..3d20c08
--- /dev/null
+++ b/trunk/felix/profiles/resources/OSGI-INF/remote-service/multi-services.xml
@@ -0,0 +1,38 @@
+<?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.
+-->
+
+<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
+ <service-description>
+ <provide interface="org.apache.cxf.dosgi.dsw.hooks.TestService" />
+ <provide interface="org.apache.cxf.dosgi.dsw.hooks.CxfPublishHookTest$AdditionalInterface" />
+ <property name="osgi.remote.interfaces">org.apache.cxf.dosgi.dsw.hooks.TestService,org.apache.cxf.dosgi.dsw.hooks.CxfPublishHookTest$AdditionalInterface</property>
+ <property name="osgi.remote.requires.intents">SOAP HTTP</property>
+ <property name="osgi.remote.configuration.type">pojo</property>
+ <property name="osgi.remote.configuration.pojo.address"
+ interface="org.apache.cxf.dosgi.dsw.hooks.TestService">
+ http://localhost:9001/hello
+ </property>
+ <property name="osgi.remote.configuration.pojo.address"
+ interface="org.apache.cxf.dosgi.dsw.hooks.CxfPublishHookTest$AdditionalInterface">
+ http://localhost:9002/hello
+ </property>
+ </service-description>
+</service-descriptions>
diff --git a/trunk/felix/profiles/resources/OSGI-INF/remote-service/remote-services.xml b/trunk/felix/profiles/resources/OSGI-INF/remote-service/remote-services.xml
new file mode 100644
index 0000000..45b2a20
--- /dev/null
+++ b/trunk/felix/profiles/resources/OSGI-INF/remote-service/remote-services.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
+ <service-description>
+ <provide interface="org.apache.cxf.dosgi.dsw.hooks.TestService" />
+ <provide interface="org.apache.cxf.dosgi.dsw.hooks.CxfPublishHookTest$AdditionalInterface" />
+
+ <property name="osgi.remote.interfaces">*</property>
+ <property name="osgi.remote.requires.intents">SOAP HTTP</property>
+ <property name="osgi.remote.configuration.type">pojo</property>
+ <property name="osgi.remote.configuration.pojo.address">http://localhost:9000/hello</property>
+ </service-description>
+</service-descriptions>
diff --git a/trunk/felix/profiles/resources/test-resources/rs1.xml b/trunk/felix/profiles/resources/test-resources/rs1.xml
new file mode 100644
index 0000000..f67a833
--- /dev/null
+++ b/trunk/felix/profiles/resources/test-resources/rs1.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
+ <service-description>
+ <provide interface="SomeService" />
+ <property name="osgi.remote.requires.intents">confidentiality</property>
+ </service-description>
+ <service-description>
+ <provide interface="SomeOtherService" />
+ <provide interface="WithSomeSecondInterface" />
+ </service-description>
+</service-descriptions>
diff --git a/trunk/felix/profiles/resources/test-resources/rs2.xml b/trunk/felix/profiles/resources/test-resources/rs2.xml
new file mode 100644
index 0000000..098aa21
--- /dev/null
+++ b/trunk/felix/profiles/resources/test-resources/rs2.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
+ <service-description>
+ <provide interface="org.example.Service" />
+ <property name="deployment.intents">confidentiality.message integrity</property>
+ <property name="osgi.remote.interfaces">*</property>
+ </service-description>
+</service-descriptions>
diff --git a/trunk/felix/profiles/resources/test-resources/sd-1.xml b/trunk/felix/profiles/resources/test-resources/sd-1.xml
new file mode 100644
index 0000000..483b196
--- /dev/null
+++ b/trunk/felix/profiles/resources/test-resources/sd-1.xml
@@ -0,0 +1,3 @@
+<test>
+ <some-other-tag/>
+</test>
\ No newline at end of file
diff --git a/trunk/felix/profiles/resources/test-resources/sd.xml b/trunk/felix/profiles/resources/test-resources/sd.xml
new file mode 100644
index 0000000..c7cebfb
--- /dev/null
+++ b/trunk/felix/profiles/resources/test-resources/sd.xml
@@ -0,0 +1,8 @@
+<service-decorations xmlns="http://cxf.apache.org/xmlns/service-decoration/1.0.0">
+ <service-decoration>
+ <match interface="org.acme.foo.*">
+ <match-property name="test.prop" value="xyz"/>
+ <add-property name="test.too" value="ahaha" type="java.lang.String"/>
+ </match>
+ </service-decoration>
+</service-decorations>
\ No newline at end of file
diff --git a/trunk/felix/profiles/resources/test-resources/sd0.xml b/trunk/felix/profiles/resources/test-resources/sd0.xml
new file mode 100644
index 0000000..0ad0ad1
--- /dev/null
+++ b/trunk/felix/profiles/resources/test-resources/sd0.xml
@@ -0,0 +1,2 @@
+<service-decorations xmlns="http://cxf.apache.org/xmlns/service-decoration/1.0.0">
+</service-decorations>
\ No newline at end of file
diff --git a/trunk/felix/profiles/resources/test-resources/sd1.xml b/trunk/felix/profiles/resources/test-resources/sd1.xml
new file mode 100644
index 0000000..6a5e811
--- /dev/null
+++ b/trunk/felix/profiles/resources/test-resources/sd1.xml
@@ -0,0 +1,8 @@
+<service-decorations xmlns="http://cxf.apache.org/xmlns/service-decoration/1.0.0">
+ <service-decoration>
+ <match interface="org.test.A">
+ <add-property name="A" value="B"/>
+ <add-property name="C" value="2" type="java.lang.Integer"/>
+ </match>
+ </service-decoration>
+</service-decorations>
\ No newline at end of file
diff --git a/trunk/felix/profiles/resources/test-resources/sd2.xml b/trunk/felix/profiles/resources/test-resources/sd2.xml
new file mode 100644
index 0000000..fb6a93a
--- /dev/null
+++ b/trunk/felix/profiles/resources/test-resources/sd2.xml
@@ -0,0 +1,14 @@
+<service-decorations xmlns="http://cxf.apache.org/xmlns/service-decoration/1.0.0">
+ <service-decoration>
+ <match interface="org.test.(B|C)">
+ <match-property name="x" value="y"/>
+ <add-property name="bool" value="true" type="java.lang.Boolean"/>
+ </match>
+ </service-decoration>
+ <service-decoration>
+ <match interface="org.test.(B|C)">
+ <match-property name="x" value="z"/>
+ <add-property name="bool" value="false" type="java.lang.Boolean"/>
+ </match>
+ </service-decoration>
+</service-decorations>
\ No newline at end of file
diff --git a/trunk/felix/profiles/src/main/java/felix/EmptyClass.java b/trunk/felix/profiles/src/main/java/felix/EmptyClass.java
new file mode 100644
index 0000000..58758f7
--- /dev/null
+++ b/trunk/felix/profiles/src/main/java/felix/EmptyClass.java
@@ -0,0 +1,22 @@
+/**
+ * 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 felix;
+
+public class EmptyClass {
+}
\ No newline at end of file
diff --git a/trunk/felix/profiles/src/main/resources/client_bundles.txt b/trunk/felix/profiles/src/main/resources/client_bundles.txt
new file mode 100644
index 0000000..0bf6534
--- /dev/null
+++ b/trunk/felix/profiles/src/main/resources/client_bundles.txt
@@ -0,0 +1,82 @@
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-annotation_1.0_spec/1.1.1/geronimo-annotation_1.0_spec-1.1.1.jar
+
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-activation_1.1_spec/1.1/geronimo-activation_1.1_spec-1.1.jar
+
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-javamail_1.4_spec/1.7.1/geronimo-javamail_1.4_spec-1.7.1.jar
+
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-ws-metadata_2.0_spec/1.1.3/geronimo-ws-metadata_2.0_spec-1.1.3.jar
+
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-servlet_3.0_spec/1.0/geronimo-servlet_3.0_spec-1.0.jar
+
+start file:${maven.repo.local}/org/apache/commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar
+
+start file:${maven.repo.local}/org/jdom/com.springsource.org.jdom/1.1.0/com.springsource.org.jdom-1.1.0.jar
+
+start file:${maven.repo.local}/org/jaxen/com.springsource.org.jaxen/1.1.1/com.springsource.org.jaxen-1.1.1.jar
+
+start file:${maven.repo.local}/org/springframework/spring-core/${spring.version}/spring-core-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/spring-beans/${spring.version}/spring-beans-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/spring-context/${spring.version}/spring-context-${spring.version}.jar
+
+start file:${maven.repo.local}/org/aopalliance/com.springsource.org.aopalliance/1.0.0/com.springsource.org.aopalliance-1.0.0.jar
+
+start file:${maven.repo.local}/org/springframework/spring-aop/${spring.version}/spring-aop-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/spring-expression/${spring.version}/spring-expression-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/spring-asm/${spring.version}/spring-asm-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/osgi/spring-osgi-io/${spring.osgi.version}/spring-osgi-io-${spring.osgi.version}.jar
+
+start file:${maven.repo.local}/org/springframework/osgi/spring-osgi-core/${spring.osgi.version}/spring-osgi-core-${spring.osgi.version}.jar
+
+start file:${maven.repo.local}/org/springframework/osgi/spring-osgi-extender/${spring.osgi.version}/spring-osgi-extender-${spring.osgi.version}.jar
+
+start file:${maven.repo.local}/org/eclipse/jetty/aggregate/jetty-all-server/${jetty.version}/jetty-all-server-${jetty.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.locator/${servicemix.specs.version}/org.apache.servicemix.specs.locator-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.saaj-api-1.3/${servicemix.specs.version}/org.apache.servicemix.specs.saaj-api-1.3-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.stax-api-1.0/${servicemix.specs.version}/org.apache.servicemix.specs.stax-api-1.0-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.jaxb-api-2.1/${servicemix.specs.version}/org.apache.servicemix.specs.jaxb-api-2.1-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.jaxws-api-2.1/${servicemix.specs.version}/org.apache.servicemix.specs.jaxws-api-2.1-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.jaxb-impl/${jaxbimpl.bundle.version}/org.apache.servicemix.bundles.jaxb-impl-${jaxbimpl.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.wsdl4j/${wsdl4j.bundle.version}/org.apache.servicemix.bundles.wsdl4j-${wsdl4j.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.xmlsec/${xmlsec.bundle.version}/org.apache.servicemix.bundles.xmlsec-${xmlsec.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.wss4j/${wss4j.bundle.version}/org.apache.servicemix.bundles.wss4j-${wss4j.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/sws/xmlschema/xmlschema-core/${xmlschema.bundle.version}/xmlschema-core-${xmlschema.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.asm/${asm.bundle.version}/org.apache.servicemix.bundles.asm-${asm.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.xmlresolver/${xmlresolver.bundle.version}/org.apache.servicemix.bundles.xmlresolver-${xmlresolver.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/neethi/neethi/${neethi.bundle.version}/neethi-${neethi.bundle.version}.jar
+
+start file:${maven.repo.local}/org/codehaus/woodstox/stax2-api/3.1.1/stax2-api-3.1.1.jar
+
+start file:${maven.repo.local}/org/codehaus/woodstox/woodstox-core-asl/${woodstox.bundle.version}/woodstox-core-asl-${woodstox.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.joda-time/1.5.2_4/org.apache.servicemix.bundles.joda-time-1.5.2_4.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.opensaml/2.4.1_1/org.apache.servicemix.bundles.opensaml-2.4.1_1.jar
+
+start file:${maven.repo.local}/org/apache/cxf/cxf-bundle-minimal/${cxf.version}/cxf-bundle-minimal-${cxf.version}.jar
+
+start file:${maven.repo.local}/org/apache/cxf/cxf-dosgi-ri-discovery-local/${project.version}/cxf-dosgi-ri-discovery-local-${project.version}.jar
+
+start file:${maven.repo.local}/org/apache/cxf/cxf-dosgi-ri-dsw-cxf/${project.version}/cxf-dosgi-ri-dsw-cxf-${project.version}.jar
+
+start file:${maven.repo.local}/org/apache/cxf/cxf-dosgi-ri-samples-greeter-interface/${project.version}/cxf-dosgi-ri-samples-greeter-interface-${project.version}.jar
+
+start file:${maven.repo.local}/org/apache/cxf/cxf-dosgi-ri-samples-greeter-client/${project.version}/cxf-dosgi-ri-samples-greeter-client-${project.version}.jar
+
diff --git a/trunk/felix/profiles/src/main/resources/dosgi_bundles.txt b/trunk/felix/profiles/src/main/resources/dosgi_bundles.txt
new file mode 100644
index 0000000..8506239
--- /dev/null
+++ b/trunk/felix/profiles/src/main/resources/dosgi_bundles.txt
@@ -0,0 +1,78 @@
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-annotation_1.0_spec/1.1.1/geronimo-annotation_1.0_spec-1.1.1.jar
+
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-activation_1.1_spec/1.1/geronimo-activation_1.1_spec-1.1.jar
+
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-javamail_1.4_spec/1.7.1/geronimo-javamail_1.4_spec-1.7.1.jar
+
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-ws-metadata_2.0_spec/1.1.3/geronimo-ws-metadata_2.0_spec-1.1.3.jar
+
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-servlet_3.0_spec/1.0/geronimo-servlet_3.0_spec-1.0.jar
+
+start file:${maven.repo.local}/org/apache/commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar
+
+start file:${maven.repo.local}/org/jdom/com.springsource.org.jdom/1.1.0/com.springsource.org.jdom-1.1.0.jar
+
+start file:${maven.repo.local}/org/jaxen/com.springsource.org.jaxen/1.1.1/com.springsource.org.jaxen-1.1.1.jar
+
+start file:${maven.repo.local}/org/springframework/spring-core/${spring.version}/spring-core-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/spring-beans/${spring.version}/spring-beans-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/spring-context/${spring.version}/spring-context-${spring.version}.jar
+
+start file:${maven.repo.local}/org/aopalliance/com.springsource.org.aopalliance/1.0.0/com.springsource.org.aopalliance-1.0.0.jar
+
+start file:${maven.repo.local}/org/springframework/spring-aop/${spring.version}/spring-aop-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/spring-expression/${spring.version}/spring-expression-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/spring-asm/${spring.version}/spring-asm-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/osgi/spring-osgi-io/${spring.osgi.version}/spring-osgi-io-${spring.osgi.version}.jar
+
+start file:${maven.repo.local}/org/springframework/osgi/spring-osgi-core/${spring.osgi.version}/spring-osgi-core-${spring.osgi.version}.jar
+
+start file:${maven.repo.local}/org/springframework/osgi/spring-osgi-extender/${spring.osgi.version}/spring-osgi-extender-${spring.osgi.version}.jar
+
+start file:${maven.repo.local}/org/eclipse/jetty/aggregate/jetty-all-server/${jetty.version}/jetty-all-server-${jetty.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.locator/${servicemix.specs.version}/org.apache.servicemix.specs.locator-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.saaj-api-1.3/${servicemix.specs.version}/org.apache.servicemix.specs.saaj-api-1.3-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.stax-api-1.0/${servicemix.specs.version}/org.apache.servicemix.specs.stax-api-1.0-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.jaxb-api-2.1/${servicemix.specs.version}/org.apache.servicemix.specs.jaxb-api-2.1-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.jaxws-api-2.1/${servicemix.specs.version}/org.apache.servicemix.specs.jaxws-api-2.1-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.jaxb-impl/${jaxbimpl.bundle.version}/org.apache.servicemix.bundles.jaxb-impl-${jaxbimpl.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.wsdl4j/${wsdl4j.bundle.version}/org.apache.servicemix.bundles.wsdl4j-${wsdl4j.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.xmlsec/${xmlsec.bundle.version}/org.apache.servicemix.bundles.xmlsec-${xmlsec.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.wss4j/${wss4j.bundle.version}/org.apache.servicemix.bundles.wss4j-${wss4j.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/sws/xmlschema/xmlschema-core/${xmlschema.bundle.version}/xmlschema-core-${xmlschema.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.asm/${asm.bundle.version}/org.apache.servicemix.bundles.asm-${asm.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.xmlresolver/${xmlresolver.bundle.version}/org.apache.servicemix.bundles.xmlresolver-${xmlresolver.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/neethi/neethi/${neethi.bundle.version}/neethi-${neethi.bundle.version}.jar
+
+start file:${maven.repo.local}/org/codehaus/woodstox/stax2-api/3.1.1/stax2-api-3.1.1.jar
+
+start file:${maven.repo.local}/org/codehaus/woodstox/woodstox-core-asl/${woodstox.bundle.version}/woodstox-core-asl-${woodstox.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.joda-time/1.5.2_4/org.apache.servicemix.bundles.joda-time-1.5.2_4.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.opensaml/2.4.1_1/org.apache.servicemix.bundles.opensaml-2.4.1_1.jar
+
+start file:${maven.repo.local}/org/apache/cxf/cxf-bundle-minimal/${cxf.version}/cxf-bundle-minimal-${cxf.version}.jar
+
+start file:${maven.repo.local}/org/apache/cxf/cxf-dosgi-ri-discovery-local/${project.version}/cxf-dosgi-ri-discovery-local-${project.version}.jar
+
+start file:${maven.repo.local}/org/apache/cxf/cxf-dosgi-ri-dsw-cxf/${project.version}/cxf-dosgi-ri-dsw-cxf-${project.version}.jar
+
diff --git a/trunk/felix/profiles/src/main/resources/server_bundles.txt b/trunk/felix/profiles/src/main/resources/server_bundles.txt
new file mode 100644
index 0000000..b15a054
--- /dev/null
+++ b/trunk/felix/profiles/src/main/resources/server_bundles.txt
@@ -0,0 +1,82 @@
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-annotation_1.0_spec/1.1.1/geronimo-annotation_1.0_spec-1.1.1.jar
+
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-activation_1.1_spec/1.1/geronimo-activation_1.1_spec-1.1.jar
+
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-javamail_1.4_spec/1.7.1/geronimo-javamail_1.4_spec-1.7.1.jar
+
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-ws-metadata_2.0_spec/1.1.3/geronimo-ws-metadata_2.0_spec-1.1.3.jar
+
+start file:${maven.repo.local}/org/apache/geronimo/specs/geronimo-servlet_3.0_spec/1.0/geronimo-servlet_3.0_spec-1.0.jar
+
+start file:${maven.repo.local}/org/apache/commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar
+
+start file:${maven.repo.local}/org/jdom/com.springsource.org.jdom/1.1.0/com.springsource.org.jdom-1.1.0.jar
+
+start file:${maven.repo.local}/org/jaxen/com.springsource.org.jaxen/1.1.1/com.springsource.org.jaxen-1.1.1.jar
+
+start file:${maven.repo.local}/org/springframework/spring-core/${spring.version}/spring-core-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/spring-beans/${spring.version}/spring-beans-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/spring-context/${spring.version}/spring-context-${spring.version}.jar
+
+start file:${maven.repo.local}/org/aopalliance/com.springsource.org.aopalliance/1.0.0/com.springsource.org.aopalliance-1.0.0.jar
+
+start file:${maven.repo.local}/org/springframework/spring-aop/${spring.version}/spring-aop-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/spring-expression/${spring.version}/spring-expression-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/spring-asm/${spring.version}/spring-asm-${spring.version}.jar
+
+start file:${maven.repo.local}/org/springframework/osgi/spring-osgi-io/${spring.osgi.version}/spring-osgi-io-${spring.osgi.version}.jar
+
+start file:${maven.repo.local}/org/springframework/osgi/spring-osgi-core/${spring.osgi.version}/spring-osgi-core-${spring.osgi.version}.jar
+
+start file:${maven.repo.local}/org/springframework/osgi/spring-osgi-extender/${spring.osgi.version}/spring-osgi-extender-${spring.osgi.version}.jar
+
+start file:${maven.repo.local}/org/eclipse/jetty/aggregate/jetty-all-server/${jetty.version}/jetty-all-server-${jetty.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.locator/${servicemix.specs.version}/org.apache.servicemix.specs.locator-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.saaj-api-1.3/${servicemix.specs.version}/org.apache.servicemix.specs.saaj-api-1.3-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.stax-api-1.0/${servicemix.specs.version}/org.apache.servicemix.specs.stax-api-1.0-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.jaxb-api-2.1/${servicemix.specs.version}/org.apache.servicemix.specs.jaxb-api-2.1-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/specs/org.apache.servicemix.specs.jaxws-api-2.1/${servicemix.specs.version}/org.apache.servicemix.specs.jaxws-api-2.1-${servicemix.specs.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.jaxb-impl/${jaxbimpl.bundle.version}/org.apache.servicemix.bundles.jaxb-impl-${jaxbimpl.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.wsdl4j/${wsdl4j.bundle.version}/org.apache.servicemix.bundles.wsdl4j-${wsdl4j.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.xmlsec/${xmlsec.bundle.version}/org.apache.servicemix.bundles.xmlsec-${xmlsec.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.wss4j/${wss4j.bundle.version}/org.apache.servicemix.bundles.wss4j-${wss4j.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/sws/xmlschema/xmlschema-core/${xmlschema.bundle.version}/xmlschema-core-${xmlschema.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.asm/${asm.bundle.version}/org.apache.servicemix.bundles.asm-${asm.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.xmlresolver/${xmlresolver.bundle.version}/org.apache.servicemix.bundles.xmlresolver-${xmlresolver.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/neethi/neethi/${neethi.bundle.version}/neethi-${neethi.bundle.version}.jar
+
+start file:${maven.repo.local}/org/codehaus/woodstox/stax2-api/3.1.1/stax2-api-3.1.1.jar
+
+start file:${maven.repo.local}/org/codehaus/woodstox/woodstox-core-asl/${woodstox.bundle.version}/woodstox-core-asl-${woodstox.bundle.version}.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.joda-time/1.5.2_4/org.apache.servicemix.bundles.joda-time-1.5.2_4.jar
+
+start file:${maven.repo.local}/org/apache/servicemix/bundles/org.apache.servicemix.bundles.opensaml/2.4.1_1/org.apache.servicemix.bundles.opensaml-2.4.1_1.jar
+
+start file:${maven.repo.local}/org/apache/cxf/cxf-bundle-minimal/${cxf.version}/cxf-bundle-minimal-${cxf.version}.jar
+
+start file:${maven.repo.local}/org/apache/cxf/cxf-dosgi-ri-discovery-local/${project.version}/cxf-dosgi-ri-discovery-local-${project.version}.jar
+
+start file:${maven.repo.local}/org/apache/cxf/cxf-dosgi-ri-dsw-cxf/${project.version}/cxf-dosgi-ri-dsw-cxf-${project.version}.jar
+
+start file:${maven.repo.local}/org/apache/cxf/cxf-dosgi-ri-samples-greeter-interface/${project.version}/cxf-dosgi-ri-samples-greeter-interface-${project.version}.jar
+
+start file:${maven.repo.local}/org/apache/cxf/cxf-dosgi-ri-samples-greeter-impl/${project.version}/cxf-dosgi-ri-samples-greeter-impl-${project.version}.jar
+
diff --git a/trunk/felix/remote-debug.txt b/trunk/felix/remote-debug.txt
new file mode 100644
index 0000000..229262b
--- /dev/null
+++ b/trunk/felix/remote-debug.txt
@@ -0,0 +1 @@
+-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
diff --git a/trunk/felix/shell.tui/LICENSE b/trunk/felix/shell.tui/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/trunk/felix/shell.tui/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
diff --git a/trunk/felix/shell.tui/NOTICE b/trunk/felix/shell.tui/NOTICE
new file mode 100644
index 0000000..d96621a
--- /dev/null
+++ b/trunk/felix/shell.tui/NOTICE
@@ -0,0 +1,20 @@
+Apache Felix Shell Textual Interface
+Copyright 2006-2008 The Apache Software Foundation
+
+I. Included Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+
+II. Used Software
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright 2006 The OSGi Alliance.
+Licensed under the Apache License 2.0.
+
+
+III. License Summary
+- Apache License 2.0
diff --git a/trunk/felix/shell.tui/pom.xml b/trunk/felix/shell.tui/pom.xml
new file mode 100644
index 0000000..933ab2b
--- /dev/null
+++ b/trunk/felix/shell.tui/pom.xml
@@ -0,0 +1,92 @@
+<?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>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGi Temporary Private Apache Felix Shell TUI Build</name>
+ <description>A simple textual user interface for Felix' shell service.</description>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>felix-shell-extension</artifactId>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <maven.test.skip>true</maven.test.skip>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>1.0.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.shell</artifactId>
+ <version>1.0.1</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>2.8</version>
+ <configuration>
+ <excludes>**/*.java</excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ <version>2.6</version>
+ <configuration>
+ <excludes>
+ <exclude>**/*.java</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>1.4.0</version>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Private-Package>org.apache.felix.shell.tui.*</Private-Package>
+ <Bundle-Activator>org.apache.felix.shell.tui.Activator</Bundle-Activator>
+ <Bundle-DocURL>http://oscar-osgi.sf.net/obr2/${project.artifactId}/</Bundle-DocURL>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
+ <Import-Service>org.apache.felix.shell.ShellService</Import-Service>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/felix/shell.tui/src/main/java/org/apache/felix/shell/tui/Activator.java b/trunk/felix/shell.tui/src/main/java/org/apache/felix/shell/tui/Activator.java
new file mode 100644
index 0000000..c41ca32
--- /dev/null
+++ b/trunk/felix/shell.tui/src/main/java/org/apache/felix/shell/tui/Activator.java
@@ -0,0 +1,187 @@
+/**
+ * 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.felix.shell.tui;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import org.apache.felix.shell.ShellService;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+
+public class Activator implements BundleActivator {
+
+ private static final String RUN_CMD = "run ";
+
+ private BundleContext context;
+ private ShellTuiRunnable shellRunnable;
+ private Thread thread;
+ private ServiceListener listener;
+ private ServiceReference shellRef;
+ private ShellService shell;
+
+ public void start(BundleContext bcontext) {
+ context = bcontext;
+
+ // Listen for registering/unregistering impl service.
+ listener = new ShellServiceListener();
+ try {
+ context.addServiceListener(listener,
+ "(objectClass=" + org.apache.felix.shell.ShellService.class.getName() + ")");
+ } catch (InvalidSyntaxException ex) {
+ System.err.println("ShellTui: Cannot add service listener.");
+ System.err.println("ShellTui: " + ex);
+ }
+
+ // Now try to manually initialize the impl service
+ // since one might already be available.
+ initializeService();
+
+ // Start impl thread.
+ shellRunnable = new ShellTuiRunnable();
+ thread = new Thread(shellRunnable, "Felix Shell TUI");
+ thread.start();
+ }
+
+ public void stop(BundleContext bcontext) {
+ if (shellRunnable != null) {
+ shellRunnable.stop();
+ thread.interrupt();
+ }
+ if (listener != null) {
+ context.removeServiceListener(listener);
+ }
+ uninitializeService();
+ }
+
+ private synchronized void initializeService() {
+ if (shell != null) {
+ return;
+ }
+ shellRef = context.getServiceReference(org.apache.felix.shell.ShellService.class.getName());
+ if (shellRef == null) {
+ return;
+ }
+ shell = (ShellService)context.getService(shellRef);
+ }
+
+ private synchronized void uninitializeService() {
+ if (shellRef != null) {
+ context.ungetService(shellRef);
+ }
+ shellRef = null;
+ shell = null;
+ }
+
+ private class ShellServiceListener implements ServiceListener {
+ public void serviceChanged(ServiceEvent event) {
+ synchronized (Activator.this) {
+ if (event.getType() == ServiceEvent.REGISTERED) {
+ // Ignore additional services if we already have one.
+ if (shellRef == null) {
+ // Initialize the service if we don't have one.
+ initializeService();
+ }
+ } else if (event.getType() == ServiceEvent.UNREGISTERING
+ && event.getServiceReference().equals(shellRef)) {
+ // Unget the service if it is unregistering.
+ uninitializeService();
+ // Try to get another service.
+ initializeService();
+ }
+ }
+ }
+ }
+
+ private class ShellTuiRunnable implements Runnable {
+
+ private volatile boolean stop;
+
+ public void stop() {
+ stop = true;
+ }
+
+ public void run() {
+ String line;
+ BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+
+ while (!stop) {
+ System.out.print("-> ");
+
+ try {
+ line = in.readLine();
+ } catch (IOException ex) {
+ System.err.println("Could not read input, please try again.");
+ continue;
+ }
+
+ synchronized (Activator.this) {
+ if (shell == null) {
+ System.out.println("No impl service available.");
+ continue;
+ }
+
+ if (line == null) {
+ continue;
+ }
+
+ line = line.trim();
+
+ if (line.isEmpty()) {
+ continue;
+ }
+
+ try {
+ if (line.startsWith(RUN_CMD)) {
+ String path = line.substring(RUN_CMD.length()).trim();
+ System.out.println("loading commands from: " + path);
+ File commands = new File(path);
+ if (commands.exists()) {
+ BufferedReader reader = new BufferedReader(new FileReader(commands));
+ String command = reader.readLine().trim();
+ while (command != null) {
+ if (!command.isEmpty()) {
+ System.out.println("\nexecuting: " + command);
+ shell.executeCommand(command.trim(), System.out, System.err);
+ }
+ command = reader.readLine();
+ }
+ reader.close();
+ } else {
+ System.err.println(path + " not found");
+ }
+ } else {
+ shell.executeCommand(line, System.out, System.err);
+ }
+ } catch (Exception ex) {
+ System.err.println("ShellTui: " + ex);
+ ex.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/trunk/osgi-api/pom.xml b/trunk/osgi-api/pom.xml
new file mode 100644
index 0000000..4ad1bb4
--- /dev/null
+++ b/trunk/osgi-api/pom.xml
@@ -0,0 +1,69 @@
+<?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>
+ <artifactId>cxf-dosgi-ri-osgi-api</artifactId>
+ <packaging>bundle</packaging>
+ <name>CXF dOSGi Required OSGi Compendium APIs</name>
+ <description>The minimal set of OSGi Compendium APIs required by DOSGi</description>
+ <!--
+ 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.
+ -->
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <!-- the minimal set of OSGi Compendium packages that is required for DOSGi to run -->
+ <required-packages>org.osgi.service.remoteserviceadmin,org.osgi.service.event</required-packages>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <!-- we must both import and export to prevent trouble if another bundle exports them too -->
+ <Import-Package>${required-packages},*</Import-Package>
+ <Export-Package>${required-packages}</Export-Package>
+ <!-- embed the packages in this bundle -->
+ <Private-Package>${required-packages}</Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/trunk/parent/pom.xml b/trunk/parent/pom.xml
new file mode 100644
index 0000000..4284763
--- /dev/null
+++ b/trunk/parent/pom.xml
@@ -0,0 +1,394 @@
+<?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">
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri</artifactId>
+ <version>1.6.0</version>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+
+ <packaging>pom</packaging>
+ <name>Distributed OSGI Reference Implementation Parent</name>
+
+ <!-- Some versions of libraries used are different to the ones from CXF,
+ this is generally caused by the fact that not all versions are available
+ yet as OSGi bundles. -->
+
+ <!-- Note: the Felix Framework bundle contains the org.osgi core packages,
+ which may override the standalone org.osgi dependency bundles - so if the
+ org.osgi bundle versions are modified below, the corresponding Felix Framework
+ version must be updated as well. -->
+
+ <properties>
+ <!-- portable text resource processing -->
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
+ <osgi.version>4.3.1</osgi.version>
+ <osgi.compendium.version>4.3.1</osgi.compendium.version>
+ <cxf.version>2.7.8</cxf.version>
+ <cxf.build-utils.version>2.5.0</cxf.build-utils.version>
+ <felix.version>4.2.1</felix.version>
+ <spring.version>3.0.6.RELEASE</spring.version>
+ <spring.osgi.version>1.2.1</spring.osgi.version>
+ <zookeeper.version>3.3.2</zookeeper.version>
+
+ <remote.service.admin.interfaces.version>1.0.0</remote.service.admin.interfaces.version>
+
+ <servicemix.specs.version>2.2.0</servicemix.specs.version>
+ <pax.web.version>1.0.11</pax.web.version>
+ <pax.logging.version>1.7.0</pax.logging.version>
+ <servlet.version>3.0</servlet.version>
+ <log4j.version>1.2.17</log4j.version>
+ <jetty.version>7.4.2.v20110526</jetty.version>
+ <xmlschema.bundle.version>2.0.3</xmlschema.bundle.version>
+ <xmlresolver.bundle.version>1.2_5</xmlresolver.bundle.version>
+ <neethi.bundle.version>3.0.2</neethi.bundle.version>
+ <wsdl4j.bundle.version>1.6.2_6</wsdl4j.bundle.version>
+ <xmlsec.version>1.5.3</xmlsec.version>
+ <asm.bundle.version>3.3.1_1</asm.bundle.version>
+ <commons.pool.bundle.version>1.5.4_1</commons.pool.bundle.version>
+ <woodstox.bundle.version>4.1.4</woodstox.bundle.version>
+ <jaxbimpl.bundle.version>2.2.1.1_2</jaxbimpl.bundle.version>
+ <slf4j.version>1.7.0</slf4j.version>
+ <felix.plugin.version>2.4.0</felix.plugin.version>
+ <exam.version>3.2.0</exam.version>
+ <cxf.resources.base.path />
+ <cxf.checkstyle.extension />
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>${osgi.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>${osgi.compendium.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-jcl</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.7</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>2.5.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <version>2.5.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>xmlunit</groupId>
+ <artifactId>xmlunit</artifactId>
+ <version>1.4</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-atinject_1.0_spec</artifactId>
+ <version>1.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit4</artifactId>
+ <version>${exam.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-inject</artifactId>
+ <version>${exam.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-forked</artifactId>
+ <version>${exam.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-link-mvn</artifactId>
+ <version>${exam.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse</groupId>
+ <artifactId>org.eclipse.osgi</artifactId>
+ <version>3.8.0.v20120529-1548</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.framework</artifactId>
+ <version>${felix.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.ops4j.pax.tinybundles</groupId>
+ <artifactId>tinybundles</artifactId>
+ <version>2.0.0</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <build>
+ <defaultGoal>install</defaultGoal>
+
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>${felix.plugin.version}</version>
+ <extensions>true</extensions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.2.1</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.8.1</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.5.1</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ <maxmem>256M</maxmem>
+ <fork>false</fork>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.6</version>
+ <dependencies>
+ <dependency>
+ <groupId>ant</groupId>
+ <artifactId>ant-trax</artifactId>
+ <version>1.6.5</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.5</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.8</version>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-remote-resources-plugin</artifactId>
+ <version>1.2</version>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf.build-utils</groupId>
+ <artifactId>cxf-buildtools</artifactId>
+ <version>${cxf.build-utils.version}</version>
+ </dependency>
+ </dependencies>
+ <executions>
+ <execution>
+ <goals>
+ <goal>process</goal>
+ </goals>
+ <configuration>
+ <resourceBundles>
+ <resourceBundle>org.apache:apache-jar-resource-bundle:1.4</resourceBundle>
+ </resourceBundles>
+ <supplementalModels>
+ <supplementalModel>notice-supplements.xml</supplementalModel>
+ </supplementalModels>
+ <properties>
+ <projectName>Apache CXF Distributed
+ OSGi DSW Reference
+ Implementation</projectName>
+ </properties>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-eclipse-plugin</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf.build-utils</groupId>
+ <artifactId>cxf-buildtools</artifactId>
+ <version>${cxf.build-utils.version}</version>
+ </dependency>
+ </dependencies>
+ <configuration>
+ <buildcommands>
+ <java.lang.String>org.eclipse.jdt.core.javabuilder</java.lang.String>
+ <java.lang.String>net.sf.eclipsecs.core.CheckstyleBuilder</java.lang.String>
+ <java.lang.String>net.sourceforge.pmd.eclipse.plugin.pmdBuilder</java.lang.String>
+ </buildcommands>
+ <projectnatures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
+ <nature>net.sourceforge.pmd.eclipse.plugin.pmdNature</nature>
+ </projectnatures>
+ <ajdtVersion>none</ajdtVersion>
+ <additionalConfig>
+ <file>
+ <name>.checkstyle</name>
+ <location>/cxf-eclipse-checkstyle</location>
+ </file>
+ <file>
+ <name>.pmd</name>
+ <location>/cxf-eclipse-pmd</location>
+ </file>
+ <file>
+ <name>.ruleset</name>
+ <location>/cxf-pmd-ruleset.xml</location>
+ </file>
+ </additionalConfig>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-remote-resources-plugin</artifactId>
+ <version>1.2</version>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf.build-utils</groupId>
+ <artifactId>cxf-buildtools</artifactId>
+ <version>${cxf.build-utils.version}</version>
+ </dependency>
+ </dependencies>
+ <executions>
+ <execution>
+ <goals>
+ <goal>process</goal>
+ </goals>
+ <configuration>
+ <resourceBundles>
+ <resourceBundle>org.apache:apache-jar-resource-bundle:1.4</resourceBundle>
+ </resourceBundles>
+ <supplementalModels>
+ <supplementalModel>notice-supplements.xml</supplementalModel>
+ </supplementalModels>
+ <properties>
+ <projectName>Apache CXF Distributed OSGi
+ DSW Reference Implementation</projectName>
+ </properties>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>2.9.1</version>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf.build-utils</groupId>
+ <artifactId>cxf-buildtools</artifactId>
+ <version>${cxf.build-utils.version}</version>
+ </dependency>
+ </dependencies>
+ <configuration>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ <executions>
+ <execution>
+ <id>validate</id>
+ <phase>validate</phase>
+ <configuration>
+ <configLocation>${cxf.resources.base.path}cxf-checkstyle${cxf.checkstyle.extension}.xml</configLocation>
+ <consoleOutput>true</consoleOutput>
+ <failsOnError>true</failsOnError>
+ <linkXRef>false</linkXRef>
+ <suppressionsLocation>${cxf.resources.base.path}cxf-checkstyle-suppressions.xml</suppressionsLocation>
+ <sourceDirectory>${basedir}/src</sourceDirectory>
+ <excludes>**/archetype-resources/**/*.java</excludes>
+ </configuration>
+ <goals>
+ <goal>checkstyle</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <repositories>
+ <repository>
+ <id>apache.snapshots</id>
+ <name>Apache Maven Snapshot Repository</name>
+ <url>http://repository.apache.org/content/groups/snapshots/</url>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+
+ </repositories>
+
+ <pluginRepositories>
+ <pluginRepository>
+ <id>apache.snapshots</id>
+ <name>Apache Maven Snapshot Repository</name>
+ <url>http://repository.apache.org/content/groups/snapshots/</url>
+ <layout>default</layout>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ <releases>
+ <enabled>false</enabled>
+ </releases>
+ </pluginRepository>
+ </pluginRepositories>
+
+</project>
diff --git a/trunk/pom.xml b/trunk/pom.xml
new file mode 100644
index 0000000..5a55293
--- /dev/null
+++ b/trunk/pom.xml
@@ -0,0 +1,179 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri</artifactId>
+ <version>1.6.0</version>
+
+ <packaging>pom</packaging>
+ <name>Distributed OSGI Reference Implementation</name>
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/cxf/dosgi/tags/cxf-dosgi-ri-1.6.0</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/cxf/dosgi/tags/cxf-dosgi-ri-1.6.0</developerConnection>
+ </scm>
+
+ <issueManagement>
+ <system>jira</system>
+ <url>https://issues.apache.org/jira/browse/DOSGI</url>
+ </issueManagement>
+
+ <prerequisites>
+ <maven>3.0</maven>
+ </prerequisites>
+
+ <distributionManagement>
+ <repository>
+ <id>apache.releases.https</id>
+ <name>Apache Release Distribution Repository</name>
+ <url>https://repository.apache.org/service/local/staging/deploy/maven2</url>
+ </repository>
+ <snapshotRepository>
+ <id>apache.snapshots.https</id>
+ <name>Apache Development Snapshot Repository</name>
+ <url>https://repository.apache.org/content/repositories/snapshots</url>
+ <uniqueVersion>false</uniqueVersion>
+ </snapshotRepository>
+ </distributionManagement>
+
+ <inceptionYear>2008</inceptionYear>
+ <mailingLists>
+ <mailingList>
+ <name>Apache CXF User List</name>
+ <subscribe>users-subscribe@cxf.apache.org</subscribe>
+ <unsubscribe>users-unsubscribe@cxf.apache.org</unsubscribe>
+ <post>users@cxf.apache.org</post>
+ <archive>http://mail-archives.apache.org/mod_mbox/cxf-users</archive>
+ </mailingList>
+ <mailingList>
+ <name>Apache CXF Developer List</name>
+ <subscribe>dev-subscribe@cxf.apache.org</subscribe>
+ <unsubscribe>dev-unsubscribe@cxf.apache.org</unsubscribe>
+ <post>dev@cxf.apache.org</post>
+ <archive>http://mail-archives.apache.org/mod_mbox/cxf-dev</archive>
+ </mailingList>
+ <mailingList>
+ <name>Apache CXF Commits List</name>
+ <subscribe>commits-subscribe@cxf.apache.org</subscribe>
+ <unsubscribe>commits-unsubscribe@cxf.apache.org</unsubscribe>
+ <post>commits@cxf.apache.org</post>
+ <archive>http://mail-archives.apache.org/mod_mbox/cxf-commits</archive>
+ </mailingList>
+ <mailingList>
+ <name>Apache CXF Issues List</name>
+ <subscribe>issues-subscribe@cxf.apache.org</subscribe>
+ <unsubscribe>issues-unsubscribe@cxf.apache.org</unsubscribe>
+ <post>issues@cxf.apache.org</post>
+ <archive>http://mail-archives.apache.org/mod_mbox/cxf-issues</archive>
+ </mailingList>
+ </mailingLists>
+
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+
+ <organization>
+ <name>The Apache Software Foundation</name>
+ <url>http://www.apache.org/</url>
+ </organization>
+
+ <modules>
+ <module>parent</module>
+ <module>felix</module>
+ <module>dsw</module>
+ <module>discovery</module>
+ <module>samples</module>
+ <module>osgi-api</module>
+ <module>distribution</module>
+ <module>systests2</module>
+ </modules>
+
+ <profiles>
+ <profile>
+ <id>release</id>
+ <build>
+ <plugins>
+ <!-- We want to deploy the artifact to a staging location for perusal -->
+ <plugin>
+ <inherited>true</inherited>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.6</version>
+ <configuration>
+ <altDeploymentRepository>${deploy.altRepository}</altDeploymentRepository>
+ <updateReleaseInfo>true</updateReleaseInfo>
+ </configuration>
+ </plugin>
+ <!-- We want to sign the artifact, the POM, and all attached artifacts -->
+ <plugin>
+ <artifactId>maven-gpg-plugin</artifactId>
+ <version>1.3</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+ <build>
+ <defaultGoal>install</defaultGoal>
+
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-release-plugin</artifactId>
+ <configuration>
+ <tagBase>https://svn.apache.org/repos/asf/cxf/dosgi/tags</tagBase>
+ <useReleaseProfile>false</useReleaseProfile>
+ <preparationGoals>clean install</preparationGoals>
+ <goals>deploy</goals>
+ <arguments>-Prelease,deploy</arguments>
+ <autoVersionSubmodules>true</autoVersionSubmodules>
+ <mavenExecutorId>forked-path</mavenExecutorId>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ <version>2.15</version>
+ <configuration>
+ <aggregate>true</aggregate>
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
+</project>
diff --git a/trunk/samples/discovery/client/pom.xml b/trunk/samples/discovery/client/pom.xml
new file mode 100644
index 0000000..3d2f96e
--- /dev/null
+++ b/trunk/samples/discovery/client/pom.xml
@@ -0,0 +1,66 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-discovery-client</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Discovery Sample Client Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-discovery-parent</artifactId>
+ <version>1.6.0</version>
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-discovery-interface</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.name}</Bundle-Name>
+ <Bundle-Description>The client-side
+ implementation of the Distributed OSGi
+ Discovery sample</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Activator>org.apache.cxf.dosgi.samples.discovery.consumer.Activator</Bundle-Activator>
+ <Import-Package>
+ org.apache.cxf.dosgi.samples.discovery,
+ org.osgi.framework,
+ org.osgi.util.tracker
+ </Import-Package>
+ <Private-Package>org.apache.cxf.dosgi.samples.discovery.consumer</Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/samples/discovery/client/src/main/java/org/apache/cxf/dosgi/samples/discovery/consumer/Activator.java b/trunk/samples/discovery/client/src/main/java/org/apache/cxf/dosgi/samples/discovery/consumer/Activator.java
new file mode 100644
index 0000000..2221116
--- /dev/null
+++ b/trunk/samples/discovery/client/src/main/java/org/apache/cxf/dosgi/samples/discovery/consumer/Activator.java
@@ -0,0 +1,86 @@
+/**
+ * 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.samples.discovery.consumer;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.cxf.dosgi.samples.discovery.DisplayService;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class Activator implements BundleActivator {
+
+ private ServiceTracker tracker;
+ private Map<DisplayService, String> displays = new ConcurrentHashMap<DisplayService, String>();
+ private ScheduledExecutorService scheduler;
+ private ScheduledFuture<?> handle;
+
+ public void start(BundleContext bc) throws Exception {
+ tracker = new ServiceTracker(bc, DisplayService.class.getName(), null) {
+
+ public Object addingService(ServiceReference reference) {
+ Object svc = super.addingService(reference);
+ if (svc instanceof DisplayService) {
+ DisplayService d = (DisplayService) svc;
+ System.out.println("Adding display: " + d.getID() + " (" + d + ")");
+ displays.put(d, d.getID());
+ }
+ return svc;
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ String value = displays.remove(service);
+ System.out.println("Removed display: " + value);
+ super.removedService(reference, service);
+ }
+ };
+ tracker.open();
+
+ scheduler = Executors.newScheduledThreadPool(1);
+ Runnable printer = new Runnable() {
+ int counter;
+ public void run() {
+ counter++;
+ String text = "some text " + counter;
+ System.out.println("Sending text to displays: " + text);
+ for (Entry<DisplayService, String> entry : displays.entrySet()) {
+ try {
+ entry.getKey().displayText(text);
+ } catch (Throwable th) {
+ System.out.println("Could not send message to display: " + entry.getValue());
+ }
+ }
+ }
+ };
+ handle = scheduler.scheduleAtFixedRate(printer, 5, 5, TimeUnit.SECONDS);
+ }
+
+ public void stop(BundleContext bc) throws Exception {
+ handle.cancel(true);
+ tracker.close();
+ }
+}
diff --git a/trunk/samples/discovery/impl/pom.xml b/trunk/samples/discovery/impl/pom.xml
new file mode 100644
index 0000000..37e1fc8
--- /dev/null
+++ b/trunk/samples/discovery/impl/pom.xml
@@ -0,0 +1,61 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-discovery-impl</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Discovery Sample Implementation Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-discovery-parent</artifactId>
+ <version>1.6.0</version>
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-discovery-interface</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.name}</Bundle-Name>
+ <Bundle-Description>The server-side
+ implementation of the Distributed OSGi
+ Discovery sample</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Activator>org.apache.cxf.dosgi.samples.discovery.impl.Activator</Bundle-Activator>
+ <Import-Package>
+ org.apache.cxf.dosgi.samples.discovery,
+ org.osgi.framework
+ </Import-Package>
+ <Private-Package>org.apache.cxf.dosgi.samples.discovery.impl</Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/samples/discovery/impl/src/main/java/org/apache/cxf/dosgi/samples/discovery/impl/Activator.java b/trunk/samples/discovery/impl/src/main/java/org/apache/cxf/dosgi/samples/discovery/impl/Activator.java
new file mode 100644
index 0000000..f9c972a
--- /dev/null
+++ b/trunk/samples/discovery/impl/src/main/java/org/apache/cxf/dosgi/samples/discovery/impl/Activator.java
@@ -0,0 +1,71 @@
+/**
+ * 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.samples.discovery.impl;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.UnknownHostException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.cxf.dosgi.samples.discovery.DisplayService;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class Activator implements BundleActivator {
+
+ private ServiceRegistration reg;
+
+ public void start(BundleContext bc) throws Exception {
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+
+ String host = getHostName();
+ int port = getPort();
+
+ props.put("service.exported.interfaces", "*");
+ props.put("service.exported.configs", "org.apache.cxf.ws");
+ props.put("org.apache.cxf.ws.address", getAddress(host, port)); // old obsolete value
+ props.put("endpoint.id", getAddress(host, port));
+
+ reg = bc.registerService(DisplayService.class.getName(),
+ new DisplayServiceImpl(host + ":" + port), props);
+ }
+
+ private static String getAddress(String host, int port) {
+ return "http://" + host + ":" + port + "/display";
+ }
+
+ private static String getHostName() {
+ try {
+ return InetAddress.getLocalHost().getCanonicalHostName();
+ } catch (UnknownHostException e) {
+ return "localhost";
+ }
+ }
+
+ private static int getPort() throws IOException {
+ return new ServerSocket(0).getLocalPort();
+ }
+
+ public void stop(BundleContext bc) throws Exception {
+ reg.unregister();
+ }
+}
diff --git a/trunk/samples/discovery/impl/src/main/java/org/apache/cxf/dosgi/samples/discovery/impl/DisplayServiceImpl.java b/trunk/samples/discovery/impl/src/main/java/org/apache/cxf/dosgi/samples/discovery/impl/DisplayServiceImpl.java
new file mode 100644
index 0000000..bd84a62
--- /dev/null
+++ b/trunk/samples/discovery/impl/src/main/java/org/apache/cxf/dosgi/samples/discovery/impl/DisplayServiceImpl.java
@@ -0,0 +1,40 @@
+/**
+ * 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.samples.discovery.impl;
+
+import org.apache.cxf.dosgi.samples.discovery.DisplayService;
+
+public class DisplayServiceImpl implements DisplayService {
+
+ private final String id;
+
+ public DisplayServiceImpl(String id) {
+ this.id = id;
+ System.out.println("Created DisplayService [" + id + "]");
+ }
+
+ public boolean displayText(String text) {
+ System.out.println("DisplayService [" + id + "]: " + text);
+ return true;
+ }
+
+ public String getID() {
+ return id;
+ }
+}
diff --git a/trunk/samples/discovery/interface/pom.xml b/trunk/samples/discovery/interface/pom.xml
new file mode 100644
index 0000000..e89593f
--- /dev/null
+++ b/trunk/samples/discovery/interface/pom.xml
@@ -0,0 +1,52 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-discovery-interface</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Discovery Sample Interface Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-discovery-parent</artifactId>
+ <version>1.6.0</version>
+ </parent>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.name}</Bundle-Name>
+ <Bundle-Description>The interfaces of the Distributed OSGi Discovery sample</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Import-Package />
+ <Export-Package>org.apache.cxf.dosgi.samples.discovery</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/samples/discovery/interface/src/main/java/org/apache/cxf/dosgi/samples/discovery/DisplayService.java b/trunk/samples/discovery/interface/src/main/java/org/apache/cxf/dosgi/samples/discovery/DisplayService.java
new file mode 100644
index 0000000..f05cef5
--- /dev/null
+++ b/trunk/samples/discovery/interface/src/main/java/org/apache/cxf/dosgi/samples/discovery/DisplayService.java
@@ -0,0 +1,24 @@
+/**
+ * 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.samples.discovery;
+
+public interface DisplayService {
+ boolean displayText(String text);
+ String getID();
+}
diff --git a/trunk/samples/discovery/pom.xml b/trunk/samples/discovery/pom.xml
new file mode 100644
index 0000000..da83970
--- /dev/null
+++ b/trunk/samples/discovery/pom.xml
@@ -0,0 +1,42 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-discovery-parent</artifactId>
+ <packaging>pom</packaging>
+ <name>Distributed OSGI Discovery Sample</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <modules>
+ <module>interface</module>
+ <module>impl</module>
+ <module>client</module>
+ </modules>
+
+</project>
diff --git a/trunk/samples/ds/client/pom.xml b/trunk/samples/ds/client/pom.xml
new file mode 100644
index 0000000..89265e1
--- /dev/null
+++ b/trunk/samples/ds/client/pom.xml
@@ -0,0 +1,63 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-ds-client</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Declarative Services Sample Client Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-ds-parent</artifactId>
+ <version>1.6.0</version>
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-ds-interface</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.name}</Bundle-Name>
+ <Bundle-Description>The client-side
+ implementation of the Distributed OSGi with
+ Declarative Services sample</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Activator>org.apache.cxf.dosgi.samples.ds.consumer.Activator</Bundle-Activator>
+ <Import-Package>*</Import-Package>
+ <Private-Package>org.apache.cxf.dosgi.samples.ds.consumer</Private-Package>
+ <Service-Component>OSGI-INF/component.xml</Service-Component>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/samples/ds/client/src/main/java/org/apache/cxf/dosgi/samples/ds/consumer/Activator.java b/trunk/samples/ds/client/src/main/java/org/apache/cxf/dosgi/samples/ds/consumer/Activator.java
new file mode 100644
index 0000000..304493d
--- /dev/null
+++ b/trunk/samples/ds/client/src/main/java/org/apache/cxf/dosgi/samples/ds/consumer/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.cxf.dosgi.samples.ds.consumer;
+
+import org.apache.cxf.dosgi.samples.ds.AdderService;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * This Activator simply registers a service tracker to indicate its interest in the
+ * AdderService which causes the service to get registered by the Listener Hook.
+ * It is a workaround for the problem that the current ListenerHook is incompatible
+ * with the Equinox DS implementation which doesn't specify a filter when looking up
+ * a service. See also DOSGI-73.
+ */
+public class Activator implements BundleActivator {
+
+ private ServiceTracker tracker;
+
+ public void start(BundleContext context) throws Exception {
+ tracker = new ServiceTracker(context, AdderService.class.getName(), null);
+ tracker.open();
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ tracker.close();
+ }
+}
diff --git a/trunk/samples/ds/client/src/main/java/org/apache/cxf/dosgi/samples/ds/consumer/AdderConsumer.java b/trunk/samples/ds/client/src/main/java/org/apache/cxf/dosgi/samples/ds/consumer/AdderConsumer.java
new file mode 100644
index 0000000..060ed42
--- /dev/null
+++ b/trunk/samples/ds/client/src/main/java/org/apache/cxf/dosgi/samples/ds/consumer/AdderConsumer.java
@@ -0,0 +1,39 @@
+/**
+ * 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.samples.ds.consumer;
+
+import org.apache.cxf.dosgi.samples.ds.AdderService;
+
+public class AdderConsumer {
+
+ private AdderService adder;
+
+ public void bindAdder(AdderService a) {
+ adder = a;
+ }
+
+ public void unbindAdder(AdderService a) {
+ adder = null;
+ }
+
+ public void start() {
+ System.out.println("Declarative Service consumer component.");
+ System.out.println("Using adder service: 1 + 1 = " + adder.add(1, 1));
+ }
+}
diff --git a/trunk/samples/ds/client/src/main/resources/OSGI-INF/component.xml b/trunk/samples/ds/client/src/main/resources/OSGI-INF/component.xml
new file mode 100644
index 0000000..31b91f7
--- /dev/null
+++ b/trunk/samples/ds/client/src/main/resources/OSGI-INF/component.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="DS Consumer Sample" activate="start">
+ <implementation class="org.apache.cxf.dosgi.samples.ds.consumer.AdderConsumer"/>
+ <reference interface="org.apache.cxf.dosgi.samples.ds.AdderService" name="AdderService" cardinality="1..1" policy="dynamic" bind="bindAdder" unbind="unbindAdder"/>
+</scr:component>
+
diff --git a/trunk/samples/ds/client/src/main/resources/OSGI-INF/remote-service/remote-services.xml b/trunk/samples/ds/client/src/main/resources/OSGI-INF/remote-service/remote-services.xml
new file mode 100644
index 0000000..e1f0b21
--- /dev/null
+++ b/trunk/samples/ds/client/src/main/resources/OSGI-INF/remote-service/remote-services.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0">
+ <endpoint-description>
+ <property name="objectClass">
+ <array>
+ <value>org.apache.cxf.dosgi.samples.ds.AdderService</value>
+ </array>
+ </property>
+ <property name="endpoint.id">http://localhost:9090/adder</property>
+ <property name="service.imported.configs">org.apache.cxf.ws</property>
+ </endpoint-description>
+</endpoint-descriptions>
diff --git a/trunk/samples/ds/impl/pom.xml b/trunk/samples/ds/impl/pom.xml
new file mode 100644
index 0000000..26f2c6e
--- /dev/null
+++ b/trunk/samples/ds/impl/pom.xml
@@ -0,0 +1,60 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-ds-impl</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Declarative Services Sample Implementation Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-ds-parent</artifactId>
+ <version>1.6.0</version>
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-ds-interface</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.name}</Bundle-Name>
+ <Bundle-Description>The server-side implementation of the Distributed OSGi with Declarative Services sample</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Import-Package>org.apache.cxf.dosgi.samples.ds</Import-Package>
+ <Private-Package>org.apache.cxf.dosgi.samples.ds.impl</Private-Package>
+ <Service-Component>OSGI-INF/component.xml</Service-Component>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/samples/ds/impl/src/main/java/org/apache/cxf/dosgi/samples/ds/impl/AdderServiceImpl.java b/trunk/samples/ds/impl/src/main/java/org/apache/cxf/dosgi/samples/ds/impl/AdderServiceImpl.java
new file mode 100644
index 0000000..0d1fcba
--- /dev/null
+++ b/trunk/samples/ds/impl/src/main/java/org/apache/cxf/dosgi/samples/ds/impl/AdderServiceImpl.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.cxf.dosgi.samples.ds.impl;
+
+import org.apache.cxf.dosgi.samples.ds.AdderService;
+
+public class AdderServiceImpl implements AdderService {
+
+ public int add(int a, int b) {
+ int result = a + b;
+ System.out.println("Adder service invoked: " + a + " + " + b + " = " + result);
+ return result;
+ }
+}
diff --git a/trunk/samples/ds/impl/src/main/resources/OSGI-INF/component.xml b/trunk/samples/ds/impl/src/main/resources/OSGI-INF/component.xml
new file mode 100644
index 0000000..3687b78
--- /dev/null
+++ b/trunk/samples/ds/impl/src/main/resources/OSGI-INF/component.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="DS Service Sample">
+ <implementation class="org.apache.cxf.dosgi.samples.ds.impl.AdderServiceImpl"/>
+
+ <property name="service.exported.interfaces" value="*" />
+ <property name="service.exported.configs" value="org.apache.cxf.ws" />
+ <property name="org.apache.cxf.ws.address" value="http://localhost:9090/adder" />
+
+ <service>
+ <provide interface="org.apache.cxf.dosgi.samples.ds.AdderService"/>
+ </service>
+</scr:component>
diff --git a/trunk/samples/ds/interface/pom.xml b/trunk/samples/ds/interface/pom.xml
new file mode 100644
index 0000000..c6f45d3
--- /dev/null
+++ b/trunk/samples/ds/interface/pom.xml
@@ -0,0 +1,52 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-ds-interface</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Declarative Services Sample Interface Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-ds-parent</artifactId>
+ <version>1.6.0</version>
+ </parent>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.name}</Bundle-Name>
+ <Bundle-Description>The interfaces of the Distributed OSGi with Declarative Services sample</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Import-Package />
+ <Export-Package>org.apache.cxf.dosgi.samples.ds</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/samples/ds/interface/src/main/java/org/apache/cxf/dosgi/samples/ds/AdderService.java b/trunk/samples/ds/interface/src/main/java/org/apache/cxf/dosgi/samples/ds/AdderService.java
new file mode 100644
index 0000000..f5e77fc
--- /dev/null
+++ b/trunk/samples/ds/interface/src/main/java/org/apache/cxf/dosgi/samples/ds/AdderService.java
@@ -0,0 +1,23 @@
+/**
+ * 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.samples.ds;
+
+public interface AdderService {
+ int add(int a, int b);
+}
diff --git a/trunk/samples/ds/pom.xml b/trunk/samples/ds/pom.xml
new file mode 100644
index 0000000..e1ee1a3
--- /dev/null
+++ b/trunk/samples/ds/pom.xml
@@ -0,0 +1,41 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-ds-parent</artifactId>
+ <packaging>pom</packaging>
+ <name>Distributed OSGI Declarative Services Sample</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <modules>
+ <module>interface</module>
+ <module>impl</module>
+ <module>client</module>
+ </modules>
+
+</project>
diff --git a/trunk/samples/greeter/client/pom.xml b/trunk/samples/greeter/client/pom.xml
new file mode 100644
index 0000000..83a8bd2
--- /dev/null
+++ b/trunk/samples/greeter/client/pom.xml
@@ -0,0 +1,77 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-client</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Greeter Bundle Client</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <bundle.import.package>*</bundle.import.package>
+ <bundle.private.package>org.apache.cxf.dosgi.samples.greeter.client</bundle.private.package>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-interface</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>CXF Distributed OSGi Greeter Demo Client Bundle</Bundle-Name>
+ <Bundle-Description>The client-side implementation of the Distributed OSGi Greeter demo</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Activator>org.apache.cxf.dosgi.samples.greeter.client.Activator</Bundle-Activator>
+ <Import-Package>${bundle.import.package}</Import-Package>
+ <Private-Package>${bundle.private.package}</Private-Package>
+ <DynamicImport-Package>org.apache.cxf.dosgi.dsw.qos,org.apache.cxf</DynamicImport-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/trunk/samples/greeter/client/src/main/java/org/apache/cxf/dosgi/samples/greeter/client/Activator.java b/trunk/samples/greeter/client/src/main/java/org/apache/cxf/dosgi/samples/greeter/client/Activator.java
new file mode 100644
index 0000000..6f46760
--- /dev/null
+++ b/trunk/samples/greeter/client/src/main/java/org/apache/cxf/dosgi/samples/greeter/client/Activator.java
@@ -0,0 +1,96 @@
+/**
+ * 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.samples.greeter.client;
+
+import java.util.Map;
+
+import org.apache.cxf.dosgi.samples.greeter.GreeterData;
+import org.apache.cxf.dosgi.samples.greeter.GreeterException;
+import org.apache.cxf.dosgi.samples.greeter.GreeterService;
+import org.apache.cxf.dosgi.samples.greeter.GreetingPhrase;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class Activator implements BundleActivator {
+
+ private ServiceTracker tracker;
+
+ public void start(final BundleContext bc) {
+ tracker = new ServiceTracker(bc, GreeterService.class.getName(), null) {
+ @Override
+ public Object addingService(ServiceReference reference) {
+ Object service = super.addingService(reference);
+ if (service instanceof GreeterService) {
+ useService((GreeterService) service);
+ }
+ return service;
+ }
+ };
+ tracker.open();
+ }
+
+ protected void useService(final GreeterService greeter) {
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ greeterUI(greeter);
+ }
+ });
+ t.start();
+ }
+
+ private void greeterUI(final GreeterService greeter) {
+ while (true) {
+ System.out.println("*** Opening greeter client dialog ***");
+ Object gd = getGreeterData();
+ if (gd instanceof String) {
+ System.out.println("*** Invoking greeter ***");
+ Map<GreetingPhrase, String> result = greeter.greetMe((String) gd);
+
+ System.out.println("greetMe(\"" + gd + "\") returns:");
+ for (Map.Entry<GreetingPhrase, String> greeting : result.entrySet()) {
+ System.out.println(" " + greeting.getKey().getPhrase()
+ + " " + greeting.getValue());
+ }
+ } else if (gd instanceof GreeterData) {
+ System.out.println("*** Invoking greeter ***");
+ try {
+ GreetingPhrase[] result = greeter.greetMe((GreeterData) gd);
+ System.out.println("greetMe(\"" + gd + "\") returns:");
+ for (GreetingPhrase phrase : result) {
+ System.out.println(" " + phrase.getPhrase());
+ }
+ } catch (GreeterException ex) {
+ System.out.println("GreeterException: " + ex.toString());
+ }
+ }
+ }
+ }
+
+ private static Object getGreeterData() {
+ GreeterDialog gd = new GreeterDialog();
+ gd.setVisible(true);
+ return gd.getSelection();
+ }
+
+ public void stop(BundleContext bc) throws Exception {
+ tracker.close();
+ }
+}
diff --git a/trunk/samples/greeter/client/src/main/java/org/apache/cxf/dosgi/samples/greeter/client/GreeterDataImpl.java b/trunk/samples/greeter/client/src/main/java/org/apache/cxf/dosgi/samples/greeter/client/GreeterDataImpl.java
new file mode 100644
index 0000000..368be36
--- /dev/null
+++ b/trunk/samples/greeter/client/src/main/java/org/apache/cxf/dosgi/samples/greeter/client/GreeterDataImpl.java
@@ -0,0 +1,46 @@
+/**
+ * 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.samples.greeter.client;
+
+import org.apache.cxf.dosgi.samples.greeter.GreeterData;
+
+public class GreeterDataImpl implements GreeterData {
+
+ private final String name;
+ private final int age;
+ private final boolean exception;
+
+ public GreeterDataImpl(String n, int a, boolean b) {
+ name = n;
+ age = a;
+ exception = b;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public boolean isException() {
+ return exception;
+ }
+}
diff --git a/trunk/samples/greeter/client/src/main/java/org/apache/cxf/dosgi/samples/greeter/client/GreeterDialog.java b/trunk/samples/greeter/client/src/main/java/org/apache/cxf/dosgi/samples/greeter/client/GreeterDialog.java
new file mode 100644
index 0000000..3983a82
--- /dev/null
+++ b/trunk/samples/greeter/client/src/main/java/org/apache/cxf/dosgi/samples/greeter/client/GreeterDialog.java
@@ -0,0 +1,204 @@
+/**
+ * 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.samples.greeter.client;
+
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JTextField;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+public class GreeterDialog extends JDialog {
+
+ private static final long serialVersionUID = 1L;
+
+ JTextField name1field;
+ JTextField name2field;
+ JTextField ageTextField;
+ JCheckBox throwExCB;
+ Object selection;
+
+ public GreeterDialog() {
+ super((Frame) null, "Invoke Remote Greeter Service", true);
+
+ JPanel panel = new JPanel();
+ panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
+ setContentPane(panel);
+
+ final JRadioButton rb1 = new JRadioButton("invoke: Map<GreetingPhrase, String> greetMe(String name);");
+ rb1.setSelected(true);
+ rb1.setAlignmentX(Component.LEFT_ALIGNMENT);
+ panel.add(rb1);
+
+ final JPanel simplePanel = createFirstOptionPanel();
+ rb1.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ enablePanel(simplePanel, rb1.isSelected());
+ }
+ });
+ panel.add(simplePanel);
+ panel.add(new JLabel(" ")); // add a spacer
+
+ final JRadioButton rb2
+ = new JRadioButton("invoke: GreetingPhrase[] greetMe(GreeterData data) throws GreeterException;");
+ rb2.setAlignmentX(Component.LEFT_ALIGNMENT);
+ panel.add(rb2);
+
+ final JPanel complexPanel = createSecondOptionPanel();
+
+ rb2.addChangeListener(new ChangeListener() {
+ public void stateChanged(ChangeEvent e) {
+ enablePanel(complexPanel, rb2.isSelected());
+ }
+ });
+
+ panel.add(complexPanel);
+ enablePanel(complexPanel, false);
+
+ JPanel buttons = new JPanel(new FlowLayout(FlowLayout.CENTER));
+ buttons.setAlignmentX(Component.LEFT_ALIGNMENT);
+
+ JButton b1 = new JButton("Invoke");
+ buttons.add(b1);
+
+ b1.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ if (rb1.isSelected()) {
+ selection = name1field.getText();
+ } else {
+ selection = new GreeterDataImpl(name2field.getText(),
+ new Integer(ageTextField.getText()),
+ throwExCB.isSelected());
+ }
+
+ setVisible(false);
+ }
+ });
+
+ panel.add(buttons);
+
+ ButtonGroup bg = new ButtonGroup();
+ bg.add(rb1);
+ bg.add(rb2);
+
+ pack();
+ setLocationRelativeTo(null); // centers frame on screen
+ }
+
+ private JPanel createFirstOptionPanel() {
+ final JPanel simplePanel = new JPanel(new GridBagLayout());
+ simplePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ GridBagConstraints c1 = new GridBagConstraints();
+
+ JLabel lb1 = new JLabel("Name: ");
+ c1.weightx = 0.0;
+ c1.gridx = 0;
+ c1.gridy = 0;
+ c1.insets = new Insets(0, 25, 0, 0);
+ c1.anchor = GridBagConstraints.LINE_START;
+ simplePanel.add(lb1, c1);
+
+ name1field = new JTextField(20);
+ c1.weightx = 0.2;
+ c1.gridx = 1;
+ c1.gridy = 0;
+ c1.insets = new Insets(0, 10, 0, 0);
+ c1.anchor = GridBagConstraints.LINE_START;
+ simplePanel.add(name1field, c1);
+ return simplePanel;
+ }
+ private JPanel createSecondOptionPanel() {
+ final JPanel complexPanel = new JPanel(new GridBagLayout());
+ complexPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+ GridBagConstraints c2 = new GridBagConstraints();
+
+ JLabel lb2 = new JLabel("Name: ");
+ c2.weightx = 0.0;
+ c2.gridx = 0;
+ c2.gridy = 0;
+ c2.insets = new Insets(0, 25, 0, 0);
+ c2.anchor = GridBagConstraints.LINE_START;
+ complexPanel.add(lb2, c2);
+
+ name2field = new JTextField(20);
+ c2.weightx = 0.2;
+ c2.gridx = 1;
+ c2.gridy = 0;
+ c2.insets = new Insets(0, 10, 0, 0);
+ c2.anchor = GridBagConstraints.LINE_START;
+ complexPanel.add(name2field, c2);
+
+ JLabel lb3 = new JLabel("Age: ");
+ c2.weightx = 0.0;
+ c2.gridx = 0;
+ c2.gridy = 1;
+ c2.insets = new Insets(0, 25, 0, 0);
+ c2.anchor = GridBagConstraints.LINE_START;
+ complexPanel.add(lb3, c2);
+
+ ageTextField = new JTextField(7);
+ c2.weightx = 0.2;
+ c2.gridx = 1;
+ c2.gridy = 1;
+ c2.insets = new Insets(0, 10, 0, 0);
+ c2.anchor = GridBagConstraints.LINE_START;
+ complexPanel.add(ageTextField, c2);
+
+ throwExCB = new JCheckBox("Throw Exception");
+ c2.weightx = 0.0;
+ c2.gridx = 0;
+ c2.gridy = 2;
+ c2.gridwidth = 2;
+ c2.insets = new Insets(0, 22, 0, 0);
+ c2.anchor = GridBagConstraints.LINE_START;
+ complexPanel.add(throwExCB, c2);
+ return complexPanel;
+ }
+
+ public Object getSelection() {
+ return selection;
+ }
+
+ private static void enablePanel(JPanel panel, boolean b) {
+ for (Component c : panel.getComponents()) {
+ c.setEnabled(b);
+ }
+ }
+
+ public static void main(String ... args) {
+ GreeterDialog gd = new GreeterDialog();
+ gd.setVisible(true);
+ System.exit(0);
+ }
+}
diff --git a/trunk/samples/greeter/client/src/main/resources/OSGI-INF/cxf/intents/intent-map.xml b/trunk/samples/greeter/client/src/main/resources/OSGI-INF/cxf/intents/intent-map.xml
new file mode 100644
index 0000000..026bf13
--- /dev/null
+++ b/trunk/samples/greeter/client/src/main/resources/OSGI-INF/cxf/intents/intent-map.xml
@@ -0,0 +1,75 @@
+<?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.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:p="http://cxf.apache.org/policy"
+ xmlns:wsp="http://www.w3.org/ns/ws-policy"
+ xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
+ xmlns:http="http://cxf.apache.org/transports/http/configuration"
+ xmlns:wsrm-policy="http://schemas.xmlsoap.org/ws/2005/02/rm/policy"
+ xsi:schemaLocation="
+ http://cxf.apache.org/policy http://cxf.apache.org/schemas/policy.xsd
+ http://www.w3.org/ns/ws-policy http://www.w3.org/2007/02/ws-policy.xsd
+ http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd
+ http://schemas.xmlsoap.org/ws/2005/02/rm/policy http://schemas.xmlsoap.org/ws/2005/02/rm/wsrm-policy.xsd
+ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+ <bean id="intentMap" class="org.apache.cxf.dosgi.dsw.qos.IntentMap">
+ <property name="intents">
+ <map>
+ <entry key="reliability" value-ref="reliableMessaging"/>
+ <entry key="decoupled" value-ref="decoupledAddressing"/>
+ </map>
+ </property>
+ </bean>
+
+ <p:policies id="reliableMessaging">
+ <wsp:PolicyReference URI="#ReliabilityPolicy"/>
+ <wsp:PolicyReference URI="#DecoupledPolicy"/>
+ <wsp:PolicyReference URI="#AddressingPolicy"/>
+ </p:policies>
+
+ <p:policies id="decoupledAddressing">
+ <wsp:PolicyReference URI="#DecoupledPolicy"/>
+ <wsp:PolicyReference URI="#AddressingPolicy"/>
+ </p:policies>
+
+ <wsp:Policy wsu:Id="ReliabilityPolicy">
+ <wsrm-policy:RMAssertion>
+ <wsrm-policy:BaseRetransmissionInterval Milliseconds="4000"/>
+ <wsrm-policy:AcknowledgementInterval Milliseconds="2000" />
+ </wsrm-policy:RMAssertion>
+ </wsp:Policy>
+
+ <wsp:Policy wsu:Id="DecoupledPolicy">
+ <wsp:ExactlyOne>
+ <http:client DecoupledEndpoint="http://localhost:9970/decoupled_endpoint"/>
+ </wsp:ExactlyOne>
+ </wsp:Policy>
+
+ <wsp:Policy wsu:Id="AddressingPolicy"
+ xmlns:wsam="http://www.w3.org/2007/02/addressing/metadata">
+ <wsam:Addressing>
+ <wsp:Policy />
+ </wsam:Addressing>
+ </wsp:Policy>
+
+</beans>
diff --git a/trunk/samples/greeter/client/src/main/resources/OSGI-INF/remote-service/remote-services.xml b/trunk/samples/greeter/client/src/main/resources/OSGI-INF/remote-service/remote-services.xml
new file mode 100644
index 0000000..af3486e
--- /dev/null
+++ b/trunk/samples/greeter/client/src/main/resources/OSGI-INF/remote-service/remote-services.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0">
+ <endpoint-description>
+ <property name="objectClass">
+ <array>
+ <value>org.apache.cxf.dosgi.samples.greeter.GreeterService</value>
+ </array>
+ </property>
+ <property name="endpoint.id">http://localhost:9090/greeter</property>
+ <property name="service.imported.configs">org.apache.cxf.ws</property>
+ </endpoint-description>
+</endpoint-descriptions>
+
diff --git a/trunk/samples/greeter/impl/pom.xml b/trunk/samples/greeter/impl/pom.xml
new file mode 100644
index 0000000..83581e5
--- /dev/null
+++ b/trunk/samples/greeter/impl/pom.xml
@@ -0,0 +1,76 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-impl</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Greeter Implementation Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <bundle.import.package>*</bundle.import.package>
+ <bundle.private.package>org.apache.cxf.dosgi.samples.greeter.impl</bundle.private.package>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-interface</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>CXF Distributed OSGi Greeter Demo Service Implementation Bundle</Bundle-Name>
+ <Bundle-Description>The server-side implementation of the CXF Distributed OSGi Greeter demo</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Activator>org.apache.cxf.dosgi.samples.greeter.impl.Activator</Bundle-Activator>
+ <Import-Package>${bundle.import.package}</Import-Package>
+ <Private-Package>${bundle.private.package}</Private-Package>
+ <DynamicImport-Package>org.apache.cxf.dosgi.dsw.qos,org.apache.cxf</DynamicImport-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/trunk/samples/greeter/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/Activator.java b/trunk/samples/greeter/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/Activator.java
new file mode 100644
index 0000000..9bbbf66
--- /dev/null
+++ b/trunk/samples/greeter/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/Activator.java
@@ -0,0 +1,47 @@
+/**
+ * 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.samples.greeter.impl;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.cxf.dosgi.samples.greeter.GreeterService;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class Activator implements BundleActivator {
+
+ private ServiceRegistration registration;
+
+ public void start(BundleContext bc) throws Exception {
+ Dictionary<String, String> props = new Hashtable<String, String>();
+
+ props.put("service.exported.interfaces", "*");
+ props.put("service.exported.configs", "org.apache.cxf.ws");
+ props.put("org.apache.cxf.ws.address", "http://localhost:9090/greeter");
+
+ registration = bc.registerService(GreeterService.class.getName(),
+ new GreeterServiceImpl(), props);
+ }
+
+ public void stop(BundleContext bc) throws Exception {
+ registration.unregister();
+ }
+}
diff --git a/trunk/samples/greeter/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/GreeterServiceImpl.java b/trunk/samples/greeter/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/GreeterServiceImpl.java
new file mode 100644
index 0000000..6e6a4af
--- /dev/null
+++ b/trunk/samples/greeter/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/GreeterServiceImpl.java
@@ -0,0 +1,61 @@
+/**
+ * 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.samples.greeter.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.samples.greeter.GreeterData;
+import org.apache.cxf.dosgi.samples.greeter.GreeterException;
+import org.apache.cxf.dosgi.samples.greeter.GreeterService;
+import org.apache.cxf.dosgi.samples.greeter.GreetingPhrase;
+
+public class GreeterServiceImpl implements GreeterService {
+
+ public Map<GreetingPhrase, String> greetMe(String name) {
+ System.out.println("Invoking: greetMe(" + name + ")");
+
+ Map<GreetingPhrase, String> greetings = new HashMap<GreetingPhrase, String>();
+
+ greetings.put(new GreetingPhrase("Hello"), name);
+ greetings.put(new GreetingPhrase("Hoi"), name);
+ greetings.put(new GreetingPhrase("Hola"), name);
+ greetings.put(new GreetingPhrase("Bonjour"), name);
+
+ return greetings;
+ }
+
+ public GreetingPhrase[] greetMe(GreeterData gd) throws GreeterException {
+ if (gd.isException()) {
+ System.out.println("Throwing custom exception from: greetMe(" + gd.getName() + ")");
+ throw new GreeterException(gd.getName());
+ }
+
+ String details = gd.getName() + "(" + gd.getAge() + ")";
+ System.out.println("Invoking: greetMe(" + details + ")");
+
+ GreetingPhrase[] greetings = new GreetingPhrase[] {
+ new GreetingPhrase("Howdy " + details),
+ new GreetingPhrase("Hallo " + details),
+ new GreetingPhrase("Ni hao " + details)
+ };
+
+ return greetings;
+ }
+}
diff --git a/trunk/samples/greeter/interface/pom.xml b/trunk/samples/greeter/interface/pom.xml
new file mode 100644
index 0000000..e7b35fe
--- /dev/null
+++ b/trunk/samples/greeter/interface/pom.xml
@@ -0,0 +1,72 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-interface</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Greeter Interface Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <bundle.import.package>*</bundle.import.package>
+ <bundle.export.package>org.apache.cxf.dosgi.samples.greeter</bundle.export.package>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>CXF Distributed OSGi Greeter Demo Interface Bundle</Bundle-Name>
+ <Bundle-Description>The interfaces of the CXF Distributed OSGi Greeter demo</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Import-Package>${bundle.import.package}</Import-Package>
+ <Export-Package>${bundle.export.package}</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/trunk/samples/greeter/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/GreeterData.java b/trunk/samples/greeter/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/GreeterData.java
new file mode 100644
index 0000000..be23cc4
--- /dev/null
+++ b/trunk/samples/greeter/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/GreeterData.java
@@ -0,0 +1,25 @@
+/**
+ * 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.samples.greeter;
+
+public interface GreeterData {
+ String getName();
+ int getAge();
+ boolean isException();
+}
diff --git a/trunk/samples/greeter/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/GreeterException.java b/trunk/samples/greeter/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/GreeterException.java
new file mode 100644
index 0000000..ebc58d1
--- /dev/null
+++ b/trunk/samples/greeter/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/GreeterException.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.cxf.dosgi.samples.greeter;
+
+public class GreeterException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+ private String name;
+
+ public GreeterException() {
+ }
+
+ public GreeterException(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String theName) {
+ name = theName;
+ }
+
+ @Override
+ public String toString() {
+ return "GreeterService can not greet " + name;
+ }
+}
diff --git a/trunk/samples/greeter/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/GreeterService.java b/trunk/samples/greeter/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/GreeterService.java
new file mode 100644
index 0000000..1a0d0e0
--- /dev/null
+++ b/trunk/samples/greeter/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/GreeterService.java
@@ -0,0 +1,27 @@
+/**
+ * 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.samples.greeter;
+
+import java.util.Map;
+
+public interface GreeterService {
+
+ Map<GreetingPhrase, String> greetMe(String name);
+ GreetingPhrase[] greetMe(GreeterData name) throws GreeterException;
+}
diff --git a/trunk/samples/greeter/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/GreetingPhrase.java b/trunk/samples/greeter/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/GreetingPhrase.java
new file mode 100644
index 0000000..857ed70
--- /dev/null
+++ b/trunk/samples/greeter/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/GreetingPhrase.java
@@ -0,0 +1,50 @@
+/**
+ * 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.samples.greeter;
+
+public class GreetingPhrase {
+
+ private String phrase;
+
+ public GreetingPhrase() {
+ }
+
+ public GreetingPhrase(String phrase) {
+ this.phrase = phrase;
+ }
+
+ public void setPhrase(String thePhrase) {
+ this.phrase = thePhrase;
+ }
+
+ public String getPhrase() {
+ return phrase;
+ }
+
+ @Override
+ public int hashCode() {
+ return phrase.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return GreetingPhrase.class.isAssignableFrom(other.getClass())
+ && phrase.equals(((GreetingPhrase) other).phrase);
+ }
+}
diff --git a/trunk/samples/greeter/pom.xml b/trunk/samples/greeter/pom.xml
new file mode 100644
index 0000000..d398d7c
--- /dev/null
+++ b/trunk/samples/greeter/pom.xml
@@ -0,0 +1,42 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-parent</artifactId>
+ <packaging>pom</packaging>
+ <name>Distributed OSGI Greeter Bundles</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <modules>
+ <module>interface</module>
+ <module>impl</module>
+ <module>client</module>
+ </modules>
+
+</project>
diff --git a/trunk/samples/greeter_rest/client/pom.xml b/trunk/samples/greeter_rest/client/pom.xml
new file mode 100644
index 0000000..9be90ea
--- /dev/null
+++ b/trunk/samples/greeter_rest/client/pom.xml
@@ -0,0 +1,76 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-rest-client</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Greeter Bundle Client</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <bundle.import.package>javax.ws.rs.*,*</bundle.import.package>
+ <bundle.private.package>org.apache.cxf.dosgi.samples.greeter.client.rest</bundle.private.package>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-rest-interface</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>CXF Distributed OSGi Greeter REST Demo Client Bundle</Bundle-Name>
+ <Bundle-Description>The client-side implementation of the Distributed OSGi Greeter REST demo</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Activator>org.apache.cxf.dosgi.samples.greeter.client.rest.Activator</Bundle-Activator>
+ <Import-Package>${bundle.import.package}</Import-Package>
+ <Private-Package>${bundle.private.package}</Private-Package>
+ <DynamicImport-Package>org.apache.cxf.dosgi.dsw.qos,org.apache.cxf</DynamicImport-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/trunk/samples/greeter_rest/client/src/main/java/org/apache/cxf/dosgi/samples/greeter/client/rest/Activator.java b/trunk/samples/greeter_rest/client/src/main/java/org/apache/cxf/dosgi/samples/greeter/client/rest/Activator.java
new file mode 100644
index 0000000..667060f
--- /dev/null
+++ b/trunk/samples/greeter_rest/client/src/main/java/org/apache/cxf/dosgi/samples/greeter/client/rest/Activator.java
@@ -0,0 +1,132 @@
+/**
+ * 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.samples.greeter.client.rest;
+
+import javax.swing.JOptionPane;
+
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterException;
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterInfo;
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterService;
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterService2;
+import org.apache.cxf.dosgi.samples.greeter.rest.GreetingPhrase;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class Activator implements BundleActivator {
+
+ private ServiceTracker tracker;
+ private ServiceTracker tracker2;
+
+ public void start(final BundleContext bc) {
+ tracker = new ServiceTracker(bc, GreeterService.class.getName(), null) {
+ @Override
+ public Object addingService(ServiceReference reference) {
+ Object service = super.addingService(reference);
+ if (service instanceof GreeterService) {
+ useGreeterService((GreeterService) service);
+ }
+ return service;
+ }
+ };
+ tracker.open();
+
+ tracker2 = new ServiceTracker(bc, GreeterService2.class.getName(), null) {
+ @Override
+ public Object addingService(ServiceReference reference) {
+ Object service = super.addingService(reference);
+ if (service instanceof GreeterService2) {
+ useGreeterService2((GreeterService2) service);
+ }
+ return service;
+ }
+ };
+ tracker2.open();
+ }
+
+ protected void useGreeterService(final GreeterService greeter) {
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ greeterUI(greeter);
+ }
+ });
+ t.start();
+ }
+
+ protected void useGreeterService2(final GreeterService2 greeter) {
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ greeter2UI(greeter);
+ }
+ });
+ t.start();
+ }
+
+ private void greeterUI(final GreeterService greeter) {
+ while (true) {
+ System.out.println("*** Opening greeter client dialog ***");
+ String name = JOptionPane.showInputDialog("Enter name:");
+ if (name == null) {
+ break;
+ } else {
+ System.out.println("*** Invoking greeter ***");
+ try {
+ GreeterInfo info = greeter.greetMe(name);
+
+ System.out.println("greetMe(\"" + name + "\") returns:");
+ for (GreetingPhrase greeting: info.getGreetings()) {
+ System.out.println(" " + greeting.getPhrase()
+ + " " + greeting.getName());
+ }
+ } catch (GreeterException ex) {
+ System.out.println("GreeterException: " + ex.toString());
+ }
+ }
+ }
+ }
+
+ private void greeter2UI(final GreeterService2 greeter) {
+ while (true) {
+ System.out.println("*** Opening greeter2 client dialog ***");
+ String name = JOptionPane.showInputDialog("Greeter2: Enter name");
+ if (name == null) {
+ break;
+ } else {
+ System.out.println("*** Invoking greeter2 ***");
+ try {
+ GreeterInfo info = greeter.greetMe(name);
+
+ System.out.println("greetMe(\"" + name + "\") returns:");
+ for (GreetingPhrase greeting: info.getGreetings()) {
+ System.out.println(" " + greeting.getPhrase()
+ + " " + greeting.getName());
+ }
+ } catch (GreeterException ex) {
+ System.out.println("GreeterException: " + ex.toString());
+ }
+ }
+ }
+ }
+
+ public void stop(BundleContext bc) throws Exception {
+ tracker.close();
+ tracker2.close();
+ }
+}
diff --git a/trunk/samples/greeter_rest/client/src/main/resources/OSGI-INF/remote-service/remote-services.xml b/trunk/samples/greeter_rest/client/src/main/resources/OSGI-INF/remote-service/remote-services.xml
new file mode 100644
index 0000000..1b2ec65
--- /dev/null
+++ b/trunk/samples/greeter_rest/client/src/main/resources/OSGI-INF/remote-service/remote-services.xml
@@ -0,0 +1,31 @@
+<?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.
+ -->
+<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0">
+ <service-description>
+ <provide interface="org.apache.cxf.dosgi.samples.greeter.rest.GreeterService" />
+ <property name="service.exported.interfaces">*</property>
+ <property name="service.exported.configs">org.apache.cxf.rs</property>
+ <property name="service.exported.intents">HTTP</property>
+ <property name="org.apache.cxf.rs.address">http://localhost:8080/greeter</property>
+ </service-description>
+ <service-description>
+ <provide interface="org.apache.cxf.dosgi.samples.greeter.rest.GreeterService2" />
+ <property name="service.exported.interfaces">*</property>
+ <property name="service.exported.configs">org.apache.cxf.rs</property>
+ <property name="service.exported.intents">HTTP</property>
+ <property name="org.apache.cxf.rs.address">http://localhost:8080/greeter2</property>
+ </service-description>
+</service-descriptions>
diff --git a/trunk/samples/greeter_rest/impl/pom.xml b/trunk/samples/greeter_rest/impl/pom.xml
new file mode 100644
index 0000000..b180322
--- /dev/null
+++ b/trunk/samples/greeter_rest/impl/pom.xml
@@ -0,0 +1,77 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-rest-impl</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Greeter Implementation Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <bundle.import.package>javax.ws.rs.*,*</bundle.import.package>
+ <bundle.private.package>org.apache.cxf.dosgi.samples.greeter.impl.rest</bundle.private.package>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-rest-interface</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>CXF Distributed OSGi Greeter Demo Service REST Implementation Bundle</Bundle-Name>
+ <Bundle-Description>The server-side implementation of the CXF Distributed OSGi Greeter REST demo</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Activator>org.apache.cxf.dosgi.samples.greeter.impl.rest.Activator</Bundle-Activator>
+ <Import-Package>${bundle.import.package}</Import-Package>
+ <Private-Package>${bundle.private.package}</Private-Package>
+ <DynamicImport-Package>org.apache.cxf.dosgi.dsw.qos,org.apache.cxf</DynamicImport-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/trunk/samples/greeter_rest/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/rest/Activator.java b/trunk/samples/greeter_rest/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/rest/Activator.java
new file mode 100644
index 0000000..5ee239b
--- /dev/null
+++ b/trunk/samples/greeter_rest/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/rest/Activator.java
@@ -0,0 +1,59 @@
+/**
+ * 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.samples.greeter.impl.rest;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterService;
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterService2;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class Activator implements BundleActivator {
+
+ private ServiceRegistration registration;
+ private ServiceRegistration registration2;
+
+ public void start(BundleContext bc) throws Exception {
+ Dictionary<String, Object> props = getProperties("/greeter");
+ registration = bc.registerService(GreeterService.class.getName(),
+ new GreeterServiceImpl(), props);
+
+ props = getProperties("/greeter2");
+ registration2 = bc.registerService(GreeterService2.class.getName(),
+ new GreeterServiceImpl2(), props);
+ }
+
+ private Dictionary<String, Object> getProperties(String address) {
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+
+ props.put("service.exported.interfaces", "*");
+ props.put("service.exported.configs", "org.apache.cxf.rs");
+ props.put("service.exported.intents", "HTTP");
+ props.put("org.apache.cxf.rs.httpservice.context", address);
+ return props;
+ }
+
+ public void stop(BundleContext bc) throws Exception {
+ registration.unregister();
+ registration2.unregister();
+ }
+}
diff --git a/trunk/samples/greeter_rest/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/rest/GreeterServiceImpl.java b/trunk/samples/greeter_rest/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/rest/GreeterServiceImpl.java
new file mode 100644
index 0000000..cf8df84
--- /dev/null
+++ b/trunk/samples/greeter_rest/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/rest/GreeterServiceImpl.java
@@ -0,0 +1,49 @@
+/**
+ * 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.samples.greeter.impl.rest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterException;
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterInfo;
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterService;
+import org.apache.cxf.dosgi.samples.greeter.rest.GreetingPhrase;
+
+public class GreeterServiceImpl implements GreeterService {
+
+ private static final String STRANGER_NAME = "Stranger";
+
+ public GreeterInfo greetMe(String name) throws GreeterException {
+ System.out.println("Invoking: greetMe(" + name + ")");
+
+ if (name.equals(STRANGER_NAME)) {
+ throw new GreeterException(name);
+ }
+
+ GreeterInfo info = new GreeterInfo();
+ List<GreetingPhrase> list = new ArrayList<GreetingPhrase>();
+ list.add(new GreetingPhrase("Hello", name));
+ list.add(new GreetingPhrase("Hoi", name));
+ list.add(new GreetingPhrase("Hola", name));
+ list.add(new GreetingPhrase("Bonjour", name));
+ info.setGreetings(list);
+ return info;
+ }
+}
diff --git a/trunk/samples/greeter_rest/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/rest/GreeterServiceImpl2.java b/trunk/samples/greeter_rest/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/rest/GreeterServiceImpl2.java
new file mode 100644
index 0000000..83731c5
--- /dev/null
+++ b/trunk/samples/greeter_rest/impl/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl/rest/GreeterServiceImpl2.java
@@ -0,0 +1,33 @@
+/**
+ * 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.samples.greeter.impl.rest;
+
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterException;
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterInfo;
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterService2;
+
+public class GreeterServiceImpl2 implements GreeterService2 {
+
+ private GreeterServiceImpl greeter = new GreeterServiceImpl();
+
+ public GreeterInfo greetMe(String name) throws GreeterException {
+ System.out.println("Delegating from GreeterServiceImpl2 to GreeterServiceImpl");
+ return greeter.greetMe(name);
+ }
+}
diff --git a/trunk/samples/greeter_rest/impl2/pom.xml b/trunk/samples/greeter_rest/impl2/pom.xml
new file mode 100644
index 0000000..2c9ebe7
--- /dev/null
+++ b/trunk/samples/greeter_rest/impl2/pom.xml
@@ -0,0 +1,77 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-rest-impl2</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Greeter Implementation2 Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <bundle.import.package>javax.ws.rs.*,*</bundle.import.package>
+ <bundle.private.package>org.apache.cxf.dosgi.samples.greeter.impl2.rest</bundle.private.package>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-rest-interface</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>CXF Distributed OSGi Greeter Demo Service REST Implementation2 Bundle</Bundle-Name>
+ <Bundle-Description>The 2nd server-side implementation of the CXF Distributed OSGi Greeter REST demo</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Activator>org.apache.cxf.dosgi.samples.greeter.impl2.rest.Activator</Bundle-Activator>
+ <Import-Package>${bundle.import.package}</Import-Package>
+ <Private-Package>${bundle.private.package}</Private-Package>
+ <DynamicImport-Package>org.apache.cxf.dosgi.dsw.qos,org.apache.cxf</DynamicImport-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/trunk/samples/greeter_rest/impl2/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl2/rest/Activator.java b/trunk/samples/greeter_rest/impl2/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl2/rest/Activator.java
new file mode 100644
index 0000000..6076701
--- /dev/null
+++ b/trunk/samples/greeter_rest/impl2/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl2/rest/Activator.java
@@ -0,0 +1,53 @@
+/**
+ * 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.samples.greeter.impl2.rest;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterService;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class Activator implements BundleActivator {
+
+ private ServiceRegistration registration;
+
+ public void start(BundleContext bc) throws Exception {
+ Dictionary<String, Object> props = getProperties("/rest");
+ registration = bc.registerService(GreeterService.class.getName(),
+ new GreeterServiceImpl2(), props);
+ }
+
+ private Dictionary<String, Object> getProperties(String address) {
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+
+ props.put("service.exported.interfaces", "*");
+ props.put("service.exported.configs", "org.apache.cxf.rs");
+ props.put("service.exported.intents", "HTTP");
+ props.put("org.apache.cxf.rs.httpservice.context", address);
+ props.put("org.apache.cxf.rs.address", "/service");
+ return props;
+ }
+
+ public void stop(BundleContext bc) throws Exception {
+ registration.unregister();
+ }
+}
diff --git a/trunk/samples/greeter_rest/impl2/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl2/rest/GreeterServiceImpl2.java b/trunk/samples/greeter_rest/impl2/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl2/rest/GreeterServiceImpl2.java
new file mode 100644
index 0000000..2b0881f
--- /dev/null
+++ b/trunk/samples/greeter_rest/impl2/src/main/java/org/apache/cxf/dosgi/samples/greeter/impl2/rest/GreeterServiceImpl2.java
@@ -0,0 +1,49 @@
+/**
+ * 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.samples.greeter.impl2.rest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterException;
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterInfo;
+import org.apache.cxf.dosgi.samples.greeter.rest.GreeterService;
+import org.apache.cxf.dosgi.samples.greeter.rest.GreetingPhrase;
+
+public class GreeterServiceImpl2 implements GreeterService {
+
+ private static final String STRANGER_NAME = "Stranger";
+
+ public GreeterInfo greetMe(String name) throws GreeterException {
+ System.out.println("Invoking from GreeterServiceImpl2: greetMe(" + name + ")");
+
+ if (name.equals(STRANGER_NAME)) {
+ throw new GreeterException(name);
+ }
+
+ GreeterInfo info = new GreeterInfo();
+ List<GreetingPhrase> list = new ArrayList<GreetingPhrase>();
+ list.add(new GreetingPhrase("Hello", name));
+ list.add(new GreetingPhrase("Hoi", name));
+ list.add(new GreetingPhrase("Hola", name));
+ list.add(new GreetingPhrase("Bonjour", name));
+ info.setGreetings(list);
+ return info;
+ }
+}
diff --git a/trunk/samples/greeter_rest/interface/pom.xml b/trunk/samples/greeter_rest/interface/pom.xml
new file mode 100644
index 0000000..a7d9985
--- /dev/null
+++ b/trunk/samples/greeter_rest/interface/pom.xml
@@ -0,0 +1,81 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-rest-interface</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Greeter Rest Interface Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../../parent/pom.xml</relativePath>
+ </parent>
+
+ <properties>
+ <bundle.import.package>javax.ws.rs,javax.xml.bind.annotation,*</bundle.import.package>
+ <bundle.export.package>org.apache.cxf.dosgi.samples.greeter.rest</bundle.export.package>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.jsr339-api-m10</artifactId>
+ <version>${servicemix.specs.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.jaxb-api-2.1</artifactId>
+ <version>${servicemix.specs.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>CXF Distributed OSGi Greeter REST Demo Interface Bundle</Bundle-Name>
+ <Bundle-Description>The interfaces of the CXF Distributed OSGi Greeter REST demo</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Import-Package>${bundle.import.package}</Import-Package>
+ <Export-Package>${bundle.export.package}</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreeterException.java b/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreeterException.java
new file mode 100644
index 0000000..fb83637
--- /dev/null
+++ b/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreeterException.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.cxf.dosgi.samples.greeter.rest;
+
+public class GreeterException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+ private String name;
+
+ public GreeterException() {
+ }
+
+ public GreeterException(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String theName) {
+ name = theName;
+ }
+
+ @Override
+ public String toString() {
+ return "GreeterService can not greet " + name;
+ }
+}
diff --git a/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreeterInfo.java b/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreeterInfo.java
new file mode 100644
index 0000000..6dba206
--- /dev/null
+++ b/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreeterInfo.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.samples.greeter.rest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlRootElement;
+@XmlRootElement
+public class GreeterInfo {
+
+ private List<GreetingPhrase> greetings = new ArrayList<GreetingPhrase>();
+
+ public void setGreetings(List<GreetingPhrase> list) {
+ greetings = list;
+ }
+
+ public List<GreetingPhrase> getGreetings() {
+ return greetings;
+ }
+}
diff --git a/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreeterService.java b/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreeterService.java
new file mode 100644
index 0000000..7e67bd4
--- /dev/null
+++ b/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreeterService.java
@@ -0,0 +1,31 @@
+/**
+ * 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.samples.greeter.rest;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+
+@Path("greeter")
+public interface GreeterService {
+
+ @GET
+ @Path("greeting/{name}")
+ GreeterInfo greetMe(@PathParam("name") String name) throws GreeterException;
+}
diff --git a/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreeterService2.java b/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreeterService2.java
new file mode 100644
index 0000000..453157c
--- /dev/null
+++ b/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreeterService2.java
@@ -0,0 +1,24 @@
+/**
+ * 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.samples.greeter.rest;
+
+public interface GreeterService2 {
+
+ GreeterInfo greetMe(String name) throws GreeterException;
+}
diff --git a/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreetingPhrase.java b/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreetingPhrase.java
new file mode 100644
index 0000000..545e091
--- /dev/null
+++ b/trunk/samples/greeter_rest/interface/src/main/java/org/apache/cxf/dosgi/samples/greeter/rest/GreetingPhrase.java
@@ -0,0 +1,64 @@
+/**
+ * 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.samples.greeter.rest;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement
+public class GreetingPhrase {
+
+ private String phrase;
+ private String name;
+
+ public GreetingPhrase() {
+ }
+
+ public GreetingPhrase(String phrase, String name) {
+ this.phrase = phrase;
+ this.name = name;
+ }
+
+ public void setPhrase(String thePhrase) {
+ this.phrase = thePhrase;
+ }
+
+ public String getPhrase() {
+ return phrase;
+ }
+
+ public void setName(String theName) {
+ this.name = theName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public int hashCode() {
+ return phrase.hashCode() + 37 * name.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return GreetingPhrase.class.isAssignableFrom(other.getClass())
+ && phrase.equals(((GreetingPhrase) other).phrase)
+ && name.equals(((GreetingPhrase) other).name);
+ }
+}
diff --git a/trunk/samples/greeter_rest/interface/src/main/resources/OSGI-INF/cxf/jaxrs/GreeterService2-model.xml b/trunk/samples/greeter_rest/interface/src/main/resources/OSGI-INF/cxf/jaxrs/GreeterService2-model.xml
new file mode 100644
index 0000000..2c4da17
--- /dev/null
+++ b/trunk/samples/greeter_rest/interface/src/main/resources/OSGI-INF/cxf/jaxrs/GreeterService2-model.xml
@@ -0,0 +1,7 @@
+<model xmlns="http://cxf.apache.org/jaxrs">
+ <resource name="org.apache.cxf.dosgi.samples.greeter.rest.GreeterService2" path="greeter">
+ <operation name="greetMe" verb="GET" path="greeting/{name}">
+ <param name="name" type="PATH"/>
+ </operation>
+ </resource>
+</model>
diff --git a/trunk/samples/greeter_rest/pom.xml b/trunk/samples/greeter_rest/pom.xml
new file mode 100644
index 0000000..f2f5feb
--- /dev/null
+++ b/trunk/samples/greeter_rest/pom.xml
@@ -0,0 +1,43 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-rest-parent</artifactId>
+ <packaging>pom</packaging>
+ <name>Distributed OSGI Greeter Rest Bundles</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <modules>
+ <module>interface</module>
+ <module>impl</module>
+ <module>impl2</module>
+ <module>client</module>
+ </modules>
+
+</project>
diff --git a/trunk/samples/pom.xml b/trunk/samples/pom.xml
new file mode 100644
index 0000000..89f9e69
--- /dev/null
+++ b/trunk/samples/pom.xml
@@ -0,0 +1,45 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples</artifactId>
+ <version>1.6.0</version>
+ <packaging>pom</packaging>
+ <name>Distributed OSGI RI Sample Bundles</name>
+ <url>http://cxf.apache.org/</url>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+
+ <modules>
+ <module>greeter</module>
+ <module>greeter_rest</module>
+ <module>spring_dm</module>
+ <module>ds</module>
+ <module>discovery</module>
+ <module>security_filter</module>
+ </modules>
+</project>
diff --git a/trunk/samples/security_filter/pom.xml b/trunk/samples/security_filter/pom.xml
new file mode 100644
index 0000000..c68a0e9
--- /dev/null
+++ b/trunk/samples/security_filter/pom.xml
@@ -0,0 +1,73 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-security</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Security Sample Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-servlet_${servlet.version}_spec</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.specs</groupId>
+ <artifactId>org.apache.servicemix.specs.jsr311-api-1.0</artifactId>
+ <version>${servicemix.specs.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>${project.name}</Bundle-Name>
+ <Bundle-Description>An example Distributed OSGi
+ endpoint that passes requests through a
+ security filter</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Private-Package>org.apache.cxf.dosgi.samples.security</Private-Package>
+ <Bundle-Activator>org.apache.cxf.dosgi.samples.security.Activator</Bundle-Activator>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/trunk/samples/security_filter/src/main/java/org/apache/cxf/dosgi/samples/security/Activator.java b/trunk/samples/security_filter/src/main/java/org/apache/cxf/dosgi/samples/security/Activator.java
new file mode 100644
index 0000000..2514106
--- /dev/null
+++ b/trunk/samples/security_filter/src/main/java/org/apache/cxf/dosgi/samples/security/Activator.java
@@ -0,0 +1,65 @@
+/**
+ * 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.samples.security;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.servlet.Filter;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+/**
+ * Registers a REST endpoint and a servlet filter to control access to the
+ * endpoint.
+ */
+public class Activator implements BundleActivator {
+
+ private ServiceRegistration restRegistration;
+ private ServiceRegistration filterRegistration;
+
+ public void start(BundleContext bundleContext) throws Exception {
+ // Register a rest endpoint
+ Dictionary<String, Object> restProps = new Hashtable<String, Object>();
+ restProps.put("service.exported.interfaces", SecureRestEndpoint.class.getName());
+ restProps.put("service.exported.configs", "org.apache.cxf.rs");
+ restProps.put("org.apache.cxf.rs.httpservice.context", "/secure");
+ restRegistration = bundleContext.registerService(SecureRestEndpoint.class.getName(),
+ new SecureRestEndpoint(), restProps);
+
+ // Register a servlet filter (this could be done in another OSGi bundle,
+ // too)
+ Dictionary<String, Object> filterProps = new Hashtable<String, Object>();
+ filterProps.put("org.apache.cxf.httpservice.filter", Boolean.TRUE);
+ // Pax-Web whiteboard (if deployed) will attempt to apply this filter to
+ // servlets by name or URL, and will complain
+ // if neither servletName or urlPatterns are specified. The felix http
+ // service whiteboard may do something similar.
+ filterProps.put("servletNames", "none");
+ filterRegistration = bundleContext.registerService(Filter.class.getName(),
+ new SampleSecurityFilter(), filterProps);
+ }
+
+ public void stop(BundleContext bundleContext) throws Exception {
+ restRegistration.unregister();
+ filterRegistration.unregister();
+ }
+}
diff --git a/trunk/samples/security_filter/src/main/java/org/apache/cxf/dosgi/samples/security/SampleSecurityFilter.java b/trunk/samples/security_filter/src/main/java/org/apache/cxf/dosgi/samples/security/SampleSecurityFilter.java
new file mode 100644
index 0000000..bfc8504
--- /dev/null
+++ b/trunk/samples/security_filter/src/main/java/org/apache/cxf/dosgi/samples/security/SampleSecurityFilter.java
@@ -0,0 +1,61 @@
+/**
+ * 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.samples.security;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A filter that requires a query string of "secure" to invoke the protected
+ * resource.
+ */
+public class SampleSecurityFilter implements Filter {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SampleSecurityFilter.class);
+
+ public void destroy() {
+ LOG.info("destroy()");
+ }
+
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ if ("secure".equals(((HttpServletRequest)request).getQueryString())) {
+ LOG.info("Access granted");
+ chain.doFilter(request, response);
+ } else {
+ LOG.warn("Access denied");
+ ((HttpServletResponse)response).sendError(HttpServletResponse.SC_FORBIDDEN);
+ }
+ }
+
+ public void init(FilterConfig config) throws ServletException {
+ LOG.info("init()");
+ }
+}
diff --git a/trunk/samples/security_filter/src/main/java/org/apache/cxf/dosgi/samples/security/SecureRestEndpoint.java b/trunk/samples/security_filter/src/main/java/org/apache/cxf/dosgi/samples/security/SecureRestEndpoint.java
new file mode 100644
index 0000000..8c066ac
--- /dev/null
+++ b/trunk/samples/security_filter/src/main/java/org/apache/cxf/dosgi/samples/security/SecureRestEndpoint.java
@@ -0,0 +1,35 @@
+/**
+ * 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.samples.security;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+@Path("/")
+public class SecureRestEndpoint {
+
+ @GET
+ @Path("hello")
+ @Produces(MediaType.TEXT_PLAIN)
+ public String sayHello() {
+ return "Hello and congratulations, you made it past the security filter";
+ }
+}
diff --git a/trunk/samples/spring_dm/client/pom.xml b/trunk/samples/spring_dm/client/pom.xml
new file mode 100644
index 0000000..9f689e4
--- /dev/null
+++ b/trunk/samples/spring_dm/client/pom.xml
@@ -0,0 +1,60 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-spring-dm-client</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Spring-DM Sample Client Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-spring-dm-parent</artifactId>
+ <version>1.6.0</version>
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-spring-dm-interface</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>CXF Distributed OSGi Spring-DM Sample Client Bundle</Bundle-Name>
+ <Bundle-Description>The client-side implementation of the Distributed OSGi with Spring-DM sample</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Import-Package>org.apache.cxf.dosgi.samples.springdm</Import-Package>
+ <Private-Package>org.apache.cxf.dosgi.samples.springdm.client</Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/samples/spring_dm/client/src/main/java/org/apache/cxf/dosgi/samples/springdm/client/DinnerServiceConsumer.java b/trunk/samples/spring_dm/client/src/main/java/org/apache/cxf/dosgi/samples/springdm/client/DinnerServiceConsumer.java
new file mode 100644
index 0000000..8fe34c8
--- /dev/null
+++ b/trunk/samples/spring_dm/client/src/main/java/org/apache/cxf/dosgi/samples/springdm/client/DinnerServiceConsumer.java
@@ -0,0 +1,38 @@
+/**
+ * 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.samples.springdm.client;
+
+import org.apache.cxf.dosgi.samples.springdm.DinnerService;
+import org.apache.cxf.dosgi.samples.springdm.Restaurant;
+
+public class DinnerServiceConsumer {
+
+ DinnerService dinnerService;
+
+ public void setDinnerService(DinnerService ds) {
+ dinnerService = ds;
+ }
+
+ public void start() {
+ System.out.println("Found the following restaurants:");
+ for (Restaurant r : dinnerService.findRestaurants("nice and not too expensive!")) {
+ System.out.format(" %s (%s) Rating: %d\n", r.getName(), r.getAddress(), r.getRating());
+ }
+ }
+}
diff --git a/trunk/samples/spring_dm/client/src/main/resources/META-INF/spring/client-spring.xml b/trunk/samples/spring_dm/client/src/main/resources/META-INF/spring/client-spring.xml
new file mode 100644
index 0000000..252df7b
--- /dev/null
+++ b/trunk/samples/spring_dm/client/src/main/resources/META-INF/spring/client-spring.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+ <osgi:reference id="dinnerServiceRef" interface="org.apache.cxf.dosgi.samples.springdm.DinnerService"/>
+
+ <bean class="org.apache.cxf.dosgi.samples.springdm.client.DinnerServiceConsumer"
+ init-method="start">
+ <property name="dinnerService" ref="dinnerServiceRef"/>
+ </bean>
+</beans>
diff --git a/trunk/samples/spring_dm/client/src/main/resources/OSGI-INF/remote-service/remote-services.xml b/trunk/samples/spring_dm/client/src/main/resources/OSGI-INF/remote-service/remote-services.xml
new file mode 100644
index 0000000..19c44db
--- /dev/null
+++ b/trunk/samples/spring_dm/client/src/main/resources/OSGI-INF/remote-service/remote-services.xml
@@ -0,0 +1,29 @@
+<!--
+ 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.
+-->
+<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0">
+ <endpoint-description>
+ <property name="objectClass">
+ <array>
+ <value>org.apache.cxf.dosgi.samples.springdm.DinnerService</value>
+ </array>
+ </property>
+ <property name="endpoint.id">http://localhost:9000/org/apache/cxf/dosgi/samples/springdm/DinnerService</property>
+ <property name="service.imported.configs">org.apache.cxf.ws</property>
+ </endpoint-description>
+</endpoint-descriptions>
diff --git a/trunk/samples/spring_dm/impl/pom.xml b/trunk/samples/spring_dm/impl/pom.xml
new file mode 100644
index 0000000..9571883
--- /dev/null
+++ b/trunk/samples/spring_dm/impl/pom.xml
@@ -0,0 +1,60 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-spring-dm-impl</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Spring-DM Sample Implementation Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-spring-dm-parent</artifactId>
+ <version>1.6.0</version>
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-spring-dm-interface</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>CXF Distributed OSGi Spring-DM Sample Implementation Bundle</Bundle-Name>
+ <Bundle-Description>The server-side implementation of the Distributed OSGi with Spring-DM sample</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Import-Package>org.apache.cxf.dosgi.samples.springdm</Import-Package>
+ <Private-Package>org.apache.cxf.dosgi.samples.springdm.impl</Private-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/samples/spring_dm/impl/src/main/java/org/apache/cxf/dosgi/samples/springdm/impl/DinnerServiceImpl.java b/trunk/samples/spring_dm/impl/src/main/java/org/apache/cxf/dosgi/samples/springdm/impl/DinnerServiceImpl.java
new file mode 100644
index 0000000..e98e192
--- /dev/null
+++ b/trunk/samples/spring_dm/impl/src/main/java/org/apache/cxf/dosgi/samples/springdm/impl/DinnerServiceImpl.java
@@ -0,0 +1,39 @@
+/**
+ * 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.samples.springdm.impl;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.cxf.dosgi.samples.springdm.DinnerService;
+import org.apache.cxf.dosgi.samples.springdm.Restaurant;
+
+public class DinnerServiceImpl implements DinnerService {
+
+ List<Restaurant> restaurants = Arrays.asList(
+ new Restaurant("Jojo's", "1 food way", 3),
+ new Restaurant("Boohaa's", "95 forage ave", 1),
+ new Restaurant("MicMac", "Plastic Plaza", 1)
+ );
+
+ public List<Restaurant> findRestaurants(String query) {
+ System.out.println("Hey! Someone's using the Dinner Service! Query: " + query);
+ return restaurants;
+ }
+}
diff --git a/trunk/samples/spring_dm/impl/src/main/resources/META-INF/spring/spring.xml b/trunk/samples/spring_dm/impl/src/main/resources/META-INF/spring/spring.xml
new file mode 100644
index 0000000..7c798c4
--- /dev/null
+++ b/trunk/samples/spring_dm/impl/src/main/resources/META-INF/spring/spring.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">
+ <osgi:service interface="org.apache.cxf.dosgi.samples.springdm.DinnerService">
+ <osgi:service-properties>
+ <entry key="service.exported.interfaces" value="*" />
+ </osgi:service-properties>
+
+ <bean class="org.apache.cxf.dosgi.samples.springdm.impl.DinnerServiceImpl" />
+ </osgi:service>
+</beans>
diff --git a/trunk/samples/spring_dm/interface/pom.xml b/trunk/samples/spring_dm/interface/pom.xml
new file mode 100644
index 0000000..4c2dd88
--- /dev/null
+++ b/trunk/samples/spring_dm/interface/pom.xml
@@ -0,0 +1,52 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-spring-dm-interface</artifactId>
+ <packaging>bundle</packaging>
+ <name>Distributed OSGI Spring-DM Sample Interface Bundle</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-spring-dm-parent</artifactId>
+ <version>1.6.0</version>
+ </parent>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-Name>CXF Distributed OSGi Spring-DM Sample Interface Bundle</Bundle-Name>
+ <Bundle-Description>The interfaces of the Distributed OSGi with Spring-DM sample</Bundle-Description>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Import-Package />
+ <Export-Package>org.apache.cxf.dosgi.samples.springdm</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/samples/spring_dm/interface/src/main/java/org/apache/cxf/dosgi/samples/springdm/DinnerService.java b/trunk/samples/spring_dm/interface/src/main/java/org/apache/cxf/dosgi/samples/springdm/DinnerService.java
new file mode 100644
index 0000000..da296b3
--- /dev/null
+++ b/trunk/samples/spring_dm/interface/src/main/java/org/apache/cxf/dosgi/samples/springdm/DinnerService.java
@@ -0,0 +1,25 @@
+/**
+ * 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.samples.springdm;
+
+import java.util.List;
+
+public interface DinnerService {
+ List<Restaurant> findRestaurants(String searchQuery);
+}
diff --git a/trunk/samples/spring_dm/interface/src/main/java/org/apache/cxf/dosgi/samples/springdm/Restaurant.java b/trunk/samples/spring_dm/interface/src/main/java/org/apache/cxf/dosgi/samples/springdm/Restaurant.java
new file mode 100644
index 0000000..f6f06b1
--- /dev/null
+++ b/trunk/samples/spring_dm/interface/src/main/java/org/apache/cxf/dosgi/samples/springdm/Restaurant.java
@@ -0,0 +1,59 @@
+/**
+ * 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.samples.springdm;
+
+public class Restaurant {
+
+ private String name;
+ private String address;
+ private int rating;
+
+ public Restaurant() {
+ }
+
+ public Restaurant(String name, String address, int rating) {
+ this.name = name;
+ this.address = address;
+ this.rating = rating;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getRating() {
+ return rating;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setAddress(String address) {
+ this.address = address;
+ }
+
+ public void setRating(int rating) {
+ this.rating = rating;
+ }
+}
diff --git a/trunk/samples/spring_dm/pom.xml b/trunk/samples/spring_dm/pom.xml
new file mode 100644
index 0000000..f40a8e6
--- /dev/null
+++ b/trunk/samples/spring_dm/pom.xml
@@ -0,0 +1,41 @@
+<?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>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-spring-dm-parent</artifactId>
+ <packaging>pom</packaging>
+ <name>Distributed OSGI Spring-DM Sample</name>
+ <version>1.6.0</version>
+
+ <parent>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <modules>
+ <module>interface</module>
+ <module>impl</module>
+ <module>client</module>
+ </modules>
+</project>
diff --git a/trunk/systests2/common/pom.xml b/trunk/systests2/common/pom.xml
new file mode 100644
index 0000000..5c9ef18
--- /dev/null
+++ b/trunk/systests2/common/pom.xml
@@ -0,0 +1,108 @@
+<?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.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.apache.cxf.dosgi.systests</groupId>
+ <artifactId>cxf-dosgi-ri-systests2-common</artifactId>
+ <packaging>bundle</packaging>
+
+ <name>Distributed OSGi System Tests Common Classes</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.ops4j.pax.tinybundles</groupId>
+ <artifactId>tinybundles</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-core</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxws</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-databinding-aegis</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-interface</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <excludeDependencies>true</excludeDependencies>
+ <instructions>
+ <Bundle-Name>Bundle containing common code for the CXF DOSGi system tests</Bundle-Name>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
+ <!-- The TinyBundles are only used during the 'configure' stage and are therefore not
+ needed at runtime.
+ Other imports marked as optional as not all tests have the same dependencies. -->
+ <Import-Package>
+ !org.ops4j.pax.swissbox.tinybundles.*,
+ *;resolution:=optional
+ </Import-Package>
+ <Export-Package>*</Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test1/GreeterDataImpl.java b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test1/GreeterDataImpl.java
new file mode 100644
index 0000000..31f2988
--- /dev/null
+++ b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test1/GreeterDataImpl.java
@@ -0,0 +1,39 @@
+/**
+ * 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.systests2.common.test1;
+
+import org.apache.cxf.dosgi.samples.greeter.GreeterData;
+
+public class GreeterDataImpl implements GreeterData {
+
+ public int getAge() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public String getName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public boolean isException() {
+ // TODO Auto-generated method stub
+ return false;
+ }
+}
diff --git a/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test1/MyActivator.java b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test1/MyActivator.java
new file mode 100644
index 0000000..6740725
--- /dev/null
+++ b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test1/MyActivator.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.cxf.dosgi.systests2.common.test1;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class MyActivator implements BundleActivator {
+
+ private ServiceTracker startTracker;
+ private ServiceTracker tracker;
+
+ public void start(final BundleContext bc) throws Exception {
+ Filter filter = bc.createFilter("(&(objectClass=java.lang.Object)(testName=test1))");
+ tracker = new MyServiceTracker(bc);
+
+ // The start tracker waits until a service from the test class is set before the
+ // 'MyServiceTracker' is activated.
+ startTracker = new StartServiceTracker(bc, filter, tracker);
+ startTracker.open();
+ }
+
+ public void stop(BundleContext bc) throws Exception {
+ startTracker.close();
+ tracker.close();
+ }
+}
diff --git a/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test1/MyServiceTracker.java b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test1/MyServiceTracker.java
new file mode 100644
index 0000000..8cc1e42
--- /dev/null
+++ b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test1/MyServiceTracker.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.systests2.common.test1;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.samples.greeter.GreeterException;
+import org.apache.cxf.dosgi.samples.greeter.GreeterService;
+import org.apache.cxf.dosgi.samples.greeter.GreetingPhrase;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class MyServiceTracker extends ServiceTracker {
+
+ private static StringBuffer invocationResult = new StringBuffer();
+
+ public MyServiceTracker(BundleContext context) {
+ super(context, GreeterService.class.getName(), null);
+ }
+
+ public Object addingService(ServiceReference reference) {
+ Object svc = super.addingService(reference);
+ if (svc instanceof GreeterService) {
+ System.out.println("[client] Got a GreeterService...");
+ invokeGreeter((GreeterService) svc);
+ }
+ return svc;
+ }
+
+ public static String getResult() {
+ return invocationResult.toString();
+ }
+
+ private void invokeGreeter(GreeterService svc) {
+ try {
+ Map<GreetingPhrase, String> result = svc.greetMe("OSGi");
+ for (Map.Entry<GreetingPhrase, String> e : result.entrySet()) {
+ GreetingPhrase key = e.getKey();
+ invocationResult.append(key.getPhrase());
+ invocationResult.append(e.getValue());
+ }
+ try {
+ svc.greetMe(new GreeterDataImpl());
+ } catch (GreeterException ex) {
+ invocationResult.append(";exception");
+ }
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put("result", invocationResult.toString());
+ props.put("testResult", "test1");
+
+ System.out.println("[client] Successfully invoked remote service. Registering test response service...");
+ context.registerService(String.class.getName(), "test1", props);
+ } catch (Exception x) {
+ System.err.println("[client] Error during remote service invocation:");
+ x.printStackTrace(System.err);
+ }
+ }
+}
\ No newline at end of file
diff --git a/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test1/StartServiceTracker.java b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test1/StartServiceTracker.java
new file mode 100644
index 0000000..40319ec
--- /dev/null
+++ b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test1/StartServiceTracker.java
@@ -0,0 +1,41 @@
+/**
+ * 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.systests2.common.test1;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class StartServiceTracker extends ServiceTracker {
+
+ private ServiceTracker tracker;
+
+ public StartServiceTracker(BundleContext context, Filter filter, ServiceTracker tracker) {
+ super(context, filter, null);
+ this.tracker = tracker;
+ }
+
+ @Override
+ public Object addingService(ServiceReference reference) {
+ System.out.println("Test object available, so starting the service client tracker...");
+ tracker.open();
+ return super.addingService(reference);
+ }
+}
diff --git a/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/Test2Service.java b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/Test2Service.java
new file mode 100644
index 0000000..806cdc9
--- /dev/null
+++ b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/Test2Service.java
@@ -0,0 +1,23 @@
+/**
+ * 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.systests2.common.test2;
+
+public interface Test2Service {
+ String getRemoteStackTrace();
+}
diff --git a/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/client/ClientActivator.java b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/client/ClientActivator.java
new file mode 100644
index 0000000..0efd943
--- /dev/null
+++ b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/client/ClientActivator.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.systests2.common.test2.client;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class ClientActivator implements BundleActivator {
+
+ private ServiceTracker tracker;
+
+ public void start(final BundleContext bc) throws Exception {
+ tracker = new Test2ServiceTracker(bc);
+ tracker.open();
+ }
+
+ public void stop(BundleContext bc) throws Exception {
+ tracker.close();
+ }
+}
diff --git a/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/client/Test2ServiceTracker.java b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/client/Test2ServiceTracker.java
new file mode 100644
index 0000000..5933e21
--- /dev/null
+++ b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/client/Test2ServiceTracker.java
@@ -0,0 +1,72 @@
+/**
+ * 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.systests2.common.test2.client;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.cxf.dosgi.systests2.common.test2.Test2Service;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class Test2ServiceTracker extends ServiceTracker {
+
+ public Test2ServiceTracker(BundleContext context) {
+ super(context, getFilter(context), null);
+ }
+
+ private static Filter getFilter(BundleContext context) {
+ Filter f = null;
+ try {
+ // It's very important that the service.imported condition is there too
+ // otherwise the tracker will make a local 'direct' invocation on the service.
+ // The service.imported forces a proxy lookup.
+ f = context.createFilter("(&(objectClass=" + Test2Service.class.getName() + ")(service.imported=*))");
+ } catch (InvalidSyntaxException e) {
+ e.printStackTrace();
+ }
+ return f;
+ }
+
+ @Override
+ public Object addingService(ServiceReference reference) {
+ Object svc = super.addingService(reference);
+ if (svc instanceof Test2Service) {
+ System.out.println("*** Ref: " + reference);
+ for (String key : reference.getPropertyKeys()) {
+ System.out.println(" " + key + "-" + reference.getProperty(key));
+ }
+
+ invokeRemoteTestService(context, (Test2Service) svc);
+ }
+ return svc;
+ }
+
+ private void invokeRemoteTestService(BundleContext bc, Test2Service svc) {
+ String res = svc.getRemoteStackTrace();
+
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put("result", res);
+ props.put("testResult", "test2");
+ bc.registerService(String.class.getName(), "test2", props);
+ }
+}
diff --git a/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/server/ServerActivator.java b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/server/ServerActivator.java
new file mode 100644
index 0000000..315b250
--- /dev/null
+++ b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/server/ServerActivator.java
@@ -0,0 +1,53 @@
+/**
+ * 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.systests2.common.test2.server;
+
+import java.net.ServerSocket;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.cxf.dosgi.systests2.common.test2.Test2Service;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class ServerActivator implements BundleActivator {
+
+ private ServiceRegistration reg;
+
+ public void start(BundleContext bc) throws Exception {
+ Test2Service svc = new Test2ServiceImpl();
+
+ // Dynamically assign a free port
+ int freePort = new ServerSocket(0).getLocalPort();
+ String url = "http://localhost:" + freePort + "/test2";
+ System.out.println("*** Server using URL: " + url);
+
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put("service.exported.interfaces", "*");
+ props.put("service.exported.configs", "org.apache.cxf.ws");
+ props.put("endpoint.id", url);
+
+ reg = bc.registerService(Test2Service.class.getName(), svc, props);
+ }
+
+ public void stop(BundleContext bc) throws Exception {
+ reg.unregister();
+ }
+}
diff --git a/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/server/Test2ServiceImpl.java b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/server/Test2ServiceImpl.java
new file mode 100644
index 0000000..c03f572
--- /dev/null
+++ b/trunk/systests2/common/src/main/java/org/apache/cxf/dosgi/systests2/common/test2/server/Test2ServiceImpl.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.cxf.dosgi.systests2.common.test2.server;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.apache.cxf.dosgi.systests2.common.test2.Test2Service;
+
+public class Test2ServiceImpl implements Test2Service {
+
+ public String getRemoteStackTrace() {
+ Throwable th = new Throwable("Throwable created on the server");
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ th.printStackTrace(pw);
+
+ return sw.toString();
+ }
+}
diff --git a/trunk/systests2/common/src/main/resources/rs-test1.xml b/trunk/systests2/common/src/main/resources/rs-test1.xml
new file mode 100644
index 0000000..7392d24
--- /dev/null
+++ b/trunk/systests2/common/src/main/resources/rs-test1.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0"
+ xmlns:other="http://www.acme.org/xmlns/other/v1.0.0">
+ <endpoint-description>
+ <property name="objectClass">
+ <array>
+ <value>org.apache.cxf.dosgi.samples.greeter.GreeterService</value>
+ </array>
+ </property>
+ <property name="endpoint.id">http://localhost:9191/grrr</property>
+ <property name="service.imported.configs">org.apache.cxf.ws</property>
+ </endpoint-description>
+</endpoint-descriptions>
+
diff --git a/trunk/systests2/multi-bundle/pom.xml b/trunk/systests2/multi-bundle/pom.xml
new file mode 100644
index 0000000..cafdd73
--- /dev/null
+++ b/trunk/systests2/multi-bundle/pom.xml
@@ -0,0 +1,258 @@
+<?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.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../../parent/pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.apache.cxf.dosgi.systests</groupId>
+ <artifactId>cxf-dosgi-ri-systests2-multibundle</artifactId>
+ <packaging>jar</packaging>
+
+ <name>Distributed OSGi System Tests Multi-Bundle</name>
+
+ <!--
+ When changing code make sure to run the distro before testing
+ or you will be testing the old code.
+
+ -->
+
+ <dependencies>
+ <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>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-inject</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-forked</artifactId>
+ </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>
+ <version>1.6.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.eclipse</groupId>
+ <artifactId>org.eclipse.osgi</artifactId>
+ </dependency>
+ <!--
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.framework</artifactId>
+ <scope>test</scope>
+ </dependency>
+ -->
+
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.systests</groupId>
+ <artifactId>cxf-dosgi-ri-systests2-common</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-api</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-core</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-bindings-soap</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-transports-http-jetty</artifactId>
+ <version>${cxf.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf</groupId>
+ <artifactId>cxf-rt-frontend-jaxrs</artifactId>
+ <version>${cxf.version}</version>
+ </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>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-discovery-distributed-zookeeper-server</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-impl</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-interface</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-rest-impl</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.dosgi.samples</groupId>
+ <artifactId>cxf-dosgi-ri-samples-greeter-rest-interface</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.cxf.services.xkms</groupId>
+ <artifactId>cxf-services-xkms-features</artifactId>
+ <version>${cxf.version}</version>
+ <type>xml</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.services.xkms</groupId>
+ <artifactId>cxf-services-xkms-common</artifactId>
+ <version>${cxf.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.services.xkms</groupId>
+ <artifactId>cxf-services-xkms-client</artifactId>
+ <version>${cxf.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.apache.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-multibundle-distribution</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <classifier>dir</classifier>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <!-- use pax exam maven plugin -->
+
+ <!-- Note: settings and dependencies for the final osgi runtime
+ (TESTS) are just used in TestCases that annotated with this: @RunWith( MavenConfiguredJUnit4TestRunner.class
+ ) All other testcases will use their own settings/provisioning inside @Configure
+ Methods. -->
+ <plugin>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>maven-paxexam-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>generate-config</id>
+ <goals>
+ <goal>generate-config</goal>
+ <goal>generate-depends-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <options>
+ <platform>equinox</platform>
+ <profiles>log</profiles>
+ </options>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <!-- <org.apache.cxf.dosgi.test.debug.port>5005</org.apache.cxf.dosgi.test.debug.port>
+ <org.apache.cxf.dosgi.test.serviceWaitTimeout>180</org.apache.cxf.dosgi.test.serviceWaitTimeout> -->
+ <java.util.logging.config.file>${project.build.directory}/test-classes/logging.properties</java.util.logging.config.file>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/AbstractDosgiTest.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/AbstractDosgiTest.java
new file mode 100644
index 0000000..4f4f9ea
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/AbstractDosgiTest.java
@@ -0,0 +1,157 @@
+/**
+ * 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.systests2.multi;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URL;
+import java.util.Collection;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.cxf.aegis.databinding.AegisDatabinding;
+import org.apache.cxf.dosgi.samples.greeter.GreeterService;
+import org.apache.cxf.frontend.ClientProxyFactoryBean;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+public class AbstractDosgiTest {
+
+ private static final int TIMEOUT = 20;
+
+ /**
+ * Sleeps for a short interval, throwing an exception if timeout has been reached.
+ * Used to facilitate a retry interval with timeout when used in a loop.
+ *
+ * @param startTime the start time of the entire operation in milliseconds
+ * @param timeout the timeout duration for the entire operation in seconds
+ * @param message the error message to use when timeout occurs
+ * @throws InterruptedException if interrupted while sleeping
+ */
+ private static void sleepOrTimeout(long startTime, long timeout, String message) throws
+ InterruptedException, TimeoutException {
+ timeout *= 1000; // seconds to millis
+ long elapsed = System.currentTimeMillis() - startTime;
+ long remaining = timeout - elapsed;
+ if (remaining <= 0) {
+ throw new TimeoutException(message);
+ }
+ long interval = Math.min(remaining, 1000);
+ Thread.sleep(interval);
+ }
+
+ @SuppressWarnings({
+ "rawtypes", "unchecked"
+ })
+ protected ServiceReference waitService(BundleContext bc, Class cls, String filter, int timeout)
+ throws Exception {
+ System.out.println("Waiting for service: " + cls + " " + filter);
+ long startTime = System.currentTimeMillis();
+ while (true) {
+ Collection refs = bc.getServiceReferences(cls, filter);
+ if (refs != null && refs.size() > 0) {
+ return (ServiceReference)refs.iterator().next();
+ }
+ sleepOrTimeout(startTime, timeout, "Service not found: " + cls + " " + filter);
+ }
+ }
+
+ protected void waitPort(int port) throws Exception {
+ System.out.println("Waiting for server to appear on port: " + port);
+ long startTime = System.currentTimeMillis();
+ while (true) {
+ Socket s = null;
+ try {
+ s = new Socket((String)null, port);
+ // yep, its available
+ return;
+ } catch (IOException e) {
+ sleepOrTimeout(startTime, TIMEOUT, "Timeout waiting for port " + port);
+ } finally {
+ if (s != null) {
+ try {
+ s.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+ }
+
+ protected GreeterService createGreeterServiceProxy(String serviceUri) {
+ ClientProxyFactoryBean factory = new ClientProxyFactoryBean();
+ factory.setServiceClass(GreeterService.class);
+ factory.setAddress(serviceUri);
+ factory.getServiceFactory().setDataBinding(new AegisDatabinding());
+ return (GreeterService)factory.create();
+ }
+
+ protected Bundle getBundleByName(BundleContext bc, String name) {
+ for (Bundle bundle : bc.getBundles()) {
+ if (bundle.getSymbolicName().equals(name)) {
+ return bundle;
+ }
+ }
+ return null;
+ }
+
+ 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();
+ }
+ }
+
+ protected void waitWebPage(String urlSt) throws InterruptedException, TimeoutException {
+ System.out.println("Waiting for url " + urlSt);
+ HttpURLConnection con = null;
+ long startTime = System.currentTimeMillis();
+ while (true) {
+ try {
+ URL url = new URL(urlSt);
+ con = (HttpURLConnection)url.openConnection();
+ int status = con.getResponseCode();
+ if (status == 200) {
+ return;
+ }
+ } catch (ConnectException e) {
+ // Ignore connection refused
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ } catch (IOException e) {
+ throw new RuntimeException(e.getMessage(), e);
+ } finally {
+ if (con != null) {
+ con.disconnect();
+ }
+ }
+ sleepOrTimeout(startTime, TIMEOUT, "Timeout waiting for web page " + urlSt);
+ }
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/MultiBundleTools.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/MultiBundleTools.java
new file mode 100644
index 0000000..48e87ac
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/MultiBundleTools.java
@@ -0,0 +1,119 @@
+/**
+ * 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.systests2.multi;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+import java.util.jar.Attributes;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Option;
+
+public final class MultiBundleTools {
+
+ private MultiBundleTools() {
+ }
+
+ private static Properties getProps(File distroDir) throws FileNotFoundException, IOException {
+ Properties p = new Properties();
+ File confFile = new File(distroDir, "conf/felix.config.properties.append");
+ p.load(new FileInputStream(confFile));
+ return p;
+ }
+
+ private static int getDistroBundles(File distroDir,
+ Properties props,
+ Map<Integer, String> bundles) throws Exception {
+ int startLevel = Integer.parseInt(props.getProperty("org.osgi.framework.startlevel.beginning"));
+ for (int i = 0; i <= startLevel; i++) {
+ String val = props.getProperty("felix.auto.start." + i);
+ if (val != null) {
+ if (val.startsWith("file:")) {
+ File fullDir = new File(distroDir, val.substring("file:".length()));
+ bundles.put(i, fullDir.toURI().toASCIIString());
+ } else {
+ if (!val.contains("org.osgi.compendium")) {
+ // We're skipping that one as it's pulled in explicitly in the test
+ bundles.put(i, val);
+ }
+ }
+ }
+ }
+ return startLevel + 1; // Add 1 to start level to be on the safe side
+ }
+
+ private static File getRootDirectory() {
+ String resourceName = "/" + MultiBundleTools.class.getName().replace('.', '/') + ".class";
+ URL curURL = MultiBundleTools.class.getResource(resourceName);
+ File curFile = new File(curURL.getFile());
+ String curString = curFile.getAbsolutePath();
+ File curBase = new File(curString.substring(0, curString.length() - resourceName.length()));
+ return curBase.getParentFile().getParentFile();
+ }
+
+ private static Option[] getDistroBundleOptions() throws Exception {
+ Map<Integer, String> bundles = new TreeMap<Integer, String>();
+ File root = getRootDirectory();
+ File depRoot = new File(root, "target/dependency");
+ File distroDir = depRoot.listFiles()[0];
+ Properties props = getProps(distroDir);
+ getDistroBundles(distroDir, props, bundles);
+ List<Option> opts = new ArrayList<Option>();
+
+ /*
+ String sysPackagesValue = props.getProperty("org.osgi.framework.system.packages");
+ opts.add(CoreOptions.frameworkProperty("org.osgi.framework.system.packages")
+ .value(sysPackagesValue));
+ */
+
+ for (Map.Entry<Integer, String> entry : bundles.entrySet()) {
+ String bundleUri = entry.getValue();
+ URL bundleURL = new URL(bundleUri);
+ JarInputStream bundleJar = new JarInputStream(bundleURL.openStream());
+ Manifest manifest = bundleJar.getManifest();
+ Attributes host = manifest.getAttributes("Fragment-Host");
+ if (host != null) {
+ System.out.println(bundleUri);
+ }
+ bundleJar.close();
+
+ opts.add(CoreOptions.bundle(bundleUri));
+ }
+ System.out.println(opts);
+ return opts.toArray(new Option[opts.size()]);
+ }
+
+ public static Option getDistroWithDiscovery() throws Exception {
+ return getDistro();
+ }
+
+ public static Option getDistro() throws Exception {
+ return CoreOptions.composite(getDistroBundleOptions());
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestCustomIntent.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestCustomIntent.java
new file mode 100644
index 0000000..2200567
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestCustomIntent.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.systests2.multi;
+
+import java.io.InputStream;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import junit.framework.Assert;
+
+import org.apache.cxf.dosgi.samples.greeter.GreeterService;
+import org.apache.cxf.dosgi.samples.greeter.GreetingPhrase;
+import org.apache.cxf.dosgi.systests2.multi.customintent.AddGreetingPhraseInterceptor;
+import org.apache.cxf.dosgi.systests2.multi.customintent.CustomFeature;
+import org.apache.cxf.dosgi.systests2.multi.customintent.CustomIntentActivator;
+import org.apache.cxf.dosgi.systests2.multi.customintent.service.EmptyGreeterService;
+import org.apache.cxf.dosgi.systests2.multi.customintent.service.GreeterServiceWithCustomIntentActivator;
+import org.apache.cxf.frontend.ClientProxyFactoryBean;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+import static org.ops4j.pax.exam.CoreOptions.frameworkStartLevel;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.provision;
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+@RunWith(PaxExam.class)
+public class TestCustomIntent extends AbstractDosgiTest {
+
+ @Inject
+ BundleContext bundleContext;
+
+ protected static InputStream getCustomIntentBundle() {
+ return TinyBundles.bundle()
+ .add(CustomIntentActivator.class)
+ .add(CustomFeature.class)
+ .add(AddGreetingPhraseInterceptor.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "CustomIntent")
+ .set(Constants.BUNDLE_ACTIVATOR, CustomIntentActivator.class.getName()).build(TinyBundles.withBnd());
+ }
+
+ protected static InputStream getServiceBundle() {
+ return TinyBundles.bundle()
+ .add(GreeterServiceWithCustomIntentActivator.class)
+ .add(EmptyGreeterService.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "EmptyGreeterService")
+ .set(Constants.BUNDLE_ACTIVATOR, GreeterServiceWithCustomIntentActivator.class.getName())
+ .build(TinyBundles.withBnd());
+ }
+
+ @Configuration
+ public static Option[] configure() throws Exception {
+ return new Option[] {
+ MultiBundleTools.getDistro(),
+ systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"),
+ mavenBundle().groupId("org.apache.servicemix.bundles")
+ .artifactId("org.apache.servicemix.bundles.junit").version("4.9_2"),
+ mavenBundle().groupId("org.apache.cxf.dosgi.samples")
+ .artifactId("cxf-dosgi-ri-samples-greeter-interface").versionAsInProject(),
+ mavenBundle().groupId("org.apache.cxf.dosgi.systests")
+ .artifactId("cxf-dosgi-ri-systests2-common").versionAsInProject(),
+ streamBundle(getCustomIntentBundle()).noStart(),
+ provision(getServiceBundle()),
+ frameworkStartLevel(100) };
+ }
+
+ @Test
+ public void testCustomIntent() throws Exception {
+ // There should be warnings of unsatisfied intent myIntent in the log at debug level
+ Thread.sleep(2000);
+ getBundleByName(bundleContext, "CustomIntent").start();
+ waitPort(9090);
+
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(ClientProxyFactoryBean.class.getClassLoader());
+ try {
+ GreeterService greeterService = createGreeterServiceProxy("http://localhost:9090/greeter");
+ Map<GreetingPhrase, String> result = greeterService.greetMe("Chris");
+ Assert.assertEquals(1, result.size());
+ GreetingPhrase phrase = result.keySet().iterator().next();
+ Assert.assertEquals("Hi from custom intent", phrase.getPhrase());
+ } finally {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestDiscoveryExport.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestDiscoveryExport.java
new file mode 100644
index 0000000..84cf342
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestDiscoveryExport.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.systests2.multi;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.inject.Inject;
+
+import junit.framework.Assert;
+
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.data.Stat;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+import static org.ops4j.pax.exam.CoreOptions.frameworkStartLevel;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+@RunWith(PaxExam.class)
+public class TestDiscoveryExport extends AbstractDosgiTest {
+
+ private static final String GREETER_ZOOKEEPER_NODE
+ = "/osgi/service_registry/org/apache/cxf/dosgi/samples/greeter/GreeterService/localhost#9090##greeter";
+
+ @Inject
+ BundleContext bundleContext;
+
+ @Inject
+ ConfigurationAdmin configAdmin;
+
+ @Configuration
+ public static Option[] configure() throws Exception {
+ return new Option[] {
+ MultiBundleTools.getDistroWithDiscovery(),
+ systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"),
+ mavenBundle().groupId("org.apache.servicemix.bundles")
+ .artifactId("org.apache.servicemix.bundles.junit").version("4.9_2"),
+ mavenBundle().groupId("org.apache.cxf.dosgi.samples")
+ .artifactId("cxf-dosgi-ri-samples-greeter-interface").versionAsInProject(),
+ mavenBundle().groupId("org.apache.cxf.dosgi.samples")
+ .artifactId("cxf-dosgi-ri-samples-greeter-impl").versionAsInProject(),
+ mavenBundle().groupId("org.apache.cxf.dosgi.systests")
+ .artifactId("cxf-dosgi-ri-systests2-common").versionAsInProject(),
+ frameworkStartLevel(100),
+ //CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005")
+ };
+ }
+
+ @Test
+ public void testDiscoveryExport() throws Exception {
+ final int zkPort = getFreePort();
+ System.out.println("*** Port for ZooKeeper Server: " + zkPort);
+ updateZkServerConfig(zkPort, configAdmin);
+ updateZkClientConfig(zkPort, configAdmin);
+ ZooKeeper zk = new ZooKeeper("localhost:" + zkPort, 1000, null);
+ assertNodeExists(zk, GREETER_ZOOKEEPER_NODE, 14000);
+ 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.cxf.dosgi.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.cxf.dosgi.discovery.zookeeper.server", null).update(svrProps);
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestExportRestService.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestExportRestService.java
new file mode 100644
index 0000000..3330c00
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestExportRestService.java
@@ -0,0 +1,93 @@
+/**
+ * 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.systests2.multi;
+
+import java.io.InputStream;
+
+import javax.inject.Inject;
+
+import org.apache.cxf.dosgi.systests2.multi.rest.RestTranslate;
+import org.apache.cxf.dosgi.systests2.multi.rest.RestTranslateImpl;
+import org.apache.cxf.dosgi.systests2.multi.rest.TranslateActivator;
+import org.apache.cxf.jaxrs.client.WebClient;
+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.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+import static org.ops4j.pax.exam.CoreOptions.frameworkStartLevel;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.provision;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+@RunWith(PaxExam.class)
+public class TestExportRestService extends AbstractDosgiTest {
+
+ @Inject
+ BundleContext bundleContext;
+
+ String webPort = "9091";
+
+ @Configuration
+ public Option[] configure() throws Exception {
+ return new Option[] {
+ MultiBundleTools.getDistroWithDiscovery(),
+ systemProperty("org.osgi.service.http.port").value(webPort),
+ systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"),
+ mavenBundle().groupId("org.apache.cxf.dosgi.samples")
+ .artifactId("cxf-dosgi-ri-samples-greeter-interface").versionAsInProject(),
+ CoreOptions.junitBundles(),
+ provision(getServiceBundle()),
+ frameworkStartLevel(100),
+ //CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005")
+ };
+ }
+
+ private InputStream getServiceBundle() {
+ return TinyBundles.bundle()
+ .add(RestTranslate.class)
+ .add(RestTranslateImpl.class)
+ .add(TranslateActivator.class)
+ .set(Constants.BUNDLE_SYMBOLICNAME, "RestTranslate")
+ .set(Constants.BUNDLE_ACTIVATOR, TranslateActivator.class.getName())
+ .build(TinyBundles.withBnd());
+ }
+
+ /**
+ * FIXME This test fails..
+ */
+ @Test
+ public void testEndpointAvailable() throws Exception {
+ waitWebPage("http://localhost:" + webPort + "/cxf/translate");
+ try {
+ WebClient client = WebClient.create("http://localhost:" + webPort + "/cxf/translate/hello");
+ String result = client.get(String.class);
+ Assert.assertEquals("hallo", result);
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException(e.getMessage());
+ }
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestExportService.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestExportService.java
new file mode 100644
index 0000000..77248bc
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestExportService.java
@@ -0,0 +1,141 @@
+/**
+ * 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.systests2.multi;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+import org.apache.cxf.dosgi.samples.greeter.GreeterData;
+import org.apache.cxf.dosgi.samples.greeter.GreeterException;
+import org.apache.cxf.dosgi.samples.greeter.GreeterService;
+import org.apache.cxf.dosgi.samples.greeter.GreetingPhrase;
+import org.apache.cxf.frontend.ClientProxyFactoryBean;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.BundleContext;
+
+import static org.ops4j.pax.exam.CoreOptions.frameworkStartLevel;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+@RunWith(PaxExam.class)
+public class TestExportService extends AbstractDosgiTest {
+
+ @Inject
+ BundleContext bundleContext;
+
+ @Configuration
+ public static Option[] configure() throws Exception {
+ return new Option[] {
+ MultiBundleTools.getDistroWithDiscovery(),
+ systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"),
+ mavenBundle().groupId("org.apache.servicemix.bundles")
+ .artifactId("org.apache.servicemix.bundles.junit").version("4.9_2"),
+ mavenBundle().groupId("org.apache.cxf.dosgi.samples")
+ .artifactId("cxf-dosgi-ri-samples-greeter-interface").versionAsInProject(),
+ mavenBundle().groupId("org.apache.cxf.dosgi.samples")
+ .artifactId("cxf-dosgi-ri-samples-greeter-impl").versionAsInProject(),
+ mavenBundle().groupId("org.apache.cxf.dosgi.systests")
+ .artifactId("cxf-dosgi-ri-systests2-common").versionAsInProject(), frameworkStartLevel(100),
+ //CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005")
+ };
+ }
+
+ @Test
+ public void testAccessEndpoint() throws Exception {
+ waitPort(9090);
+
+ checkWsdl(new URL("http://localhost:9090/greeter?wsdl"));
+
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(ClientProxyFactoryBean.class.getClassLoader());
+ try {
+ checkServiceCall("http://localhost:9090/greeter");
+ } finally {
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
+
+ private void checkServiceCall(String serviceUri) {
+ GreeterService client = createGreeterServiceProxy(serviceUri);
+
+ Map<GreetingPhrase, String> greetings = client.greetMe("Fred");
+ Assert.assertEquals("Fred", greetings.get(new GreetingPhrase("Hello")));
+ System.out.println("Invocation result: " + greetings);
+
+ try {
+ GreeterData gd = new GreeterDataImpl("Stranger", 11, true);
+ client.greetMe(gd);
+ Assert.fail("GreeterException has to be thrown");
+ } catch (GreeterException ex) {
+ Assert.assertEquals("Wrong exception message", "GreeterService can not greet Stranger",
+ ex.toString());
+ }
+ }
+
+ private void checkWsdl(URL wsdlURL) throws ParserConfigurationException, SAXException, IOException {
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware(true);
+ dbf.setValidating(false);
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ Document doc = db.parse(wsdlURL.openStream());
+ Element el = doc.getDocumentElement();
+ Assert.assertEquals("definitions", el.getLocalName());
+ Assert.assertEquals("http://schemas.xmlsoap.org/wsdl/", el.getNamespaceURI());
+ Assert.assertEquals("GreeterService", el.getAttribute("name"));
+ }
+
+ class GreeterDataImpl implements GreeterData {
+
+ private String name;
+ private int age;
+ private boolean exception;
+
+ GreeterDataImpl(String n, int a, boolean ex) {
+ name = n;
+ age = a;
+ exception = ex;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getAge() {
+ return age;
+ }
+
+ public boolean isException() {
+ return exception;
+ }
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestImportService.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestImportService.java
new file mode 100644
index 0000000..0f8b0b8
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/TestImportService.java
@@ -0,0 +1,154 @@
+/**
+ * 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.systests2.multi;
+
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.apache.cxf.aegis.databinding.AegisDatabinding;
+import org.apache.cxf.dosgi.samples.greeter.GreeterData;
+import org.apache.cxf.dosgi.samples.greeter.GreeterException;
+import org.apache.cxf.dosgi.samples.greeter.GreeterService;
+import org.apache.cxf.dosgi.samples.greeter.GreetingPhrase;
+import org.apache.cxf.dosgi.systests2.common.test1.GreeterDataImpl;
+import org.apache.cxf.dosgi.systests2.common.test1.MyActivator;
+import org.apache.cxf.dosgi.systests2.common.test1.MyServiceTracker;
+import org.apache.cxf.dosgi.systests2.common.test1.StartServiceTracker;
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.frontend.ServerFactoryBean;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+
+import static org.ops4j.pax.exam.CoreOptions.frameworkStartLevel;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.provision;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+
+@RunWith(PaxExam.class)
+public class TestImportService extends AbstractDosgiTest {
+
+ @Inject
+ BundleContext bundleContext;
+
+ @Configuration
+ public static Option[] configure() throws Exception {
+ return new Option[] {
+ MultiBundleTools.getDistroWithDiscovery(),
+ systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("INFO"),
+ mavenBundle().groupId("org.apache.cxf.dosgi.samples")
+ .artifactId("cxf-dosgi-ri-samples-greeter-interface").versionAsInProject(),
+ mavenBundle().groupId("org.apache.servicemix.bundles")
+ .artifactId("org.apache.servicemix.bundles.junit").version("4.9_2"),
+ mavenBundle().groupId("org.apache.cxf.dosgi.systests")
+ .artifactId("cxf-dosgi-ri-systests2-common").versionAsInProject(),
+ provision(createServiceConsumerBundle()),
+ // increase for debugging
+ systemProperty("org.apache.cxf.dosgi.test.serviceWaitTimeout").value(
+ System.getProperty("org.apache.cxf.dosgi.test.serviceWaitTimeout", "20")),
+ frameworkStartLevel(100),
+ //CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005")
+ };
+ }
+
+ protected static InputStream createServiceConsumerBundle() {
+ return TinyBundles.bundle()
+ .add(MyActivator.class)
+ .add(MyServiceTracker.class)
+ .add(StartServiceTracker.class)
+ .add(GreeterDataImpl.class)
+ .add("OSGI-INF/remote-service/remote-services.xml", TestImportService.class.getResource("/rs-test1.xml"))
+ .set(Constants.BUNDLE_SYMBOLICNAME, "testClientBundle")
+ .set(Constants.EXPORT_PACKAGE, "org.apache.cxf.dosgi.systests2.common.test1")
+ .set(Constants.BUNDLE_ACTIVATOR, MyActivator.class.getName())
+ .build(TinyBundles.withBnd());
+ }
+
+ @Test
+ public void testClientConsumer() throws Exception {
+ // This test tests the consumer side of Distributed OSGi. It works as follows:
+ // 1. It creates a little test bundle on the fly and starts that in the framework
+ // (this happens in the configure() method above). The test bundle waits until its
+ // instructed to start doing stuff. It's give this instruction via a service that is
+ // registered by this test (the service is of type java.lang.Object and has testName=test1).
+ // 2. The test manually creates a CXF server of the appropriate type (using ServerFactoryBean)
+ // 3. It signals the client bundle by registering a service to start doing its work.
+ // This registers a ServiceTracker in the client bundle for the remote service that is created
+ // by the test in step 2. The client bundle knows about the address through the
+ // remote-services.xml file.
+ // 4. The client bundle will invoke the remote service and record the results in a service that it
+ // registers in the Service Registry.
+ // 5. The test waits for this service to appear and then checks the results which are available as
+ // a service property.
+
+ // Set up a Server in the test
+ ServerFactoryBean factory = new ServerFactoryBean();
+ factory.setServiceClass(GreeterService.class);
+ factory.setAddress("http://localhost:9191/grrr");
+ factory.getServiceFactory().setDataBinding(new AegisDatabinding());
+ factory.setServiceBean(new TestGreeter());
+
+ Server server = null;
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(ServerFactoryBean.class.getClassLoader());
+ server = factory.create();
+
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put("testName", "test1");
+ bundleContext.registerService(Object.class.getName(), new Object(), props);
+
+ // Wait for the service tracker in the test bundle to register a service with the test result
+ @SuppressWarnings("rawtypes")
+ ServiceReference ref = waitService(bundleContext, String.class, "(testResult=test1)", 20);
+ Assert.assertEquals("HiOSGi;exception", ref.getProperty("result"));
+ } finally {
+ if (server != null) {
+ server.stop();
+ }
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+ }
+
+ public static class TestGreeter implements GreeterService {
+
+ public Map<GreetingPhrase, String> greetMe(String name) {
+ Map<GreetingPhrase, String> m = new HashMap<GreetingPhrase, String>();
+ GreetingPhrase gp = new GreetingPhrase("Hi");
+ m.put(gp, name);
+ return m;
+ }
+
+ public GreetingPhrase[] greetMe(GreeterData gd) throws GreeterException {
+ throw new GreeterException("TestGreeter");
+ }
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/AddGreetingPhraseInterceptor.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/AddGreetingPhraseInterceptor.java
new file mode 100644
index 0000000..a3a19be
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/AddGreetingPhraseInterceptor.java
@@ -0,0 +1,44 @@
+/**
+ * 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.systests2.multi.customintent;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.samples.greeter.GreetingPhrase;
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.interceptor.Fault;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageContentsList;
+import org.apache.cxf.phase.AbstractPhaseInterceptor;
+
+public final class AddGreetingPhraseInterceptor extends AbstractPhaseInterceptor<Message> {
+
+ AddGreetingPhraseInterceptor(String phase) {
+ super(phase);
+ }
+
+ public void handleMessage(Message message) throws Fault {
+ MessageContentsList contents = (MessageContentsList) message.getContent(List.class);
+ Map<GreetingPhrase, String> result = CastUtils.cast((Map<?, ?>)contents.get(0));
+ result.put(new GreetingPhrase("Hi from custom intent"), "customintent");
+ //Object content1 = contents.iterator().next();
+ System.out.println(message);
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/CustomFeature.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/CustomFeature.java
new file mode 100644
index 0000000..abac14f
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/CustomFeature.java
@@ -0,0 +1,33 @@
+/**
+ * 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.systests2.multi.customintent;
+
+import org.apache.cxf.Bus;
+import org.apache.cxf.feature.AbstractFeature;
+import org.apache.cxf.interceptor.InterceptorProvider;
+import org.apache.cxf.phase.Phase;
+
+public final class CustomFeature extends AbstractFeature {
+
+ @Override
+ protected void initializeProvider(InterceptorProvider provider, Bus bus) {
+ provider.getOutInterceptors().add(0, new AddGreetingPhraseInterceptor(Phase.USER_LOGICAL));
+ super.initializeProvider(provider, bus);
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/CustomIntentActivator.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/CustomIntentActivator.java
new file mode 100644
index 0000000..ca4efda
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/CustomIntentActivator.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.systests2.multi.customintent;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class CustomIntentActivator implements BundleActivator {
+
+ public void start(BundleContext context) throws Exception {
+ Dictionary<String, String> props = new Hashtable<String, String>();
+ props.put("org.apache.cxf.dosgi.IntentName", "myIntent");
+ context.registerService(Object.class.getName(), new CustomFeature(), props);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/service/EmptyGreeterService.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/service/EmptyGreeterService.java
new file mode 100644
index 0000000..2c0108d
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/service/EmptyGreeterService.java
@@ -0,0 +1,41 @@
+/**
+ * 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.systests2.multi.customintent.service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cxf.dosgi.samples.greeter.GreeterData;
+import org.apache.cxf.dosgi.samples.greeter.GreeterException;
+import org.apache.cxf.dosgi.samples.greeter.GreeterService;
+import org.apache.cxf.dosgi.samples.greeter.GreetingPhrase;
+
+public final class EmptyGreeterService implements GreeterService {
+
+ /**
+ * Return an empty array. Our custom intent should add a GreetingPhrase
+ */
+ public GreetingPhrase[] greetMe(GreeterData name) throws GreeterException {
+ return new GreetingPhrase[]{};
+ }
+
+ public Map<GreetingPhrase, String> greetMe(String name) {
+ return new HashMap<GreetingPhrase, String>();
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/service/GreeterServiceWithCustomIntentActivator.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/service/GreeterServiceWithCustomIntentActivator.java
new file mode 100644
index 0000000..bcf6016
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/customintent/service/GreeterServiceWithCustomIntentActivator.java
@@ -0,0 +1,42 @@
+/**
+ * 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.systests2.multi.customintent.service;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.cxf.dosgi.samples.greeter.GreeterService;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.remoteserviceadmin.RemoteConstants;
+
+public class GreeterServiceWithCustomIntentActivator implements BundleActivator {
+
+ public void start(BundleContext context) throws Exception {
+ Dictionary<String, String> props = new Hashtable<String, String>();
+ props.put(RemoteConstants.SERVICE_EXPORTED_INTERFACES, "*");
+ props.put(RemoteConstants.SERVICE_EXPORTED_CONFIGS, "org.apache.cxf.ws");
+ props.put("org.apache.cxf.ws.address", "http://localhost:9090/greeter");
+ props.put(RemoteConstants.SERVICE_EXPORTED_INTENTS, "myIntent");
+ context.registerService(GreeterService.class.getName(), new EmptyGreeterService(), props);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/rest/RestTranslate.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/rest/RestTranslate.java
new file mode 100644
index 0000000..3a55c0e
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/rest/RestTranslate.java
@@ -0,0 +1,34 @@
+/**
+ * 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.systests2.multi.rest;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+
+public interface RestTranslate {
+
+ @GET
+ String englishWords();
+
+ @GET
+ @Path("/{word}")
+ String getTranslation(@PathParam("word") String word);
+
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/rest/RestTranslateImpl.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/rest/RestTranslateImpl.java
new file mode 100644
index 0000000..640b0c9
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/rest/RestTranslateImpl.java
@@ -0,0 +1,41 @@
+/**
+ * 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.systests2.multi.rest;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class RestTranslateImpl implements RestTranslate {
+ Map<String, String> translation;
+
+ public RestTranslateImpl() {
+ translation = new HashMap<String, String>();
+ translation.put("hello", "hallo");
+ }
+
+ @Override
+ public String englishWords() {
+ return translation.keySet().toString();
+ }
+
+ @Override
+ public String getTranslation(String word) {
+ return translation.get(word);
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/rest/TranslateActivator.java b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/rest/TranslateActivator.java
new file mode 100644
index 0000000..1c48fb5
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/java/org/apache/cxf/dosgi/systests2/multi/rest/TranslateActivator.java
@@ -0,0 +1,40 @@
+/**
+ * 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.systests2.multi.rest;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+public class TranslateActivator implements BundleActivator {
+
+ public void start(BundleContext context) throws Exception {
+ Dictionary<String, String> props = new Hashtable<String, String>();
+ props.put("service.exported.interfaces", "*");
+ props.put("service.exported.configs", "org.apache.cxf.rs");
+ props.put("service.exported.intents", "HTTP");
+ props.put("org.apache.cxf.rs.address", "/translate");
+ context.registerService(RestTranslate.class.getName(), new RestTranslateImpl(), props);
+ }
+
+ public void stop(BundleContext context) throws Exception {
+ }
+}
diff --git a/trunk/systests2/multi-bundle/src/test/resources/log4j.properties b/trunk/systests2/multi-bundle/src/test/resources/log4j.properties
new file mode 100644
index 0000000..2f206d5
--- /dev/null
+++ b/trunk/systests2/multi-bundle/src/test/resources/log4j.properties
@@ -0,0 +1,13 @@
+# Set root logger level to DEBUG and its only appender to A1.
+log4j.rootLogger=INFO, A1
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.A1=org.apache.log4j.ConsoleAppender
+
+# A1 uses PatternLayout.
+log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
+
+log4j.logger.org.ops4j.pax.scanner=WARN
+log4j.logger.org.ops4j.pax.runner=WARN
+log4j.logger.org.ops4j.pax.url=WARN
diff --git a/trunk/systests2/pom.xml b/trunk/systests2/pom.xml
new file mode 100644
index 0000000..c357058
--- /dev/null
+++ b/trunk/systests2/pom.xml
@@ -0,0 +1,42 @@
+<?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.cxf.dosgi</groupId>
+ <artifactId>cxf-dosgi-ri-parent</artifactId>
+ <version>1.6.0</version>
+ <relativePath>../parent/pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.apache.cxf.dosgi.systests</groupId>
+ <artifactId>cxf-dosgi-ri-systests2</artifactId>
+ <version>1.6.0</version>
+ <packaging>pom</packaging>
+
+ <name>Distributed OSGi System Tests</name>
+
+ <modules>
+ <module>common</module>
+ <module>multi-bundle</module>
+ </modules>
+</project>
diff --git a/trunk/tck/apply-to-tck.sh.template b/trunk/tck/apply-to-tck.sh.template
new file mode 100755
index 0000000..b6d4455
--- /dev/null
+++ b/trunk/tck/apply-to-tck.sh.template
@@ -0,0 +1,15 @@
+#This script pretends the current build to be the same as the version in the R5 TCK repo.
+#This puts the wrong version numbers on the files copied to the repo, so it could be improved
+#but it keeps changes to a minimum.
+#
+#Run this script before executing the CT tests to get the CT tests to run with the current CXF DOSGi build
+
+TCK_HOME=... the osgi build clone root directory...
+ZOOKEEPER_JAR=... the zookeeper server jar, can be obtained from a zookeeper download...
+cp $ZOOKEEPER_JAR $TCK_HOME/licensed/repo/org.apache.cxf.dosgi.discovery.zookeeper/org.apache.cxf.dosgi.discovery.zookeeper-1.2.0.jar
+
+cp org.osgi.impl.service.remoteserviceadmin-4.3.0.lib $TCK_HOME/cnf/repo/org.osgi.impl.service.remoteserviceadmin
+
+cp ../distribution/single-bundle/target/cxf-dosgi-ri-singlebundle-distribution-1.5-SNAPSHOT.jar $TCK_HOME/licensed/repo/org.apache.cxf.dosgi.singlebundle/org.apache.cxf.dosgi.singlebundle-1.3.1.jar
+cp ../discovery/distributed/zookeeper-server/target/cxf-dosgi-ri-discovery-distributed-zookeeper-server-1.5-SNAPSHOT.jar $TCK_HOME/licensed/repo/org.apache.cxf.dosgi.discovery.server/org.apache.cxf.dosgi.discovery.server-1.2.0.jar
+cp ../discovery/distributed/zookeeper-server-config/target/cxf-dosgi-ri-discovery-distributed-zookeeper-server-config-1.5-SNAPSHOT.jar $TCK_HOME/licensed/repo/org.apache.cxf.dosgi.discovery.server.config/org.apache.cxf.dosgi.discovery.server.config-1.2.0.jar
diff --git a/trunk/tck/org.osgi.impl.service.remoteserviceadmin-4.3.0.lib b/trunk/tck/org.osgi.impl.service.remoteserviceadmin-4.3.0.lib
new file mode 100644
index 0000000..66a532c
--- /dev/null
+++ b/trunk/tck/org.osgi.impl.service.remoteserviceadmin-4.3.0.lib
@@ -0,0 +1,5 @@
+org.apache.cxf.dosgi.singlebundle; version=1.3.1
+com.springsource.org.apache.log4j; version=1.2.15
+slf4j.api; version=1.6.1
+slf4j.nop; version=1.6.1
+