You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2018/05/27 18:52:40 UTC

[32/60] [abbrv] [partial] celix git commit: CELIX-424: Cleans up the directory structure. Moves all libraries to the libs subdir and all bundles to the bundles subdir

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_common/src/endpoint_discovery_server.c
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_common/src/endpoint_discovery_server.c b/bundles/remote_services/discovery_common/src/endpoint_discovery_server.c
new file mode 100644
index 0000000..7620bbf
--- /dev/null
+++ b/bundles/remote_services/discovery_common/src/endpoint_discovery_server.c
@@ -0,0 +1,454 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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_discovery_server.c
+ *
+ * \date		Aug 12, 2014
+ * \author		<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ * \copyright	Apache License, Version 2.0
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#ifndef ANDROID
+#include <ifaddrs.h>
+#endif
+#include "civetweb.h"
+#include "celix_errno.h"
+#include "utils.h"
+#include "log_helper.h"
+#include "discovery.h"
+#include "endpoint_descriptor_writer.h"
+
+// defines how often the webserver is restarted (with an increased port number)
+#define MAX_NUMBER_OF_RESTARTS 	15
+#define DEFAULT_SERVER_THREADS "1"
+
+#define CIVETWEB_REQUEST_NOT_HANDLED 0
+#define CIVETWEB_REQUEST_HANDLED 1
+
+static const char *response_headers =
+		"HTTP/1.1 200 OK\r\n"
+		"Cache: no-cache\r\n"
+		"Content-Type: application/xml;charset=utf-8\r\n"
+		"\r\n";
+
+struct endpoint_discovery_server {
+	log_helper_pt* loghelper;
+	hash_map_pt entries; // key = endpointId, value = endpoint_descriptor_pt
+
+	celix_thread_mutex_t serverLock;
+
+	const char* path;
+	const char *port;
+	const char* ip;
+	struct mg_context* ctx;
+};
+
+// Forward declarations...
+static int endpointDiscoveryServer_callback(struct mg_connection *conn);
+static char* format_path(const char* path);
+
+#ifndef ANDROID
+static celix_status_t endpointDiscoveryServer_getIpAdress(char* interface, char** ip);
+#endif
+
+celix_status_t endpointDiscoveryServer_create(
+		discovery_pt discovery,
+		bundle_context_pt context,
+		const char* defaultServerPath,
+		const char* defaultServerPort,
+		const char* defaultServerIp,
+		endpoint_discovery_server_pt *server) {
+	celix_status_t status;
+
+	const char *port = NULL;
+	const char *ip = NULL;
+	char *detectedIp = NULL;
+	const char *path = NULL;
+	const char *retries = NULL;
+
+	int max_ep_num = MAX_NUMBER_OF_RESTARTS;
+
+	*server = malloc(sizeof(struct endpoint_discovery_server));
+	if (!*server) {
+		return CELIX_ENOMEM;
+	}
+
+	(*server)->loghelper = &discovery->loghelper;
+	(*server)->entries = hashMap_create(&utils_stringHash, NULL, &utils_stringEquals, NULL);
+	if (!(*server)->entries) {
+		return CELIX_ENOMEM;
+	}
+
+	status = celixThreadMutex_create(&(*server)->serverLock, NULL);
+	if (status != CELIX_SUCCESS) {
+		return CELIX_BUNDLE_EXCEPTION;
+	}
+
+	bundleContext_getProperty(context, DISCOVERY_SERVER_IP, &ip);
+#ifndef ANDROID
+	if (ip == NULL) {
+		const char *interface = NULL;
+
+		bundleContext_getProperty(context, DISCOVERY_SERVER_INTERFACE, &interface);
+		if ((interface != NULL) && (endpointDiscoveryServer_getIpAdress((char*)interface, &detectedIp) != CELIX_SUCCESS)) {
+			logHelper_log(*(*server)->loghelper, OSGI_LOGSERVICE_WARNING, "Could not retrieve IP adress for interface %s", interface);
+		}
+
+		if (detectedIp == NULL) {
+			endpointDiscoveryServer_getIpAdress(NULL, &detectedIp);
+		}
+
+		ip = detectedIp;
+	}
+#endif
+
+	if (ip != NULL) {
+		logHelper_log(*(*server)->loghelper, OSGI_LOGSERVICE_INFO, "Using %s for service annunciation", ip);
+		(*server)->ip = strdup(ip);
+	}
+	else {
+		logHelper_log(*(*server)->loghelper, OSGI_LOGSERVICE_WARNING, "No IP address for service annunciation set. Using %s", defaultServerIp);
+		(*server)->ip = strdup((char*) defaultServerIp);
+	}
+
+	if (detectedIp != NULL) {
+		free(detectedIp);
+	}
+
+	bundleContext_getProperty(context, DISCOVERY_SERVER_PORT, &port);
+	if (port == NULL) {
+		port = defaultServerPort;
+	}
+
+	bundleContext_getProperty(context, DISCOVERY_SERVER_PATH, &path);
+	if (path == NULL) {
+		path = defaultServerPath;
+	}
+
+	bundleContext_getProperty(context, DISCOVERY_SERVER_MAX_EP, &retries);
+	if (retries != NULL) {
+		errno=0;
+		max_ep_num = strtol(retries,NULL,10);
+		if(errno!=0 || max_ep_num<=0){
+			max_ep_num=MAX_NUMBER_OF_RESTARTS;
+		}
+	}
+
+	(*server)->path = format_path(path);
+
+	const struct mg_callbacks callbacks = {
+			.begin_request = endpointDiscoveryServer_callback,
+	};
+
+	unsigned int port_counter = 0;
+	char newPort[10];
+
+	do {
+		const char *options[] = {
+				"listening_ports", port,
+				"num_threads", DEFAULT_SERVER_THREADS,
+				NULL
+		};
+
+		(*server)->ctx = mg_start(&callbacks, (*server), options);
+
+		if ((*server)->ctx != NULL)
+		{
+			logHelper_log(discovery->loghelper, OSGI_LOGSERVICE_INFO, "Starting discovery server on port %s...", port);
+		}
+		else {
+			errno = 0;
+			char* endptr = (char*)port;
+			long currentPort = strtol(port, &endptr, 10);
+
+			if (*endptr || errno != 0) {
+				currentPort = strtol(defaultServerPort, NULL, 10);
+			}
+
+			port_counter++;
+			snprintf(&newPort[0], 10,  "%ld", (currentPort+1));
+
+			logHelper_log(discovery->loghelper, OSGI_LOGSERVICE_ERROR, "Error while starting discovery server on port %s - retrying on port %s...", port, newPort);
+			port = newPort;
+
+		}
+
+	} while(((*server)->ctx == NULL) && (port_counter < max_ep_num));
+
+	(*server)->port = strdup(port);
+
+	return status;
+}
+
+celix_status_t endpointDiscoveryServer_getUrl(endpoint_discovery_server_pt server, char* url)
+{
+	celix_status_t status = CELIX_BUNDLE_EXCEPTION;
+
+	if (server->ip && server->port && server->path) {
+		sprintf(url, "http://%s:%s/%s", server->ip, server->port, server->path);
+		status = CELIX_SUCCESS;
+	}
+
+	return status;
+}
+
+celix_status_t endpointDiscoveryServer_destroy(endpoint_discovery_server_pt server) {
+	celix_status_t status;
+
+	// stop & block until the actual server is shut down...
+	if (server->ctx != NULL) {
+		mg_stop(server->ctx);
+		server->ctx = NULL;
+	}
+
+	status = celixThreadMutex_lock(&server->serverLock);
+
+	hashMap_destroy(server->entries, true /* freeKeys */, false /* freeValues */);
+
+	status = celixThreadMutex_unlock(&server->serverLock);
+	status = celixThreadMutex_destroy(&server->serverLock);
+
+	free((void*) server->path);
+	free((void*) server->port);
+	free((void*) server->ip);
+
+	free(server);
+
+	return status;
+}
+
+celix_status_t endpointDiscoveryServer_addEndpoint(endpoint_discovery_server_pt server, endpoint_description_pt endpoint) {
+	celix_status_t status;
+
+	status = celixThreadMutex_lock(&server->serverLock);
+	if (status != CELIX_SUCCESS) {
+		return CELIX_BUNDLE_EXCEPTION;
+	}
+
+	// create a local copy of the endpointId which we can control...
+	char* endpointId = strdup(endpoint->id);
+	endpoint_description_pt cur_value = hashMap_get(server->entries, endpointId);
+	if (!cur_value) {
+		logHelper_log(*server->loghelper, OSGI_LOGSERVICE_INFO, "exposing new endpoint \"%s\"...", endpointId);
+
+		hashMap_put(server->entries, endpointId, endpoint);
+	}
+
+	status = celixThreadMutex_unlock(&server->serverLock);
+	if (status != CELIX_SUCCESS) {
+		return CELIX_BUNDLE_EXCEPTION;
+	}
+
+	return status;
+}
+
+celix_status_t endpointDiscoveryServer_removeEndpoint(endpoint_discovery_server_pt server, endpoint_description_pt endpoint) {
+	celix_status_t status;
+
+	status = celixThreadMutex_lock(&server->serverLock);
+	if (status != CELIX_SUCCESS) {
+		return CELIX_BUNDLE_EXCEPTION;
+	}
+
+	hash_map_entry_pt entry = hashMap_getEntry(server->entries, endpoint->id);
+	if (entry) {
+		char* key = hashMapEntry_getKey(entry);
+
+		logHelper_log(*server->loghelper, OSGI_LOGSERVICE_INFO, "removing endpoint \"%s\"...\n", key);
+
+		hashMap_remove(server->entries, key);
+
+		// we've made this key, see _addEnpoint above...
+		free((void*) key);
+	}
+
+	status = celixThreadMutex_unlock(&server->serverLock);
+	if (status != CELIX_SUCCESS) {
+		return CELIX_BUNDLE_EXCEPTION;
+	}
+
+	return status;
+}
+
+static char* format_path(const char* path) {
+	char* result = strdup(path);
+	result = utils_stringTrim(result);
+	// check whether the path starts with a leading slash...
+	if (result[0] != '/') {
+		size_t len = strlen(result);
+		result = realloc(result, len + 2);
+		memmove(result + 1, result, len);
+		result[0] = '/';
+		result[len + 1] = 0;
+	}
+	return result;
+}
+
+static celix_status_t endpointDiscoveryServer_getEndpoints(endpoint_discovery_server_pt server, const char* the_endpoint_id, array_list_pt *endpoints) {
+	celix_status_t status;
+
+	status = arrayList_create(endpoints);
+	if (status != CELIX_SUCCESS) {
+		return CELIX_ENOMEM;
+	}
+
+
+	hash_map_iterator_pt iter = hashMapIterator_create(server->entries);
+	while (hashMapIterator_hasNext(iter)) {
+		hash_map_entry_pt entry = hashMapIterator_nextEntry(iter);
+
+		char* endpoint_id = hashMapEntry_getKey(entry);
+		if (the_endpoint_id == NULL || strcmp(the_endpoint_id, endpoint_id) == 0) {
+			endpoint_description_pt endpoint = hashMapEntry_getValue(entry);
+
+			arrayList_add(*endpoints, endpoint);
+		}
+	}
+	hashMapIterator_destroy(iter);
+
+	return status;
+}
+
+static int endpointDiscoveryServer_writeEndpoints(struct mg_connection* conn, array_list_pt endpoints) {
+	celix_status_t status;
+	int rv = CIVETWEB_REQUEST_NOT_HANDLED;
+
+	endpoint_descriptor_writer_pt writer = NULL;
+	status = endpointDescriptorWriter_create(&writer);
+	if (status == CELIX_SUCCESS) {
+
+		char *buffer = NULL;
+		status = endpointDescriptorWriter_writeDocument(writer, endpoints, &buffer);
+		if (buffer) {
+			mg_write(conn, response_headers, strlen(response_headers));
+			mg_write(conn, buffer, strlen(buffer));
+		}
+
+		rv = CIVETWEB_REQUEST_HANDLED;
+	}
+
+	if(writer!=NULL){
+		endpointDescriptorWriter_destroy(writer);
+	}
+
+	return rv;
+}
+
+// returns all endpoints as XML...
+static int endpointDiscoveryServer_returnAllEndpoints(endpoint_discovery_server_pt server, struct mg_connection* conn) {
+	int status = CIVETWEB_REQUEST_NOT_HANDLED;
+
+	array_list_pt endpoints = NULL;
+
+	if (celixThreadMutex_lock(&server->serverLock) == CELIX_SUCCESS) {
+		endpointDiscoveryServer_getEndpoints(server, NULL, &endpoints);
+		if (endpoints) {
+			status = endpointDiscoveryServer_writeEndpoints(conn, endpoints);
+
+			arrayList_destroy(endpoints);
+		}
+
+
+		celixThreadMutex_unlock(&server->serverLock);
+	}
+
+	return status;
+}
+
+// returns a single endpoint as XML...
+static int endpointDiscoveryServer_returnEndpoint(endpoint_discovery_server_pt server, struct mg_connection* conn, const char* endpoint_id) {
+	int status = CIVETWEB_REQUEST_NOT_HANDLED;
+
+	array_list_pt endpoints = NULL;
+
+	if (celixThreadMutex_lock(&server->serverLock) == CELIX_SUCCESS) {
+		endpointDiscoveryServer_getEndpoints(server, endpoint_id, &endpoints);
+		if (endpoints) {
+			status = endpointDiscoveryServer_writeEndpoints(conn, endpoints);
+
+			arrayList_destroy(endpoints);
+		}
+
+		celixThreadMutex_unlock(&server->serverLock);
+	}
+
+	return status;
+}
+
+static int endpointDiscoveryServer_callback(struct mg_connection* conn) {
+	int status = CIVETWEB_REQUEST_NOT_HANDLED;
+
+	const struct mg_request_info *request_info = mg_get_request_info(conn);
+	if (request_info->uri != NULL && strcmp("GET", request_info->request_method) == 0) {
+		endpoint_discovery_server_pt server = request_info->user_data;
+
+		const char *uri = request_info->uri;
+		const size_t path_len = strlen(server->path);
+		const size_t uri_len = strlen(uri);
+
+		if (strncmp(server->path, uri, strlen(server->path)) == 0) {
+			// Be lenient when it comes to the trailing slash...
+			if (path_len == uri_len || (uri_len == (path_len + 1) && uri[path_len] == '/')) {
+				status = endpointDiscoveryServer_returnAllEndpoints(server, conn);
+			} else {
+				const char* endpoint_id = uri + path_len + 1; // right after the slash...
+
+				status = endpointDiscoveryServer_returnEndpoint(server, conn, endpoint_id);
+			}
+		}
+	}
+
+	return status;
+}
+
+#ifndef ANDROID
+static celix_status_t endpointDiscoveryServer_getIpAdress(char* interface, char** ip) {
+	celix_status_t status = CELIX_BUNDLE_EXCEPTION;
+
+	struct ifaddrs *ifaddr, *ifa;
+	char host[NI_MAXHOST];
+
+	if (getifaddrs(&ifaddr) != -1)
+	{
+		for (ifa = ifaddr; ifa != NULL && status != CELIX_SUCCESS; ifa = ifa->ifa_next)
+		{
+			if (ifa->ifa_addr == NULL)
+				continue;
+
+			if ((getnameinfo(ifa->ifa_addr,sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0) && (ifa->ifa_addr->sa_family == AF_INET)) {
+				if (interface == NULL) {
+					*ip = strdup(host);
+					status = CELIX_SUCCESS;
+				}
+				else if (strcmp(ifa->ifa_name, interface) == 0) {
+					*ip = strdup(host);
+					status = CELIX_SUCCESS;
+				}
+			}
+		}
+
+		freeifaddrs(ifaddr);
+	}
+
+	return status;
+}
+#endif

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_configured/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_configured/CMakeLists.txt b/bundles/remote_services/discovery_configured/CMakeLists.txt
new file mode 100644
index 0000000..089d1bd
--- /dev/null
+++ b/bundles/remote_services/discovery_configured/CMakeLists.txt
@@ -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.
+celix_subproject(RSA_DISCOVERY_CONFIGURED "Option to enable building the Discovery (Configured) bundle" ON)
+if (RSA_DISCOVERY_CONFIGURED)
+    find_package(CURL REQUIRED)
+    find_package(LibXml2 REQUIRED)
+
+    add_celix_bundle(rsa_discovery_configured
+        VERSION 0.9.0
+        SYMBOLIC_NAME "apache_celix_rsa_discovery_configured"
+        NAME "Apache Celix RSA Configured Discovery"
+        SOURCES
+	        src/discovery_impl.c
+            $<TARGET_OBJECTS:Celix::rsa_discovery_common>
+            $<TARGET_OBJECTS:Celix::civetweb>
+    )
+    target_include_directories(rsa_discovery_configured PRIVATE
+            src
+            $<TARGET_PROPERTY:Celix::rsa_discovery_common,INCLUDE_DIRECTORIES>
+            $<TARGET_PROPERTY:Celix::civetweb,INCLUDE_DIRECTORIES>
+    )
+    target_link_libraries(rsa_discovery_configured PRIVATE ${CURL_LIBRARIES} ${LIBXML2_LIBRARIES} Celix::log_helper Celix::rsa_common)
+
+    install_celix_bundle(rsa_discovery_configured EXPORT celix COMPONENT rsa)
+    #Setup target aliases to match external usage
+    add_library(Celix::rsa_discovery_configured ALIAS rsa_discovery_configured)
+endif (RSA_DISCOVERY_CONFIGURED)

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_configured/src/desc.xml
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_configured/src/desc.xml b/bundles/remote_services/discovery_configured/src/desc.xml
new file mode 100644
index 0000000..5998992
--- /dev/null
+++ b/bundles/remote_services/discovery_configured/src/desc.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.
+ -->
+<endpoint-descriptions xmlns="http://www.osgi.org/xmlns/rsa/v1.0.0">
+	<endpoint-description>
+		<property name="service.intents">
+			<list>
+				<value>SOAP</value>
+				<value>HTTP</value>
+			</list>
+		</property>
+		<property name="endpoint.id" value="http://ws.acme.com:9000/hello" />
+		<property name="objectClass" value="com.acme.Foo" />
+		<property name="endpoint.package.version.com.acme" value="4.2" />
+		<property name="service.imported.configs" value="com.acme" />
+		<property name="com.acme.ws.xml">
+			<xml>
+				<config xmlns="http://acme.com/defs">
+					<port>1029</port>
+					<host>www.acme.com</host>
+				</config>
+			</xml>
+		</property>
+	</endpoint-description>
+</endpoint-descriptions>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_configured/src/discovery_impl.c
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_configured/src/discovery_impl.c b/bundles/remote_services/discovery_configured/src/discovery_impl.c
new file mode 100644
index 0000000..89c777e
--- /dev/null
+++ b/bundles/remote_services/discovery_configured/src/discovery_impl.c
@@ -0,0 +1,123 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * discovery_impl.c
+ *
+ * \date        Aug 8, 2014
+ * \author    	<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ * \copyright	Apache License, Version 2.0
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <netdb.h>
+
+#include "celix_threads.h"
+#include "bundle_context.h"
+#include "utils.h"
+#include "log_helper.h"
+
+#include "discovery.h"
+#include "discovery_impl.h"
+
+
+celix_status_t discovery_create(bundle_context_pt context, discovery_pt *discovery) {
+	celix_status_t status;
+
+	*discovery = malloc(sizeof(struct discovery));
+	if (!*discovery) {
+		status = CELIX_ENOMEM;
+	}
+	else {
+		(*discovery)->context = context;
+		(*discovery)->poller = NULL;
+		(*discovery)->server = NULL;
+
+		(*discovery)->listenerReferences = hashMap_create(serviceReference_hashCode, NULL, serviceReference_equals2, NULL);
+		(*discovery)->discoveredServices = hashMap_create(utils_stringHash, NULL, utils_stringEquals, NULL);
+
+		status = celixThreadMutex_create(&(*discovery)->listenerReferencesMutex, NULL);
+		status = celixThreadMutex_create(&(*discovery)->discoveredServicesMutex, NULL);
+
+		logHelper_create(context, &(*discovery)->loghelper);
+	}
+
+	return status;
+}
+
+celix_status_t discovery_start(discovery_pt discovery) {
+    celix_status_t status;
+
+	logHelper_start(discovery->loghelper);
+
+    status = endpointDiscoveryPoller_create(discovery, discovery->context, DEFAULT_POLL_ENDPOINTS, &discovery->poller);
+    if (status != CELIX_SUCCESS) {
+    	return CELIX_BUNDLE_EXCEPTION;
+    }
+
+    status = endpointDiscoveryServer_create(discovery, discovery->context, DEFAULT_SERVER_PATH, DEFAULT_SERVER_PORT, DEFAULT_SERVER_IP, &discovery->server);
+    if (status != CELIX_SUCCESS) {
+    	return CELIX_BUNDLE_EXCEPTION;
+    }
+
+    return status;
+}
+
+celix_status_t discovery_stop(discovery_pt discovery) {
+	celix_status_t status;
+
+	status = endpointDiscoveryServer_destroy(discovery->server);
+	status = endpointDiscoveryPoller_destroy(discovery->poller);
+
+	logHelper_stop(discovery->loghelper);
+
+	return status;
+}
+
+celix_status_t discovery_destroy(discovery_pt discovery) {
+	celix_status_t status = CELIX_SUCCESS;
+
+	discovery->context = NULL;
+	discovery->poller = NULL;
+	discovery->server = NULL;
+
+	celixThreadMutex_lock(&discovery->discoveredServicesMutex);
+
+	hashMap_destroy(discovery->discoveredServices, false, false);
+	discovery->discoveredServices = NULL;
+
+	celixThreadMutex_unlock(&discovery->discoveredServicesMutex);
+
+	celixThreadMutex_destroy(&discovery->discoveredServicesMutex);
+
+	celixThreadMutex_lock(&discovery->listenerReferencesMutex);
+
+	hashMap_destroy(discovery->listenerReferences, false, false);
+	discovery->listenerReferences = NULL;
+
+	celixThreadMutex_unlock(&discovery->listenerReferencesMutex);
+
+	celixThreadMutex_destroy(&discovery->listenerReferencesMutex);
+
+	logHelper_destroy(&discovery->loghelper);
+
+	free(discovery);
+
+	return status;
+}

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_configured/src/discovery_impl.h
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_configured/src/discovery_impl.h b/bundles/remote_services/discovery_configured/src/discovery_impl.h
new file mode 100644
index 0000000..a9d56c1
--- /dev/null
+++ b/bundles/remote_services/discovery_configured/src/discovery_impl.h
@@ -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.
+ */
+/*
+ * discovery_impl.h
+ *
+ *  \date       Sep 29, 2011
+ *  \author    	<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ *  \copyright	Apache License, Version 2.0
+ */
+
+#ifndef DISCOVERY_IMPL_H_
+#define DISCOVERY_IMPL_H_
+
+#include "bundle_context.h"
+#include "service_reference.h"
+
+#include "endpoint_description.h"
+#include "endpoint_listener.h"
+
+#include "endpoint_discovery_poller.h"
+#include "endpoint_discovery_server.h"
+
+#include "log_helper.h"
+
+#define DEFAULT_SERVER_IP 	"127.0.0.1"
+#define DEFAULT_SERVER_PORT "9999"
+#define DEFAULT_SERVER_PATH "/org.apache.celix.discovery.configured"
+#define DEFAULT_POLL_ENDPOINTS "http://localhost:9999/org.apache.celix.discovery.configured"
+
+
+//struct discovery_impl {
+//	bundle_context_pt context;
+//
+//	celix_thread_mutex_t listenerReferencesMutex;
+//	celix_thread_mutex_t discoveredServicesMutex;
+//
+//	hash_map_pt listenerReferences; //key=serviceReference, value=nop
+//	hash_map_pt discoveredServices; //key=endpointId (string), value=endpoint_description_pt
+//
+//	endpoint_discovery_poller_pt poller;
+//	endpoint_discovery_server_pt server;
+//
+//	log_helper_pt loghelper;
+//};
+
+#endif /* DISCOVERY_IMPL_H_ */

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_etcd/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_etcd/CMakeLists.txt b/bundles/remote_services/discovery_etcd/CMakeLists.txt
new file mode 100644
index 0000000..c20c4f9
--- /dev/null
+++ b/bundles/remote_services/discovery_etcd/CMakeLists.txt
@@ -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.
+
+celix_subproject(RSA_DISCOVERY_ETCD "Option to enable building the Discovery (ETCD) bundle" ON)
+if (RSA_DISCOVERY_ETCD)
+	find_package(CURL REQUIRED)
+	find_package(LibXml2 REQUIRED)
+	find_package(Jansson REQUIRED)
+    
+	add_celix_bundle(rsa_discovery_etcd
+        VERSION 0.9.0
+        SYMBOLIC_NAME "apache_celix_rsa_discovery_etcd"
+        NAME "Apache Celix RSA Discovery ETCD"
+        SOURCES
+			src/discovery_impl.c
+	    	src/etcd_watcher.c
+			$<TARGET_OBJECTS:Celix::rsa_discovery_common>
+			$<TARGET_OBJECTS:Celix::civetweb>
+	)
+    target_link_libraries(rsa_discovery_etcd PRIVATE Celix::log_helper Celix::etcdlib_static Celix::rsa_common)
+	target_include_directories(rsa_discovery_etcd PRIVATE src)
+	target_include_directories(rsa_discovery_etcd PRIVATE
+			$<TARGET_PROPERTY:Celix::rsa_discovery_common,INCLUDE_DIRECTORIES>
+			$<TARGET_PROPERTY:Celix::civetweb,INCLUDE_DIRECTORIES>
+	)
+        target_include_directories(rsa_discovery_etcd SYSTEM PRIVATE
+			${CURL_INCLUDE_DIR}
+			${JANSSON_INCLUDE_DIR}
+			${LIBXML2_INCLUDE_DIR}
+	)
+	target_link_libraries(rsa_discovery_etcd PRIVATE ${CURL_LIBRARIES} ${LIBXML2_LIBRARIES} ${JANSSON_LIBRARIES})
+
+	install_celix_bundle(rsa_discovery_etcd EXPORT celix COMPONENT rsa)
+	#Setup target aliases to match external usage
+	add_library(Celix::rsa_discovery_etcd ALIAS rsa_discovery_etcd)
+endif (RSA_DISCOVERY_ETCD)

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_etcd/README.md
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_etcd/README.md b/bundles/remote_services/discovery_etcd/README.md
new file mode 100644
index 0000000..e560264
--- /dev/null
+++ b/bundles/remote_services/discovery_etcd/README.md
@@ -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.
+-->
+
+## Discovery ETCD
+
+The Celix Discovery ETCD bundles realizes OSGi services discovery based on [etcd](https://github.com/coreos/etcd).
+
+###### Properties
+    DISCOVERY_ETCD_ROOT_PATH             used path (default: discovery)
+    DEFAULT_ETCD_SERVER_IP               ip address of the etcd server (default: 127.0.0.1)
+    DEFAULT_ETCD_SERVER_PORT             port of the etcd server  (default: 2379)
+    DEFAULT_ETCD_TTL                     time-to-live for etcd entries in seconds (default: 30)
+
+###### CMake option
+    BUILD_RSA_DISCOVERY_ETCD=ON

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_etcd/src/discovery_impl.c
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_etcd/src/discovery_impl.c b/bundles/remote_services/discovery_etcd/src/discovery_impl.c
new file mode 100644
index 0000000..500399e
--- /dev/null
+++ b/bundles/remote_services/discovery_etcd/src/discovery_impl.c
@@ -0,0 +1,193 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * discovery_impl.c
+ *
+ * \date        Aug 8, 2014
+ * \author    	<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ * \copyright	Apache License, Version 2.0
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <netdb.h>
+#include <netinet/in.h>
+
+#include "constants.h"
+#include "celix_threads.h"
+#include "bundle_context.h"
+#include "array_list.h"
+#include "utils.h"
+#include "celix_errno.h"
+#include "filter.h"
+#include "service_reference.h"
+#include "service_registration.h"
+#include "remote_constants.h"
+
+
+#include "discovery.h"
+#include "discovery_impl.h"
+#include "etcd_watcher.h"
+#include "endpoint_discovery_poller.h"
+#include "endpoint_discovery_server.h"
+
+
+
+celix_status_t discovery_create(bundle_context_pt context, discovery_t** out) {
+	celix_status_t status = CELIX_SUCCESS;
+
+	discovery_t* discovery = calloc(1, sizeof(*discovery));
+	discovery_impl_t* pImpl = calloc(1, sizeof(*pImpl));
+
+	if (discovery != NULL && pImpl != NULL) {
+		discovery->pImpl = pImpl;
+		discovery->context = context;
+		discovery->poller = NULL;
+		discovery->server = NULL;
+
+		discovery->listenerReferences = hashMap_create(serviceReference_hashCode, NULL, serviceReference_equals2,
+														  NULL);
+		discovery->discoveredServices = hashMap_create(utils_stringHash, NULL, utils_stringEquals, NULL);
+
+		status = celixThreadMutex_create(&discovery->listenerReferencesMutex, NULL);
+		status = celixThreadMutex_create(&discovery->discoveredServicesMutex, NULL);
+
+		logHelper_create(context, &discovery->loghelper);
+	} else {
+		status = CELIX_ENOMEM;
+		free(discovery);
+		free(pImpl);
+	}
+
+	if (status == CELIX_SUCCESS) {
+		*out = discovery;
+	}
+
+	return status;
+}
+
+
+
+celix_status_t discovery_destroy(discovery_pt discovery) {
+	celix_status_t status = CELIX_SUCCESS;
+
+	discovery->context = NULL;
+	discovery->poller = NULL;
+	discovery->server = NULL;
+
+	celixThreadMutex_lock(&discovery->discoveredServicesMutex);
+
+	hashMap_destroy(discovery->discoveredServices, false, false);
+	discovery->discoveredServices = NULL;
+
+	celixThreadMutex_unlock(&discovery->discoveredServicesMutex);
+
+	celixThreadMutex_destroy(&discovery->discoveredServicesMutex);
+
+	celixThreadMutex_lock(&discovery->listenerReferencesMutex);
+
+	hashMap_destroy(discovery->listenerReferences, false, false);
+	discovery->listenerReferences = NULL;
+
+	celixThreadMutex_unlock(&discovery->listenerReferencesMutex);
+
+	celixThreadMutex_destroy(&discovery->listenerReferencesMutex);
+
+	logHelper_destroy(&discovery->loghelper);
+
+	free(discovery);
+
+	return status;
+}
+
+celix_status_t discovery_start(discovery_pt discovery) {
+    celix_status_t status = CELIX_SUCCESS;
+	const char *port = NULL;
+	const char *path = NULL;
+
+	logHelper_start(discovery->loghelper);
+
+	bundleContext_getProperty(discovery->context, DISCOVERY_SERVER_PORT, &port);
+	if (port == NULL) {
+		port = DEFAULT_SERVER_PORT;
+	}
+
+	bundleContext_getProperty(discovery->context, DISCOVERY_SERVER_PATH, &path);
+	if (path == NULL) {
+		path = DEFAULT_SERVER_PATH;
+	}
+
+    status = endpointDiscoveryPoller_create(discovery, discovery->context, DEFAULT_POLL_ENDPOINTS, &discovery->poller);
+    if (status != CELIX_SUCCESS) {
+    	return CELIX_BUNDLE_EXCEPTION;
+    }
+
+    status = endpointDiscoveryServer_create(discovery, discovery->context, DEFAULT_SERVER_PATH, DEFAULT_SERVER_PORT, DEFAULT_SERVER_IP, &discovery->server);
+    if (status != CELIX_SUCCESS) {
+		return CELIX_BUNDLE_EXCEPTION;
+    }
+
+    status = etcdWatcher_create(discovery, discovery->context, &discovery->pImpl->watcher);
+    if (status != CELIX_SUCCESS) {
+    	return CELIX_BUNDLE_EXCEPTION;
+    }
+    return status;
+}
+
+celix_status_t discovery_stop(discovery_pt discovery) {
+	celix_status_t status;
+
+	status = etcdWatcher_destroy(discovery->pImpl->watcher);
+	if (status != CELIX_SUCCESS) {
+		return CELIX_BUNDLE_EXCEPTION;
+	}
+
+	status = endpointDiscoveryServer_destroy(discovery->server);
+	if (status != CELIX_SUCCESS) {
+		return CELIX_BUNDLE_EXCEPTION;
+	}
+
+	status = endpointDiscoveryPoller_destroy(discovery->poller);
+	if (status != CELIX_SUCCESS) {
+		return CELIX_BUNDLE_EXCEPTION;
+	}
+	hash_map_iterator_pt iter;
+
+	celixThreadMutex_lock(&discovery->discoveredServicesMutex);
+
+	iter = hashMapIterator_create(discovery->discoveredServices);
+	while (hashMapIterator_hasNext(iter)) {
+		hash_map_entry_pt entry = hashMapIterator_nextEntry(iter);
+		endpoint_description_pt endpoint = hashMapEntry_getValue(entry);
+
+		discovery_informEndpointListeners(discovery, endpoint, false);
+	}
+	hashMapIterator_destroy(iter);
+
+	celixThreadMutex_unlock(&discovery->discoveredServicesMutex);
+
+
+	logHelper_stop(discovery->loghelper);
+
+	return status;
+}
+
+
+

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_etcd/src/discovery_impl.h
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_etcd/src/discovery_impl.h b/bundles/remote_services/discovery_etcd/src/discovery_impl.h
new file mode 100644
index 0000000..f28017b
--- /dev/null
+++ b/bundles/remote_services/discovery_etcd/src/discovery_impl.h
@@ -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.
+ */
+/*
+ * discovery_impl.h
+ *
+ *  \date       Sep 29, 2011
+ *  \author    	<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ *  \copyright	Apache License, Version 2.0
+ */
+
+#ifndef DISCOVERY_IMPL_H_
+#define DISCOVERY_IMPL_H_
+
+#include "bundle_context.h"
+#include "service_reference.h"
+
+#include "endpoint_description.h"
+#include "endpoint_listener.h"
+
+#include "endpoint_discovery_poller.h"
+#include "endpoint_discovery_server.h"
+#include "etcd_watcher.h"
+
+#include "log_helper.h"
+
+#define DEFAULT_SERVER_IP 	"127.0.0.1"
+#define DEFAULT_SERVER_PORT "9999"
+#define DEFAULT_SERVER_PATH "/org.apache.celix.discovery.etcd"
+
+#define DEFAULT_POLL_ENDPOINTS ""
+
+#define FREE_MEM(ptr) if(ptr) {free(ptr); ptr = NULL;}
+
+struct discovery_impl {
+    etcd_watcher_t* watcher;
+};
+
+#endif /* DISCOVERY_H_ */

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_etcd/src/etcd_watcher.c
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_etcd/src/etcd_watcher.c b/bundles/remote_services/discovery_etcd/src/etcd_watcher.c
new file mode 100644
index 0000000..ebeac4f
--- /dev/null
+++ b/bundles/remote_services/discovery_etcd/src/etcd_watcher.c
@@ -0,0 +1,397 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * etcd_watcher.c
+ *
+ * \date       16 Sep 2014
+ * \author     <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ * \copyright  Apache License, Version 2.0
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "log_helper.h"
+#include "log_service.h"
+#include "constants.h"
+#include "utils.h"
+#include "discovery.h"
+#include "discovery_impl.h"
+
+#include <curl/curl.h>
+#include "etcd.h"
+#include "etcd_watcher.h"
+
+#include "endpoint_discovery_poller.h"
+
+struct etcd_watcher {
+    discovery_pt discovery;
+    log_helper_pt* loghelper;
+    hash_map_pt entries;
+
+	celix_thread_mutex_t watcherLock;
+	celix_thread_t watcherThread;
+
+	volatile bool running;
+};
+
+
+#define MAX_ROOTNODE_LENGTH			128
+#define MAX_LOCALNODE_LENGTH		4096
+#define MAX_VALUE_LENGTH			256
+
+#define CFG_ETCD_ROOT_PATH			"DISCOVERY_ETCD_ROOT_PATH"
+#define DEFAULT_ETCD_ROOTPATH		"discovery"
+
+#define CFG_ETCD_SERVER_IP			"DISCOVERY_ETCD_SERVER_IP"
+#define DEFAULT_ETCD_SERVER_IP		"127.0.0.1"
+
+#define CFG_ETCD_SERVER_PORT		"DISCOVERY_ETCD_SERVER_PORT"
+#define DEFAULT_ETCD_SERVER_PORT 	2379
+
+// be careful - this should be higher than the curl timeout
+#define CFG_ETCD_TTL   				"DISCOVERY_ETCD_TTL"
+#define DEFAULT_ETCD_TTL 			30
+
+
+// note that the rootNode shouldn't have a leading slash
+static celix_status_t etcdWatcher_getRootPath(bundle_context_pt context, char* rootNode) {
+	celix_status_t status = CELIX_SUCCESS;
+	const char* rootPath = NULL;
+
+	if (((bundleContext_getProperty(context, CFG_ETCD_ROOT_PATH, &rootPath)) != CELIX_SUCCESS) || (!rootPath)) {
+		strncpy(rootNode, DEFAULT_ETCD_ROOTPATH, MAX_ROOTNODE_LENGTH);
+	}
+	else {
+		strncpy(rootNode, rootPath, MAX_ROOTNODE_LENGTH);
+	}
+
+	return status;
+}
+
+static celix_status_t etcdWatcher_getLocalNodePath(bundle_context_pt context, char* localNodePath) {
+	celix_status_t status = CELIX_SUCCESS;
+	char rootPath[MAX_ROOTNODE_LENGTH];
+    const char* uuid = NULL;
+
+    if ((etcdWatcher_getRootPath(context, rootPath) != CELIX_SUCCESS)) {
+		status = CELIX_ILLEGAL_STATE;
+    }
+	else if (((bundleContext_getProperty(context, OSGI_FRAMEWORK_FRAMEWORK_UUID, &uuid)) != CELIX_SUCCESS) || (!uuid)) {
+		status = CELIX_ILLEGAL_STATE;
+	}
+	else if (rootPath[strlen(rootPath) - 1] == '/') {
+    	snprintf(localNodePath, MAX_LOCALNODE_LENGTH, "%s%s", rootPath, uuid);
+    }
+    else {
+    	snprintf(localNodePath, MAX_LOCALNODE_LENGTH, "%s/%s", rootPath, uuid);
+    }
+
+    return status;
+}
+
+static void add_node(const char *key, const char *value, void* arg) {
+	discovery_pt discovery = (discovery_pt) arg;
+	endpointDiscoveryPoller_addDiscoveryEndpoint(discovery->poller, (char *) value);
+}
+
+/*
+ * retrieves all already existing discovery endpoints
+ * from etcd and adds them to the poller.
+ *
+ * returns the modifiedIndex of the last modified
+ * discovery endpoint (see etcd documentation).
+ */
+static celix_status_t etcdWatcher_addAlreadyExistingWatchpoints(discovery_pt discovery, long long* highestModified) {
+	celix_status_t status = CELIX_SUCCESS;
+
+	char rootPath[MAX_ROOTNODE_LENGTH];
+	status = etcdWatcher_getRootPath(discovery->context, rootPath);
+
+	if (status == CELIX_SUCCESS) {
+		if(etcd_get_directory(rootPath, add_node, discovery, highestModified)) {
+			    status = CELIX_ILLEGAL_ARGUMENT;
+		}
+	}
+
+	return status;
+}
+
+
+static celix_status_t etcdWatcher_addOwnFramework(etcd_watcher_pt watcher)
+{
+    celix_status_t status = CELIX_BUNDLE_EXCEPTION;
+    char localNodePath[MAX_LOCALNODE_LENGTH];
+    char *value;
+ 	char url[MAX_VALUE_LENGTH];
+    int modIndex;
+    char* endpoints = NULL;
+    const char* ttlStr = NULL;
+    int ttl;
+
+	bundle_context_pt context = watcher->discovery->context;
+	endpoint_discovery_server_pt server = watcher->discovery->server;
+
+    // register own framework
+    if ((status = etcdWatcher_getLocalNodePath(context, localNodePath)) != CELIX_SUCCESS) {
+        return status;
+    }
+
+	if (endpointDiscoveryServer_getUrl(server, url) != CELIX_SUCCESS) {
+		snprintf(url, MAX_VALUE_LENGTH, "http://%s:%s/%s", DEFAULT_SERVER_IP, DEFAULT_SERVER_PORT, DEFAULT_SERVER_PATH);
+	}
+
+	endpoints = url;
+
+    if ((bundleContext_getProperty(context, CFG_ETCD_TTL, &ttlStr) != CELIX_SUCCESS) || !ttlStr) {
+        ttl = DEFAULT_ETCD_TTL;
+    }
+    else
+    {
+        char* endptr = (char *) ttlStr;
+        errno = 0;
+        ttl = strtol(ttlStr, &endptr, 10);
+        if (*endptr || errno != 0) {
+            ttl = DEFAULT_ETCD_TTL;
+        }
+    }
+
+	if (etcd_get(localNodePath, &value, &modIndex) != true) {
+		etcd_set(localNodePath, endpoints, ttl, false);
+	}
+	else if (etcd_set(localNodePath, endpoints, ttl, true) == false)  {
+		logHelper_log(*watcher->loghelper, OSGI_LOGSERVICE_WARNING, "Cannot register local discovery");
+    }
+    else {
+        status = CELIX_SUCCESS;
+    }
+
+	FREE_MEM(value);
+
+    return status;
+}
+
+
+
+
+static celix_status_t etcdWatcher_addEntry(etcd_watcher_pt watcher, char* key, char* value) {
+	celix_status_t status = CELIX_BUNDLE_EXCEPTION;
+	endpoint_discovery_poller_pt poller = watcher->discovery->poller;
+
+	if (!hashMap_containsKey(watcher->entries, key)) {
+		status = endpointDiscoveryPoller_addDiscoveryEndpoint(poller, value);
+
+		if (status == CELIX_SUCCESS) {
+			hashMap_put(watcher->entries, strdup(key), strdup(value));
+		}
+	}
+
+	return status;
+}
+
+static celix_status_t etcdWatcher_removeEntry(etcd_watcher_pt watcher, char* key, char* value) {
+	celix_status_t status = CELIX_BUNDLE_EXCEPTION;
+	endpoint_discovery_poller_pt poller = watcher->discovery->poller;
+
+	hash_map_entry_pt entry = hashMap_getEntry(watcher->entries, key);
+
+	if (entry != NULL) {
+		void* origKey = hashMapEntry_getKey(entry);
+		void* value = hashMap_remove(watcher->entries, key);
+
+		free(origKey);
+
+		// check if there is another entry with the same value
+		hash_map_iterator_pt iter = hashMapIterator_create(watcher->entries);
+		unsigned int valueFound = 0;
+
+		while (hashMapIterator_hasNext(iter) && valueFound <= 1) {
+			if (strcmp(value, hashMapIterator_nextValue(iter)) == 0)
+				valueFound++;
+		}
+
+		hashMapIterator_destroy(iter);
+
+		if (valueFound == 0)
+			status = endpointDiscoveryPoller_removeDiscoveryEndpoint(poller, value);
+
+		free(value);
+
+	}
+
+	return status;
+
+}
+
+
+/*
+ * performs (blocking) etcd_watch calls to check for
+ * changing discovery endpoint information within etcd.
+ */
+static void* etcdWatcher_run(void* data) {
+	etcd_watcher_pt watcher = (etcd_watcher_pt) data;
+	time_t timeBeforeWatch = time(NULL);
+	char rootPath[MAX_ROOTNODE_LENGTH];
+	long long highestModified = 0;
+
+	bundle_context_pt context = watcher->discovery->context;
+
+	etcdWatcher_addAlreadyExistingWatchpoints(watcher->discovery, &highestModified);
+	etcdWatcher_getRootPath(context, rootPath);
+
+	while (watcher->running) {
+
+		char *rkey = NULL;
+		char *value = NULL;
+		char *preValue = NULL;
+		char *action = NULL;
+		long long modIndex;
+
+        if (etcd_watch(rootPath, highestModified + 1, &action, &preValue, &value, &rkey, &modIndex) == 0 && action != NULL) {
+			if (strcmp(action, "set") == 0) {
+				etcdWatcher_addEntry(watcher, rkey, value);
+			} else if (strcmp(action, "delete") == 0) {
+				etcdWatcher_removeEntry(watcher, rkey, value);
+			} else if (strcmp(action, "expire") == 0) {
+				etcdWatcher_removeEntry(watcher, rkey, value);
+			} else if (strcmp(action, "update") == 0) {
+				etcdWatcher_addEntry(watcher, rkey, value);
+			} else {
+				logHelper_log(*watcher->loghelper, OSGI_LOGSERVICE_INFO, "Unexpected action: %s", action);
+			}
+
+			highestModified = modIndex;
+        } else if (time(NULL) - timeBeforeWatch <= (DEFAULT_ETCD_TTL / 4)) {
+			sleep(DEFAULT_ETCD_TTL / 4);
+        }
+
+        FREE_MEM(action);
+        FREE_MEM(value);
+        FREE_MEM(preValue);
+        FREE_MEM(rkey);
+
+		// update own framework uuid
+		if (time(NULL) - timeBeforeWatch > (DEFAULT_ETCD_TTL / 4)) {
+			etcdWatcher_addOwnFramework(watcher);
+			timeBeforeWatch = time(NULL);
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * the ectdWatcher needs to have access to the endpoint_discovery_poller and therefore is only
+ * allowed to be created after the endpoint_discovery_poller
+ */
+celix_status_t etcdWatcher_create(discovery_pt discovery, bundle_context_pt context,
+		etcd_watcher_pt *watcher)
+{
+	celix_status_t status = CELIX_SUCCESS;
+
+	const char* etcd_server = NULL;
+	const char* etcd_port_string = NULL;
+	int etcd_port = 0;
+
+	if (discovery == NULL) {
+		return CELIX_BUNDLE_EXCEPTION;
+	}
+
+	(*watcher) = calloc(1, sizeof(struct etcd_watcher));
+	if (!*watcher) {
+		return CELIX_ENOMEM;
+	}
+	else
+	{
+		(*watcher)->discovery = discovery;
+		(*watcher)->loghelper = &discovery->loghelper;
+		(*watcher)->entries = hashMap_create(utils_stringHash, NULL, utils_stringEquals, NULL);
+	}
+
+	if ((bundleContext_getProperty(context, CFG_ETCD_SERVER_IP, &etcd_server) != CELIX_SUCCESS) || !etcd_server) {
+		etcd_server = DEFAULT_ETCD_SERVER_IP;
+	}
+
+	if ((bundleContext_getProperty(context, CFG_ETCD_SERVER_PORT, &etcd_port_string) != CELIX_SUCCESS) || !etcd_port_string) {
+		etcd_port = DEFAULT_ETCD_SERVER_PORT;
+	}
+	else
+	{
+		char* endptr = (char*)etcd_port_string;
+		errno = 0;
+		etcd_port =  strtol(etcd_port_string, &endptr, 10);
+		if (*endptr || errno != 0) {
+			etcd_port = DEFAULT_ETCD_SERVER_PORT;
+		}
+	}
+
+	if (etcd_init((char*) etcd_server, etcd_port, CURL_GLOBAL_DEFAULT) != 0) {
+		status = CELIX_BUNDLE_EXCEPTION;
+	} else {
+		status = CELIX_SUCCESS;
+	}
+
+    if (status == CELIX_SUCCESS) {
+        etcdWatcher_addOwnFramework(*watcher);
+        status = celixThreadMutex_create(&(*watcher)->watcherLock, NULL);
+    }
+
+    if (status == CELIX_SUCCESS) {
+        if (celixThreadMutex_lock(&(*watcher)->watcherLock) == CELIX_SUCCESS) {
+            status = celixThread_create(&(*watcher)->watcherThread, NULL, etcdWatcher_run, *watcher);
+            if (status == CELIX_SUCCESS) {
+                (*watcher)->running = true;
+            }
+            celixThreadMutex_unlock(&(*watcher)->watcherLock);
+        }
+    }
+
+    return status;
+}
+
+
+celix_status_t etcdWatcher_destroy(etcd_watcher_pt watcher) {
+	celix_status_t status = CELIX_SUCCESS;
+	char localNodePath[MAX_LOCALNODE_LENGTH];
+
+	celixThreadMutex_lock(&watcher->watcherLock);
+	watcher->running = false;
+	celixThreadMutex_unlock(&watcher->watcherLock);
+
+	celixThread_join(watcher->watcherThread, NULL);
+
+	// register own framework
+	status = etcdWatcher_getLocalNodePath(watcher->discovery->context, localNodePath);
+
+	if (status != CELIX_SUCCESS || etcd_del(localNodePath) == false)
+	{
+		logHelper_log(*watcher->loghelper, OSGI_LOGSERVICE_WARNING, "Cannot remove local discovery registration.");
+	}
+
+	watcher->loghelper = NULL;
+
+	hashMap_destroy(watcher->entries, true, true);
+
+	free(watcher);
+
+	return status;
+}
+

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_etcd/src/etcd_watcher.h
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_etcd/src/etcd_watcher.h b/bundles/remote_services/discovery_etcd/src/etcd_watcher.h
new file mode 100644
index 0000000..56bae92
--- /dev/null
+++ b/bundles/remote_services/discovery_etcd/src/etcd_watcher.h
@@ -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.
+ */
+/*
+ * etcd_watcher.h
+ *
+ * \date       17 Sep 2014
+ * \author     <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ * \copyright  Apache License, Version 2.0
+ */
+
+#ifndef ETCD_WATCHER_H_
+#define ETCD_WATCHER_H_
+
+#include "celix_errno.h"
+#include "discovery.h"
+#include "endpoint_discovery_poller.h"
+
+typedef struct etcd_watcher etcd_watcher_t;
+typedef struct etcd_watcher *etcd_watcher_pt;
+
+celix_status_t etcdWatcher_create(discovery_pt discovery,  bundle_context_pt context, etcd_watcher_pt *watcher);
+celix_status_t etcdWatcher_destroy(etcd_watcher_pt watcher);
+
+
+#endif /* ETCD_WATCHER_H_ */

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_shm/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_shm/CMakeLists.txt b/bundles/remote_services/discovery_shm/CMakeLists.txt
new file mode 100644
index 0000000..28c1fd4
--- /dev/null
+++ b/bundles/remote_services/discovery_shm/CMakeLists.txt
@@ -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.
+
+celix_subproject(RSA_DISCOVERY_SHM "Option to enable building the Discovery (SHM) bundle" OFF)
+if (RSA_DISCOVERY_SHM)
+find_package(CURL REQUIRED)
+	find_package(LibXml2 REQUIRED)
+
+	add_celix_bundle(rsa_discovery_shm
+        VERSION 0.0.1
+        SYMBOLIC_NAME "apache_celix_rsa_discovery_shm"
+        NAME "Apache Celix RSA Discovery SHM"
+        SOURCES
+			src/discovery_shm.c
+			src/discovery_shmWatcher.c
+			src/discovery_impl.c
+			$<TARGET_OBJECTS:Celix::rsa_discovery_common>
+			$<TARGET_OBJECTS:Celix::civetweb>
+	)
+	target_include_directories(rsa_discovery_shm PRIVATE
+			src
+			${LIBXML2_INCLUDE_DIR}
+			${CURL_INCLUDE_DIR}
+			$<TARGET_PROPERTY:Celix::rsa_discovery_common,INCLUDE_DIRECTORIES>
+			$<TARGET_PROPERTY:Celix::civetweb,INCLUDE_DIRECTORIES>
+	)
+	target_link_libraries(rsa_discovery_shm PRIVATE Celix::framework ${CURL_LIBRARIES} ${LIBXML2_LIBRARIES})
+
+	install_celix_bundle(rsa_discovery_shm EXPORT celix COMPONENT rsa)
+
+	#Setup target aliases to match external usage
+	add_library(Celix::rsa_discovery_shm ALIAS rsa_discovery_shm)
+endif (RSA_DISCOVERY_SHM)

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_shm/src/discovery_impl.c
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_shm/src/discovery_impl.c b/bundles/remote_services/discovery_shm/src/discovery_impl.c
new file mode 100644
index 0000000..fadff8c
--- /dev/null
+++ b/bundles/remote_services/discovery_shm/src/discovery_impl.c
@@ -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.
+ */
+/*
+ * discovery_impl.c
+ *
+ * \date        Aug 8, 2014
+ * \author    	<a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ * \copyright	Apache License, Version 2.0
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <netdb.h>
+#include <netinet/in.h>
+
+#include "constants.h"
+#include "celix_threads.h"
+#include "bundle_context.h"
+#include "array_list.h"
+#include "utils.h"
+#include "celix_errno.h"
+#include "filter.h"
+#include "service_reference.h"
+#include "service_registration.h"
+#include "remote_constants.h"
+
+
+#include "discovery.h"
+#include "discovery_impl.h"
+#include "discovery_shmWatcher.h"
+#include "endpoint_discovery_poller.h"
+#include "endpoint_discovery_server.h"
+
+celix_status_t discovery_create(bundle_context_pt context, discovery_t** out) {
+	celix_status_t status = CELIX_SUCCESS;
+
+	discovery_t* discovery = calloc(1, sizeof(*discovery));
+    discovery_impl_t* pImpl = calloc(1, sizeof(*pImpl));
+	if (discovery != NULL && pImpl != NULL) {
+        discovery->pImpl = pImpl;
+        discovery->context = context;
+        discovery->poller = NULL;
+        discovery->server = NULL;
+
+        discovery->listenerReferences = hashMap_create(serviceReference_hashCode, NULL, serviceReference_equals2, NULL);
+        discovery->discoveredServices = hashMap_create(utils_stringHash, NULL, utils_stringEquals, NULL);
+
+        celixThreadMutex_create(&discovery->listenerReferencesMutex, NULL);
+        celixThreadMutex_create(&discovery->discoveredServicesMutex, NULL);
+
+        if (logHelper_create(context, &discovery->loghelper) == CELIX_SUCCESS) {
+            logHelper_start(discovery->loghelper);
+        }
+    } else {
+        status = CELIX_ENOMEM;
+        free(discovery);
+        free(pImpl);
+    }
+
+    if (status == CELIX_SUCCESS) {
+        *out = discovery;
+    }
+
+	return status;
+}
+
+
+
+celix_status_t discovery_destroy(discovery_pt discovery) {
+	celix_status_t status = CELIX_SUCCESS;
+
+	discovery->context = NULL;
+	discovery->poller = NULL;
+	discovery->server = NULL;
+
+	celixThreadMutex_lock(&discovery->discoveredServicesMutex);
+
+	hashMap_destroy(discovery->discoveredServices, false, false);
+	discovery->discoveredServices = NULL;
+
+	celixThreadMutex_unlock(&discovery->discoveredServicesMutex);
+
+	celixThreadMutex_destroy(&discovery->discoveredServicesMutex);
+
+	celixThreadMutex_lock(&discovery->listenerReferencesMutex);
+
+	hashMap_destroy(discovery->listenerReferences, false, false);
+	discovery->listenerReferences = NULL;
+
+	celixThreadMutex_unlock(&discovery->listenerReferencesMutex);
+
+	celixThreadMutex_destroy(&discovery->listenerReferencesMutex);
+
+
+
+
+	free(discovery);
+
+	return status;
+}
+
+celix_status_t discovery_start(discovery_pt discovery) {
+    celix_status_t status;
+
+    status = endpointDiscoveryPoller_create(discovery, discovery->context, DEFAULT_POLL_ENDPOINTS, &discovery->poller);
+    if (status == CELIX_SUCCESS) {
+        status = endpointDiscoveryServer_create(discovery, discovery->context, DEFAULT_SERVER_PATH, DEFAULT_SERVER_PORT, DEFAULT_SERVER_IP, &discovery->server);
+    }
+
+    if (status == CELIX_SUCCESS) {
+        status = discoveryShmWatcher_create(discovery);
+    }
+
+    return status;
+}
+
+celix_status_t discovery_stop(discovery_pt discovery) {
+	celix_status_t status;
+
+    status = discoveryShmWatcher_destroy(discovery);
+
+    if (status == CELIX_SUCCESS) {
+        status = endpointDiscoveryServer_destroy(discovery->server);
+    }
+
+	endpointDiscoveryPoller_destroy(discovery->poller);
+
+	if (status == CELIX_SUCCESS) {
+        hash_map_iterator_pt iter;
+
+        celixThreadMutex_lock(&discovery->discoveredServicesMutex);
+
+        iter = hashMapIterator_create(discovery->discoveredServices);
+        while (hashMapIterator_hasNext(iter)) {
+            hash_map_entry_pt entry = hashMapIterator_nextEntry(iter);
+            endpoint_description_pt endpoint = hashMapEntry_getValue(entry);
+
+            discovery_informEndpointListeners(discovery, endpoint, false);
+        }
+        hashMapIterator_destroy(iter);
+
+        celixThreadMutex_unlock(&discovery->discoveredServicesMutex);
+
+        logHelper_stop(discovery->loghelper);
+        logHelper_destroy(&discovery->loghelper);
+	}
+
+	return status;
+}
+
+
+

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_shm/src/discovery_impl.h
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_shm/src/discovery_impl.h b/bundles/remote_services/discovery_shm/src/discovery_impl.h
new file mode 100644
index 0000000..e994b1f
--- /dev/null
+++ b/bundles/remote_services/discovery_shm/src/discovery_impl.h
@@ -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.
+ */
+/*
+ * discovery_impl.h
+ *
+ *  \date       Oct 01, 2014
+ *  \author    	<a href="mailto:celix-dev@celix.apache.org">Apache Celix Project Team</a>
+ *  \copyright	Apache License, Version 2.0
+ */
+
+#ifndef DISCOVERY_IMPL_H_
+#define DISCOVERY_IMPL_H_
+
+#include "bundle_context.h"
+#include "service_reference.h"
+
+#include "endpoint_description.h"
+#include "endpoint_listener.h"
+
+#include "endpoint_discovery_poller.h"
+#include "endpoint_discovery_server.h"
+#include "discovery_shmWatcher.h"
+
+#define DEFAULT_SERVER_IP   "127.0.0.1"
+#define DEFAULT_SERVER_PORT "9999"
+#define DEFAULT_SERVER_PATH "/org.apache.celix.discovery.shm"
+#define DEFAULT_POLL_ENDPOINTS "http://localhost:9999/org.apache.celix.discovery.shm"
+
+#define MAX_ROOTNODE_LENGTH    64
+#define MAX_LOCALNODE_LENGTH   256
+
+
+struct discovery_impl {
+    shm_watcher_t* watcher;
+};
+
+#endif /* DISCOVERY_H_ */

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_shm/src/discovery_shm.c
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_shm/src/discovery_shm.c b/bundles/remote_services/discovery_shm/src/discovery_shm.c
new file mode 100644
index 0000000..1b1170e
--- /dev/null
+++ b/bundles/remote_services/discovery_shm/src/discovery_shm.c
@@ -0,0 +1,284 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0 
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*
+ * discovery_shm.c
+ *
+ *  \date       26 Jul 2014
+ *  \author     <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ *  \copyright  Apache License, Version 2.0
+ */
+
+
+
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+
+#include <celix_errno.h>
+#include <celix_threads.h>
+
+#include "discovery_shm.h"
+
+#define DISCOVERY_SHM_MEMSIZE 262144
+#define DISCOVERY_SHM_FILENAME "/dev/null"
+#define DISCOVERY_SHM_FTOK_ID 50
+#define DISCOVERY_SEM_FILENAME "/dev/null"
+#define DISCOVERY_SEM_FTOK_ID 54
+
+struct shmEntry {
+    char key[SHM_ENTRY_MAX_KEY_LENGTH];
+    char value[SHM_ENTRY_MAX_VALUE_LENGTH];
+
+    time_t expires;
+};
+
+typedef struct shmEntry shmEntry;
+
+struct shmData {
+    shmEntry entries[SHM_DATA_MAX_ENTRIES];
+    int numOfEntries;
+    int shmId;
+
+    celix_thread_mutex_t globalLock;
+};
+
+void* shmAdress;
+
+static celix_status_t discoveryShm_removeWithIndex(shmData_pt data, int index);
+
+/* returns the ftok key to identify shared memory*/
+static key_t discoveryShm_getKey() {
+    return ftok(DISCOVERY_SHM_FILENAME, DISCOVERY_SHM_FTOK_ID);
+}
+
+/* creates a new shared memory block */
+celix_status_t discoveryShm_create(shmData_pt* data) {
+    celix_status_t status;
+
+    shmData_pt shmData = calloc(1, sizeof(*shmData));
+    key_t shmKey = discoveryShm_getKey();
+
+    if (!shmData) {
+        status = CELIX_ENOMEM;
+    } else if ((shmData->shmId = shmget(shmKey, DISCOVERY_SHM_MEMSIZE, IPC_CREAT | 0666)) < 0) {
+        status = CELIX_BUNDLE_EXCEPTION;
+    } else if ((shmAdress = shmat(shmData->shmId, 0, 0)) == (char*) -1) {
+        status = CELIX_BUNDLE_EXCEPTION;
+    } else {
+        celix_thread_mutexattr_t threadAttr;
+
+        shmData->numOfEntries = 0;
+
+        status = celixThreadMutexAttr_create(&threadAttr);
+
+#ifdef LINUX
+        if (status == CELIX_SUCCESS) {
+            // This is Linux specific
+            status = pthread_mutexattr_setrobust(&threadAttr, PTHREAD_MUTEX_ROBUST);
+        }
+#endif
+
+        if (status == CELIX_SUCCESS) {
+            status = celixThreadMutex_create(&shmData->globalLock, &threadAttr);
+        }
+
+        if (status == CELIX_SUCCESS) {
+            memcpy(shmAdress, shmData, sizeof(struct shmData));
+            (*data) = shmAdress;
+        }
+    }
+
+    free(shmData);
+
+    return status;
+}
+
+celix_status_t discoveryShm_attach(shmData_pt* data) {
+    celix_status_t status = CELIX_SUCCESS;
+    key_t shmKey = ftok(DISCOVERY_SHM_FILENAME, DISCOVERY_SHM_FTOK_ID);
+    int shmId = -1;
+
+    if ((shmId = shmget(shmKey, DISCOVERY_SHM_MEMSIZE, 0666)) < 0) {
+        status = CELIX_BUNDLE_EXCEPTION;
+    }
+
+    /* shmat has a curious return value of (void*)-1 in case of error */
+    void *mem=shmat(shmId, 0, 0);
+    if(mem==((void*)-1)){
+	status = CELIX_BUNDLE_EXCEPTION;
+    }
+    else{
+	(*data)=mem;
+    }
+
+    return status;
+}
+
+static celix_status_t discoveryShm_getwithIndex(shmData_pt data, char* key, char* value, int* index) {
+    celix_status_t status = CELIX_BUNDLE_EXCEPTION;
+    time_t currentTime = time(NULL);
+    unsigned int i;
+
+    for (i = 0; i < data->numOfEntries && status != CELIX_SUCCESS; i++) {
+        shmEntry entry = data->entries[i];
+        // check if entry is still valid
+        if (data->entries[i].expires < currentTime) {
+            discoveryShm_removeWithIndex(data, i);
+        } else if (strcmp(entry.key, key) == 0) {
+            if (value) {
+                strcpy(value, entry.value);
+            }
+            if (index) {
+                (*index) = i;
+            }
+            status = CELIX_SUCCESS;
+        }
+    }
+
+    return status;
+}
+
+celix_status_t discoveryShm_getKeys(shmData_pt data, char** keys, int* size) {
+    celix_status_t status;
+
+    status = celixThreadMutex_lock(&data->globalLock);
+
+    if (status == CELIX_SUCCESS) {
+    	unsigned int i = 0;
+        for (i = 0; i < data->numOfEntries; i++) {
+            shmEntry entry = data->entries[i];
+
+            if (strlen(entry.key)>0) {
+                snprintf(keys[i], SHM_ENTRY_MAX_KEY_LENGTH, "%s", entry.key);
+            }
+        }
+
+        (*size) = i;
+
+        celixThreadMutex_unlock(&data->globalLock);
+    }
+
+    return status;
+}
+
+celix_status_t discoveryShm_set(shmData_pt data, char *key, char* value) {
+    celix_status_t status;
+    int index = -1;
+
+    if (data->numOfEntries >= SHM_DATA_MAX_ENTRIES) {
+        status = CELIX_ILLEGAL_STATE;
+    } else {
+        status = celixThreadMutex_lock(&data->globalLock);
+
+        if (status == CELIX_SUCCESS) {
+            // check if key already there
+            status = discoveryShm_getwithIndex(data, key, NULL, &index);
+            if (status != CELIX_SUCCESS) {
+                index = data->numOfEntries;
+
+                snprintf(data->entries[index].key, SHM_ENTRY_MAX_KEY_LENGTH, "%s", key);
+                data->numOfEntries++;
+
+                 status = CELIX_SUCCESS;
+            }
+
+            snprintf(data->entries[index].value, SHM_ENTRY_MAX_VALUE_LENGTH, "%s", value);
+            data->entries[index].expires = (time(NULL) + SHM_ENTRY_DEFAULT_TTL);
+
+            celixThreadMutex_unlock(&data->globalLock);
+        }
+    }
+
+    return status;
+}
+
+celix_status_t discoveryShm_get(shmData_pt data, char* key, char* value) {
+    celix_status_t status;
+
+    status = celixThreadMutex_lock(&data->globalLock);
+
+    if (status == CELIX_SUCCESS) {
+        status = discoveryShm_getwithIndex(data, key, value, NULL);
+
+        celixThreadMutex_unlock(&data->globalLock);
+    }
+
+    return status;
+}
+
+static celix_status_t discoveryShm_removeWithIndex(shmData_pt data, int index) {
+    celix_status_t status = CELIX_SUCCESS;
+
+    data->numOfEntries--;
+    if (index < data->numOfEntries) {
+        memcpy((void*) &data->entries[index], (void*) &data->entries[index + 1], ((data->numOfEntries - index) * sizeof(struct shmEntry)));
+    }
+
+    return status;
+}
+
+celix_status_t discoveryShm_remove(shmData_pt data, char* key) {
+    celix_status_t status;
+    int index = -1;
+
+    status = celixThreadMutex_lock(&data->globalLock);
+
+    if (status == CELIX_SUCCESS) {
+        status = discoveryShm_getwithIndex(data, key, NULL, &index);
+
+        if (status == CELIX_SUCCESS) {
+            status = discoveryShm_removeWithIndex(data, index);
+        }
+
+        celixThreadMutex_unlock(&data->globalLock);
+    }
+
+    return status;
+}
+
+celix_status_t discoveryShm_detach(shmData_pt data) {
+    celix_status_t status = CELIX_BUNDLE_EXCEPTION;
+
+    if (data->numOfEntries == 0) {
+        status = discoveryShm_destroy(data);
+    }
+    else if (shmdt(shmAdress) == 0) {
+        status = CELIX_SUCCESS;
+    }
+
+    return status;
+}
+
+celix_status_t discoveryShm_destroy(shmData_pt data) {
+    celix_status_t status = CELIX_BUNDLE_EXCEPTION;
+
+    if (shmctl(data->shmId, IPC_RMID, 0) == 0) {
+        status = CELIX_SUCCESS;
+    }
+
+    return status;
+
+}

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_shm/src/discovery_shm.h
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_shm/src/discovery_shm.h b/bundles/remote_services/discovery_shm/src/discovery_shm.h
new file mode 100644
index 0000000..9c4593b
--- /dev/null
+++ b/bundles/remote_services/discovery_shm/src/discovery_shm.h
@@ -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.
+ */
+
+/*
+ * shm.h
+ *
+ *  \date       26 Jul 2014
+ *  \author     <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ *  \copyright  Apache License, Version 2.0
+ */
+
+
+
+#ifndef _DISCOVERY_SHM_H_
+#define _DISCOVERY_SHM_H_
+
+#include <celix_errno.h>
+
+#define SHM_ENTRY_MAX_KEY_LENGTH	256
+#define SHM_ENTRY_MAX_VALUE_LENGTH	256
+
+// defines the time-to-live in seconds
+#define SHM_ENTRY_DEFAULT_TTL		60
+
+// we currently support 64 separate discovery instances
+#define SHM_DATA_MAX_ENTRIES		64
+
+typedef struct shmData* shmData_pt;
+
+/* creates a new shared memory block */
+celix_status_t discoveryShm_create(shmData_pt* data);
+celix_status_t discoveryShm_attach(shmData_pt* data);
+celix_status_t discoveryShm_set(shmData_pt data, char *key, char* value);
+celix_status_t discoveryShm_get(shmData_pt data, char* key, char* value);
+celix_status_t discoveryShm_getKeys(shmData_pt data, char** keys, int* size);
+celix_status_t discoveryShm_remove(shmData_pt data, char* key);
+celix_status_t discoveryShm_detach(shmData_pt data);
+celix_status_t discoveryShm_destroy(shmData_pt data);
+
+#endif

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_shm/src/discovery_shmWatcher.c
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_shm/src/discovery_shmWatcher.c b/bundles/remote_services/discovery_shm/src/discovery_shmWatcher.c
new file mode 100644
index 0000000..ef7df71
--- /dev/null
+++ b/bundles/remote_services/discovery_shm/src/discovery_shmWatcher.c
@@ -0,0 +1,255 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * discovery_shmWatcher.c
+ *
+ * \date       16 Sep 2014
+ * \author     <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ * \copyright  Apache License, Version 2.0
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+
+#include "celix_log.h"
+#include "constants.h"
+#include "discovery_impl.h"
+
+#include "discovery_shm.h"
+#include "discovery_shmWatcher.h"
+
+#include "endpoint_discovery_poller.h"
+
+#define DEFAULT_SERVER_IP   "127.0.0.1"
+#define DEFAULT_SERVER_PORT "9999"
+#define DEFAULT_SERVER_PATH "/org.apache.celix.discovery.shm"
+#define DEFAULT_POLL_ENDPOINTS "http://localhost:9999/org.apache.celix.discovery.shm"
+
+#define MAX_ROOTNODE_LENGTH		 64
+#define MAX_LOCALNODE_LENGTH	256
+
+
+struct shm_watcher {
+    shmData_pt shmData;
+    celix_thread_t watcherThread;
+    celix_thread_mutex_t watcherLock;
+
+    volatile bool running;
+};
+
+// note that the rootNode shouldn't have a leading slash
+static celix_status_t discoveryShmWatcher_getRootPath(char* rootNode) {
+    celix_status_t status = CELIX_SUCCESS;
+
+    strcpy(rootNode, "discovery");
+
+    return status;
+}
+
+static celix_status_t discoveryShmWatcher_getLocalNodePath(bundle_context_pt context, char* localNodePath) {
+    celix_status_t status;
+    char rootPath[MAX_ROOTNODE_LENGTH];
+    const char* uuid = NULL;
+
+    status = discoveryShmWatcher_getRootPath(&rootPath[0]);
+
+    if (status == CELIX_SUCCESS) {
+        status = bundleContext_getProperty(context, OSGI_FRAMEWORK_FRAMEWORK_UUID, &uuid);
+    }
+
+    if (status == CELIX_SUCCESS) {
+        if (rootPath[strlen(&rootPath[0]) - 1] == '/') {
+            snprintf(localNodePath, MAX_LOCALNODE_LENGTH, "%s%s", &rootPath[0], uuid);
+        } else {
+            snprintf(localNodePath, MAX_LOCALNODE_LENGTH, "%s/%s", &rootPath[0], uuid);
+        }
+    }
+
+    return status;
+}
+
+/* retrieves all endpoints from shm and syncs them with the ones already available */
+static celix_status_t discoveryShmWatcher_syncEndpoints(discovery_pt discovery) {
+    celix_status_t status = CELIX_SUCCESS;
+    shm_watcher_pt watcher = discovery->pImpl->watcher;
+    char** shmKeyArr = calloc(SHM_DATA_MAX_ENTRIES, sizeof(*shmKeyArr));
+    array_list_pt registeredKeyArr = NULL;
+
+    int i, j, shmSize;
+
+    for (i = 0; i < SHM_DATA_MAX_ENTRIES; i++) {
+        shmKeyArr[i] = calloc(SHM_ENTRY_MAX_KEY_LENGTH, sizeof(*shmKeyArr[i]));
+    }
+
+    arrayList_create(&registeredKeyArr);
+
+    // get all urls available in shm
+    discoveryShm_getKeys(watcher->shmData, shmKeyArr, &shmSize);
+
+    // get all locally registered endpoints
+    endpointDiscoveryPoller_getDiscoveryEndpoints(discovery->poller, registeredKeyArr);
+
+    // add discovery points which are in shm, but not local yet
+    for (i = 0; i < shmSize; i++) {
+        char url[SHM_ENTRY_MAX_VALUE_LENGTH];
+
+        if (discoveryShm_get(watcher->shmData, shmKeyArr[i], &url[0]) == CELIX_SUCCESS) {
+            bool elementFound = false;
+
+            for (j = 0; j < arrayList_size(registeredKeyArr) && elementFound == false; j++) {
+
+                if (strcmp(url, (char*) arrayList_get(registeredKeyArr, j)) == 0) {
+                    free(arrayList_remove(registeredKeyArr, j));
+                    elementFound = true;
+                }
+            }
+
+            if (elementFound == false) {
+                endpointDiscoveryPoller_addDiscoveryEndpoint(discovery->poller, url);
+            }
+        }
+    }
+
+    // remove those which are not in shm
+    for (i = 0; i < arrayList_size(registeredKeyArr); i++) {
+        char* regUrl = arrayList_get(registeredKeyArr, i);
+
+        if (regUrl != NULL) {
+            endpointDiscoveryPoller_removeDiscoveryEndpoint(discovery->poller, regUrl);
+        }
+    }
+
+    for (i = 0; i < SHM_DATA_MAX_ENTRIES; i++) {
+        free(shmKeyArr[i]);
+    }
+
+    free(shmKeyArr);
+
+    for (j = 0; j < arrayList_size(registeredKeyArr); j++) {
+        free(arrayList_get(registeredKeyArr, j));
+    }
+
+    arrayList_destroy(registeredKeyArr);
+
+    return status;
+}
+
+static void* discoveryShmWatcher_run(void* data) {
+    discovery_pt discovery = (discovery_pt) data;
+    shm_watcher_pt watcher = discovery->pImpl->watcher;
+    char localNodePath[MAX_LOCALNODE_LENGTH];
+    char url[MAX_LOCALNODE_LENGTH];
+
+    if (discoveryShmWatcher_getLocalNodePath(discovery->context, &localNodePath[0]) != CELIX_SUCCESS) {
+        logHelper_log(discovery->loghelper, OSGI_LOGSERVICE_WARNING, "Cannot retrieve local discovery path.");
+    }
+
+    if (endpointDiscoveryServer_getUrl(discovery->server, &url[0]) != CELIX_SUCCESS) {
+        snprintf(url, MAX_LOCALNODE_LENGTH, "http://%s:%s/%s", DEFAULT_SERVER_IP, DEFAULT_SERVER_PORT, DEFAULT_SERVER_PATH);
+    }
+
+    while (watcher->running) {
+        // register own framework
+        if (discoveryShm_set(watcher->shmData, localNodePath, url) != CELIX_SUCCESS) {
+            logHelper_log(discovery->loghelper, OSGI_LOGSERVICE_WARNING, "Cannot set local discovery registration.");
+        }
+
+        discoveryShmWatcher_syncEndpoints(discovery);
+        sleep(5);
+    }
+
+    return NULL;
+}
+
+celix_status_t discoveryShmWatcher_create(discovery_pt discovery) {
+    celix_status_t status = CELIX_SUCCESS;
+    shm_watcher_pt watcher = NULL;
+
+    watcher = calloc(1, sizeof(*watcher));
+
+    if (!watcher) {
+        status = CELIX_ENOMEM;
+    } else {
+        status = discoveryShm_attach(&(watcher->shmData));
+
+        if (status != CELIX_SUCCESS) {
+            logHelper_log(discovery->loghelper, OSGI_LOGSERVICE_DEBUG, "Attaching to Shared Memory Failed. Trying to create.");
+
+            status = discoveryShm_create(&(watcher->shmData));
+
+            if (status != CELIX_SUCCESS) {
+                logHelper_log(discovery->loghelper, OSGI_LOGSERVICE_ERROR, "Failed to create Shared Memory Segment.");
+            }
+        }
+
+        if (status == CELIX_SUCCESS) {
+            discovery->pImpl->watcher = watcher;
+        }
+        else{
+        	discovery->pImpl->watcher = NULL;
+        	free(watcher);
+        }
+
+    }
+
+    if (status == CELIX_SUCCESS) {
+        status += celixThreadMutex_create(&watcher->watcherLock, NULL);
+        status += celixThreadMutex_lock(&watcher->watcherLock);
+        watcher->running = true;
+        status += celixThread_create(&watcher->watcherThread, NULL, discoveryShmWatcher_run, discovery);
+        status += celixThreadMutex_unlock(&watcher->watcherLock);
+    }
+
+    return status;
+}
+
+celix_status_t discoveryShmWatcher_destroy(discovery_pt discovery) {
+    celix_status_t status;
+    shm_watcher_pt watcher = discovery->pImpl->watcher;
+    char localNodePath[MAX_LOCALNODE_LENGTH];
+
+    celixThreadMutex_lock(&watcher->watcherLock);
+    watcher->running = false;
+    celixThreadMutex_unlock(&watcher->watcherLock);
+
+    celixThread_join(watcher->watcherThread, NULL);
+
+    // remove own framework
+    status = discoveryShmWatcher_getLocalNodePath(discovery->context, &localNodePath[0]);
+
+    if (status == CELIX_SUCCESS) {
+        status = discoveryShm_remove(watcher->shmData, localNodePath);
+    }
+
+    if (status == CELIX_SUCCESS) {
+        discoveryShm_detach(watcher->shmData);
+        free(watcher);
+    }
+    else {
+        logHelper_log(discovery->loghelper, OSGI_LOGSERVICE_WARNING, "Cannot remove local discovery registration.");
+    }
+
+
+    return status;
+}
+

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/discovery_shm/src/discovery_shmWatcher.h
----------------------------------------------------------------------
diff --git a/bundles/remote_services/discovery_shm/src/discovery_shmWatcher.h b/bundles/remote_services/discovery_shm/src/discovery_shmWatcher.h
new file mode 100644
index 0000000..4e7e1d5
--- /dev/null
+++ b/bundles/remote_services/discovery_shm/src/discovery_shmWatcher.h
@@ -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.
+ */
+/*
+ * shm_watcher.h
+ *
+ * \date       30 Sep 2014
+ * \author     <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
+ * \copyright  Apache License, Version 2.0
+ */
+
+#ifndef DISCOVERY_SHM_WATCHER_H_
+#define DISCOVERY_SHM_WATCHER_H_
+
+#include "celix_errno.h"
+#include "discovery.h"
+#include "endpoint_discovery_poller.h"
+
+typedef struct shm_watcher shm_watcher_t;
+typedef struct shm_watcher *shm_watcher_pt;
+
+celix_status_t discoveryShmWatcher_create(discovery_pt discovery);
+celix_status_t discoveryShmWatcher_destroy(discovery_pt discovery);
+
+
+#endif /* DISCOVERY_SHM_WATCHER_H_ */

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/examples/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/bundles/remote_services/examples/CMakeLists.txt b/bundles/remote_services/examples/CMakeLists.txt
new file mode 100644
index 0000000..0f8e7a9
--- /dev/null
+++ b/bundles/remote_services/examples/CMakeLists.txt
@@ -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.
+
+celix_subproject(RSA_EXAMPLES "Option to enable building the RSA examples" ON DEPS LAUNCHER shell_tui log_writer RSA_TOPOLOGY_MANAGER)
+if (RSA_EXAMPLES)
+    add_subdirectory(calculator_api)
+    add_subdirectory(calculator_service)
+    add_subdirectory(calculator_shell)
+
+
+#    TODO refactor shm remote service admin to use dfi
+#    if (BUILD_RSA_REMOTE_SERVICE_ADMIN_SHM AND BUILD_RSA_DISCOVERY_SHM)
+#        add_celix_container(remote-services-shm
+#            NAME "server"
+#            GROUP "remote-services/remote-services-shm"
+#            BUNDLES discovery_shm topology_manager remote_service_admin_shm calculator shell shell_tui log_service log_writer
+#        )
+#        celix_container_bundles_dir(remote-services-shm DIR_NAME "endpoints"
+#            BUNDLES org.apache.celix.calc.api.Calculator_endpoint
+#        )
+#
+#        add_celix_container(remote-services-shm-client
+#            NAME "client"
+#            GROUP "remote-services/remote-services-shm"
+#            BUNDLES topology_manager remote_service_admin_shm shell shell_tui log_service log_writer calculator_shell discovery_shm
+#        )
+#        celix_container_bundles_dir(remote-services-shm-client DIR_NAME "endpoints"
+#            BUNDLES org.apache.celix.calc.api.Calculator_proxy
+#        )
+#    endif ()
+
+
+    if (BUILD_RSA_DISCOVERY_ETCD AND BUILD_RSA_REMOTE_SERVICE_ADMIN_DFI)
+        add_celix_container(remote-services-dfi
+            NAME "server"
+            GROUP "remote-services/remote-services-dfi"
+            BUNDLES Celix::rsa_discovery_etcd Celix::rsa_topology_manager Celix::rsa_dfi calculator Celix::shell Celix::shell_tui Celix::log_service Celix::log_writer_stdout
+        )
+
+        add_celix_container("remote-services-dfi-client"
+            NAME "client"
+            GROUP "remote-services/remote-services-dfi"
+            BUNDLES Celix::rsa_topology_manager Celix::rsa_dfi Celix::shell Celix::shell_tui Celix::log_service Celix::log_writer_stdout calculator_shell Celix::rsa_discovery_etcd
+        )
+    endif ()
+endif (RSA_EXAMPLES)
+
+

http://git-wip-us.apache.org/repos/asf/celix/blob/3bce889b/bundles/remote_services/examples/calculator_api/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/bundles/remote_services/examples/calculator_api/CMakeLists.txt b/bundles/remote_services/examples/calculator_api/CMakeLists.txt
new file mode 100644
index 0000000..76acfa7
--- /dev/null
+++ b/bundles/remote_services/examples/calculator_api/CMakeLists.txt
@@ -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.
+
+add_library(calculator_api INTERFACE)
+target_include_directories(calculator_api INTERFACE include)
+set_target_properties(calculator_api PROPERTIES
+    "INTERFACE_CALCULATOR_DESCRIPTOR"
+    "${CMAKE_CURRENT_LIST_DIR}/include/org.apache.celix.calc.api.Calculator2.descriptor")
\ No newline at end of file