summaryrefslogtreecommitdiffstats
path: root/java/sca/modules/implementation-osgi-runtime/src
diff options
context:
space:
mode:
authorrfeng <rfeng@13f79535-47bb-0310-9956-ffa450edef68>2009-06-24 23:34:57 +0000
committerrfeng <rfeng@13f79535-47bb-0310-9956-ffa450edef68>2009-06-24 23:34:57 +0000
commitc5ed6e070ae8f3dd7a48c3e0d74cdefe6895b926 (patch)
tree01d169a1afa9727cfff93acaf9767103e8b2ab0c /java/sca/modules/implementation-osgi-runtime/src
parent9860bae75651b7c31cb0a7f4a66bf702712a10db (diff)
Start to add discovery service for distributed OSGi over SCA
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@788210 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java/sca/modules/implementation-osgi-runtime/src')
-rw-r--r--java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/AbstractDiscoveryService.java323
-rw-r--r--java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveryActivator.java54
-rw-r--r--java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DomainDiscoveryService.java114
-rw-r--r--java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointNotification.java67
-rw-r--r--java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointPublication.java45
-rw-r--r--java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/LocalDiscoveryService.java108
-rw-r--r--java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/ServiceEndpointDescriptionImpl.java135
-rw-r--r--java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/DiscoveredServiceNotification.java107
-rw-r--r--java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/DiscoveredServiceTracker.java81
-rw-r--r--java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/Discovery.java66
-rw-r--r--java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/ServiceEndpointDescription.java128
-rw-r--r--java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/ServicePublication.java147
12 files changed, 1375 insertions, 0 deletions
diff --git a/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/AbstractDiscoveryService.java b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/AbstractDiscoveryService.java
new file mode 100644
index 0000000000..612e10e770
--- /dev/null
+++ b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/AbstractDiscoveryService.java
@@ -0,0 +1,323 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.tuscany.sca.dosgi.discovery;
+
+import static org.osgi.service.discovery.DiscoveredServiceNotification.AVAILABLE;
+import static org.osgi.service.discovery.DiscoveredServiceTracker.FILTER_MATCH_CRITERIA;
+import static org.osgi.service.discovery.DiscoveredServiceTracker.INTERFACE_MATCH_CRITERIA;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.discovery.DiscoveredServiceTracker;
+import org.osgi.service.discovery.Discovery;
+import org.osgi.service.discovery.ServiceEndpointDescription;
+import org.osgi.service.discovery.ServicePublication;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ *
+ */
+public abstract class AbstractDiscoveryService implements Discovery {
+ private final static Logger logger = Logger.getLogger(AbstractDiscoveryService.class.getName());
+
+ protected BundleContext context;
+ private Map<String, List<DiscoveredServiceTracker>> filtersToTrackers =
+ new HashMap<String, List<DiscoveredServiceTracker>>();
+ private Map<String, List<DiscoveredServiceTracker>> interfacesToTrackers =
+ new HashMap<String, List<DiscoveredServiceTracker>>();
+ // 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
+ protected Map<ServiceEndpointDescription, Bundle> servicesInfo =
+ new ConcurrentHashMap<ServiceEndpointDescription, Bundle>();
+ private Map<DiscoveredServiceTracker, Collection<String>> trackersToFilters =
+ new HashMap<DiscoveredServiceTracker, Collection<String>>();
+ private Map<DiscoveredServiceTracker, Collection<String>> trackersToInterfaces =
+ new HashMap<DiscoveredServiceTracker, Collection<String>>();
+ private ServiceTracker trackerTracker;
+
+ public AbstractDiscoveryService(BundleContext context) {
+ super();
+ this.context = context;
+
+ // track the registration of DiscoveredServiceTrackers
+ trackerTracker = new ServiceTracker(context, DiscoveredServiceTracker.class.getName(), null) {
+ public Object addingService(ServiceReference reference) {
+ Object result = super.addingService(reference);
+ cacheTracker(reference, result);
+ return result;
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ super.modifiedService(reference, service);
+ updateTracker(reference, service);
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ super.removedService(reference, service);
+ clearTracker(service);
+ }
+ };
+
+ trackerTracker.open();
+
+ }
+
+ public void stop() {
+ trackerTracker.close();
+ }
+
+ protected ExtensionPointRegistry getExtensionPointRegistry() {
+ ServiceTracker tracker = new ServiceTracker(context, ExtensionPointRegistry.class.getName(), null);
+ tracker.open();
+ // tracker.waitForService(1000);
+ ExtensionPointRegistry extensionPointRegistry = (ExtensionPointRegistry) tracker.getService();
+ tracker.close();
+ return extensionPointRegistry;
+ }
+
+ private synchronized void cacheTracker(ServiceReference reference, Object service) {
+ if (service instanceof DiscoveredServiceTracker) {
+ DiscoveredServiceTracker tracker = (DiscoveredServiceTracker)service;
+ Collection<String> interfaces =
+ addTracker(reference, tracker, INTERFACE_MATCH_CRITERIA, interfacesToTrackers, trackersToInterfaces);
+ Collection<String> filters =
+ addTracker(reference, tracker, FILTER_MATCH_CRITERIA, filtersToTrackers, trackersToFilters);
+
+ triggerCallbacks(null, interfaces, tracker, false);
+ triggerCallbacks(null, filters, tracker, true);
+ }
+ }
+
+ private synchronized void clearTracker(Object service) {
+ if (service instanceof DiscoveredServiceTracker) {
+ removeTracker((DiscoveredServiceTracker)service, interfacesToTrackers, trackersToInterfaces);
+ removeTracker((DiscoveredServiceTracker)service, filtersToTrackers, trackersToFilters);
+ }
+ }
+
+ private synchronized void updateTracker(ServiceReference reference, Object service) {
+ if (service instanceof DiscoveredServiceTracker) {
+ DiscoveredServiceTracker tracker = (DiscoveredServiceTracker)service;
+ logger.info("updating tracker: " + tracker);
+ Collection<String> oldInterfaces = removeTracker(tracker, interfacesToTrackers, trackersToInterfaces);
+ Collection<String> oldFilters = removeTracker(tracker, filtersToTrackers, trackersToFilters);
+
+ Collection<String> newInterfaces =
+ addTracker(reference, tracker, INTERFACE_MATCH_CRITERIA, interfacesToTrackers, trackersToInterfaces);
+ Collection<String> newFilters =
+ addTracker(reference, tracker, FILTER_MATCH_CRITERIA, filtersToTrackers, trackersToFilters);
+
+ triggerCallbacks(oldInterfaces, newInterfaces, tracker, false);
+ triggerCallbacks(oldFilters, newFilters, tracker, true);
+ }
+ }
+
+ private void triggerCallbacks(Collection<String> oldInterest,
+ Collection<String> newInterest,
+ DiscoveredServiceTracker tracker,
+ boolean isFilter) {
+ // compute delta between old & new interfaces/filters and
+ // trigger callbacks for any entries in servicesInfo that
+ // match any *additional* interface/filters
+ Collection<String> deltaInterest = new ArrayList<String>();
+ if (!isEmpty(newInterest)) {
+ if (isEmpty(oldInterest)) {
+ deltaInterest.addAll(newInterest);
+ } else {
+ Iterator<String> i = newInterest.iterator();
+ while (i.hasNext()) {
+ String next = (String)i.next();
+ if (!oldInterest.contains(next)) {
+ deltaInterest.add(next);
+ }
+ }
+ }
+ }
+
+ if (servicesInfo.size() > 0) {
+ logger.info("search for matches to trigger callbacks with delta: " + deltaInterest);
+ } else {
+ logger.info("nothing to search for matches to trigger callbacks with delta: " + deltaInterest);
+ }
+ Iterator<String> i = deltaInterest.iterator();
+ while (i.hasNext()) {
+ String next = i.next();
+ for (ServiceEndpointDescription sd : servicesInfo.keySet()) {
+ triggerCallbacks(tracker, next, isFilter, sd, AVAILABLE);
+ }
+ }
+ }
+
+ private void triggerCallbacks(DiscoveredServiceTracker tracker,
+ String toMatch,
+ boolean isFilter,
+ ServiceEndpointDescription sd,
+ int type) {
+ logger.fine("check if string: " + toMatch
+ + (isFilter ? " matches " : " contained by ")
+ + sd.getProvidedInterfaces());
+
+ EndpointNotification notification =
+ isFilter ? (filterMatches(toMatch, sd) ? new EndpointNotification(sd, true, toMatch, type) : null) : (sd
+ .getProvidedInterfaces().contains(toMatch) ? new EndpointNotification(sd, false, toMatch, type) : null);
+
+ if (notification != null) {
+ tracker.serviceChanged(notification);
+ }
+ }
+
+ private boolean filterMatches(String filterValue, ServiceEndpointDescription sd) {
+ Filter filter = createFilter(filterValue);
+ return filter != null ? filter.match(getServiceProperties(null, sd)) : false;
+ }
+
+ private Filter createFilter(String filterValue) {
+
+ if (filterValue == null) {
+ return null;
+ }
+
+ try {
+ return context.createFilter(filterValue);
+ } catch (InvalidSyntaxException ex) {
+ System.out.println("Invalid filter expression " + filterValue);
+ } catch (Exception ex) {
+ System.out.println("Problem creating a Filter from " + filterValue);
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Dictionary<String, Object> getServiceProperties(String interfaceName, ServiceEndpointDescription sd) {
+ Dictionary<String, Object> d = new Hashtable<String, Object>(sd.getProperties());
+
+ String[] interfaceNames = getProvidedInterfaces(sd, interfaceName);
+ if (interfaceNames != null) {
+ d.put(INTERFACE_MATCH_CRITERIA, interfaceNames);
+ }
+ return d;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static String[] getProvidedInterfaces(ServiceEndpointDescription sd, String interfaceName) {
+
+ Collection<String> interfaceNames = sd.getProvidedInterfaces();
+ if (interfaceName == null) {
+ return null;
+ }
+
+ Iterator<String> iNames = interfaceNames.iterator();
+ while (iNames.hasNext()) {
+ if (iNames.next().equals(interfaceName)) {
+ return new String[] {interfaceName};
+ }
+ }
+ return null;
+ }
+
+ static Collection<String> removeTracker(DiscoveredServiceTracker tracker,
+ Map<String, List<DiscoveredServiceTracker>> forwardMap,
+ Map<DiscoveredServiceTracker, Collection<String>> reverseMap) {
+ Collection<String> collection = reverseMap.get(tracker);
+ if (!isEmpty(collection)) {
+ reverseMap.remove(tracker);
+ Iterator<String> i = collection.iterator();
+ while (i.hasNext()) {
+ String element = i.next();
+ if (forwardMap.containsKey(element)) {
+ forwardMap.get(element).remove(tracker);
+ } else {
+ // if the element wasn't on the forwardmap, its a new element and
+ // shouldn't be returned as part of the collection of old ones
+ i.remove();
+ }
+ }
+ }
+ return collection;
+ }
+
+ private static boolean isEmpty(Collection<?> c) {
+ return c == null || c.isEmpty();
+ }
+
+ @SuppressWarnings("unchecked")
+ static Collection<String> addTracker(ServiceReference reference,
+ DiscoveredServiceTracker tracker,
+ String property,
+ Map<String, List<DiscoveredServiceTracker>> forwardMap,
+ Map<DiscoveredServiceTracker, Collection<String>> reverseMap) {
+ Collection<String> collection = (Collection<String>)reference.getProperty(property);
+ logger.info("adding tracker: " + tracker
+ + " collection: "
+ + collection
+ + " registered against prop: "
+ + property);
+ if (!isEmpty(collection)) {
+ reverseMap.put(tracker, new ArrayList<String>(collection));
+ Iterator<String> i = collection.iterator();
+ while (i.hasNext()) {
+ String element = i.next();
+ if (forwardMap.containsKey(element)) {
+ forwardMap.get(element).add(tracker);
+ } else {
+ List<DiscoveredServiceTracker> trackerList = new ArrayList<DiscoveredServiceTracker>();
+ trackerList.add(tracker);
+ forwardMap.put(element, trackerList);
+ }
+ }
+ }
+ return collection;
+ }
+
+ protected void discoveredServiceChanged(ServiceEndpointDescription sd, int type) {
+ for (Map.Entry<DiscoveredServiceTracker, Collection<String>> entry : trackersToInterfaces.entrySet()) {
+ for (String match : entry.getValue()) {
+ triggerCallbacks(entry.getKey(), match, false, sd, type);
+ }
+ }
+ for (Map.Entry<DiscoveredServiceTracker, Collection<String>> entry : trackersToFilters.entrySet()) {
+ for (String match : entry.getValue()) {
+ triggerCallbacks(entry.getKey(), match, true, sd, type);
+ }
+ }
+ }
+
+ protected void localServicePublished(ServiceReference ref) {
+ EndpointPublication publication = new EndpointPublication(ref);
+ Hashtable props = new Hashtable();
+ ref.getBundle().getBundleContext().registerService(ServicePublication.class.getName(), publication, props);
+ }
+
+}
diff --git a/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveryActivator.java b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveryActivator.java
new file mode 100644
index 0000000000..7dd605dd8e
--- /dev/null
+++ b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveryActivator.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.tuscany.sca.dosgi.discovery;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.discovery.Discovery;
+
+public class DiscoveryActivator implements BundleActivator {
+ private List<AbstractDiscoveryService> discoveryServices = new ArrayList<AbstractDiscoveryService>();
+ private List<ServiceRegistration> discoveryServiceRegistrations = new ArrayList<ServiceRegistration>();
+
+ public void start(BundleContext context) {
+ discoveryServices.add(new LocalDiscoveryService(context));
+
+ discoveryServices.add(new DomainDiscoveryService(context));
+
+ for (AbstractDiscoveryService service : discoveryServices) {
+ ServiceRegistration registration =
+ context.registerService(Discovery.class.getName(), service, new Hashtable<String, Object>());
+ discoveryServiceRegistrations.add(registration);
+ }
+ }
+
+ public void stop(BundleContext context) {
+ for (ServiceRegistration registration : discoveryServiceRegistrations) {
+ registration.unregister();
+ }
+ for (AbstractDiscoveryService service : discoveryServices) {
+ service.stop();
+ }
+ }
+}
diff --git a/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DomainDiscoveryService.java b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DomainDiscoveryService.java
new file mode 100644
index 0000000000..40094974ba
--- /dev/null
+++ b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DomainDiscoveryService.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.dosgi.discovery;
+
+import static org.osgi.service.discovery.DiscoveredServiceNotification.AVAILABLE;
+import static org.osgi.service.discovery.DiscoveredServiceNotification.UNAVAILABLE;
+
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.assembly.Implementation;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.UtilityExtensionPoint;
+import org.apache.tuscany.sca.implementation.osgi.OSGiImplementation;
+import org.apache.tuscany.sca.interfacedef.Interface;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterface;
+import org.apache.tuscany.sca.runtime.EndpointListener;
+import org.apache.tuscany.sca.runtime.EndpointRegistry;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.discovery.ServiceEndpointDescription;
+
+/**
+ * Discovery service based on the distributed SCA domain
+ */
+public class DomainDiscoveryService extends AbstractDiscoveryService implements EndpointListener {
+ private EndpointRegistry endpointRegistry;
+
+ public DomainDiscoveryService(BundleContext context) {
+ super(context);
+
+ ExtensionPointRegistry registry = getExtensionPointRegistry();
+ this.endpointRegistry =
+ registry.getExtensionPoint(UtilityExtensionPoint.class).getUtility(EndpointRegistry.class);
+ this.endpointRegistry.addListener(this);
+ }
+
+ public void endpointAdded(Endpoint endpoint) {
+ Implementation impl = endpoint.getComponent().getImplementation();
+ if (!(impl instanceof OSGiImplementation)) {
+ return;
+ }
+
+ OSGiImplementation osgiImpl = (OSGiImplementation)impl;
+ BundleContext bundleContext = osgiImpl.getBundle().getBundleContext();
+
+ boolean local = true;
+ if (local) {
+
+ Interface intf = endpoint.getService().getInterfaceContract().getInterface();
+ JavaInterface javaInterface = (JavaInterface)intf;
+ // String filter = getOSGiFilter(provider.getOSGiProperties(service));
+ // FIXME: What is the filter?
+ String filter = "(!(sca.reference=*))";
+ // "(sca.service=" + component.getURI() + "#service-name\\(" + service.getName() + "\\))";
+ ServiceReference ref = null;
+ try {
+ ref = bundleContext.getServiceReferences(javaInterface.getName(), filter)[0];
+ } catch (InvalidSyntaxException e) {
+ // Ignore
+ }
+ if (ref != null) {
+ localServicePublished(ref);
+ }
+ } else {
+ // Remote endpoints
+ ServiceEndpointDescription description = getServiceEndpointDescription(endpoint);
+ discoveredServiceChanged(description, AVAILABLE);
+ }
+ }
+
+ private boolean isLocal(Endpoint endpoint) {
+ // FIXME: To be implemented
+ return true;
+ }
+
+ public void endpointRemoved(Endpoint endpoint) {
+ if (isLocal(endpoint)) {
+ // unregister the ServicePublication here
+ } else {
+ // Remote endpoints
+ ServiceEndpointDescription description = getServiceEndpointDescription(endpoint);
+ discoveredServiceChanged(description, UNAVAILABLE);
+ }
+ }
+
+ public void endpointUpdated(Endpoint oldEndpoint, Endpoint newEndpoint) {
+ }
+
+ public void stop() {
+ endpointRegistry.removeListener(this);
+ super.stop();
+ }
+
+ private ServiceEndpointDescription getServiceEndpointDescription(Endpoint endpoint) {
+ return null;
+ }
+}
diff --git a/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointNotification.java b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointNotification.java
new file mode 100644
index 0000000000..659a683089
--- /dev/null
+++ b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointNotification.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.tuscany.sca.dosgi.discovery;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+
+import org.osgi.service.discovery.DiscoveredServiceNotification;
+import org.osgi.service.discovery.ServiceEndpointDescription;
+
+class EndpointNotification implements DiscoveredServiceNotification {
+
+ private ServiceEndpointDescription discription;
+ private Collection<String> interfaces;
+ private Collection<String> filters;
+ private int type;
+
+ EndpointNotification(ServiceEndpointDescription sd, boolean isFilter, String match, int type) {
+ this.discription = sd;
+ if (isFilter) {
+ filters = new ArrayList<String>();
+ filters.add(match);
+ interfaces = Collections.emptySet();
+ } else {
+ interfaces = new HashSet<String>();
+ interfaces.add(match);
+ filters = Collections.emptyList();
+ }
+
+ this.type = type;
+ }
+
+ public ServiceEndpointDescription getServiceEndpointDescription() {
+ return discription;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public Collection<String> getInterfaces() {
+ return interfaces;
+ }
+
+ public Collection<String> getFilters() {
+ return filters;
+ }
+}
diff --git a/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointPublication.java b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointPublication.java
new file mode 100644
index 0000000000..1a038ba41a
--- /dev/null
+++ b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointPublication.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.tuscany.sca.dosgi.discovery;
+
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.discovery.ServicePublication;
+
+/**
+ * Publication of an SCA endpoint
+ */
+public class EndpointPublication implements ServicePublication {
+ private ServiceReference reference;
+
+ /**
+ * Create a publication for the endpoint
+ * @param reference The OSGi service reference for the given endpoint. The SCA endpoint
+ * is pointing to a local service in the OSGi service registry
+ */
+ public EndpointPublication(ServiceReference reference) {
+ super();
+ this.reference = reference;
+ }
+
+ public ServiceReference getReference() {
+ return reference;
+ }
+
+}
diff --git a/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/LocalDiscoveryService.java b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/LocalDiscoveryService.java
new file mode 100644
index 0000000000..d985fe708c
--- /dev/null
+++ b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/LocalDiscoveryService.java
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.tuscany.sca.dosgi.discovery;
+
+import static org.osgi.framework.Bundle.ACTIVE;
+import static org.osgi.framework.BundleEvent.STARTED;
+import static org.osgi.framework.BundleEvent.STOPPING;
+import static org.osgi.service.discovery.DiscoveredServiceNotification.AVAILABLE;
+import static org.osgi.service.discovery.DiscoveredServiceNotification.UNAVAILABLE;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.osgi.service.discovery.ServiceEndpointDescription;
+
+public class LocalDiscoveryService extends AbstractDiscoveryService implements BundleListener {
+ private ExtensionPointRegistry registry;
+
+ public LocalDiscoveryService(BundleContext context) {
+ super(context);
+ context.addBundleListener(this);
+ this.registry = getExtensionPointRegistry();
+ processExistingBundles();
+ }
+
+ public void bundleChanged(BundleEvent event) {
+ switch (event.getType()) {
+ case STARTED:
+ discover(event.getBundle());
+ break;
+ case STOPPING:
+ removeServicesDeclaredInBundle(event.getBundle());
+ break;
+ }
+ }
+
+ private void processExistingBundles() {
+ Bundle[] bundles = context.getBundles();
+ if (bundles == null) {
+ return;
+ }
+
+ for (Bundle b : bundles) {
+ if (b.getState() == ACTIVE) {
+ discover(b);
+ }
+ }
+ }
+
+ private void discover(Bundle b) {
+ // TODO: Use SCA contribution to load the service discription files
+ List<ServiceEndpointDescription> refs = Collections.emptyList();
+ for (ServiceEndpointDescription sed : refs) {
+ servicesInfo.put(sed, b);
+ serviceDescriptionAdded(sed);
+ }
+ throw new RuntimeException("To be implemented");
+ }
+
+ private void removeServicesDeclaredInBundle(Bundle bundle) {
+ for (Iterator<Map.Entry<ServiceEndpointDescription, Bundle>> i = servicesInfo.entrySet().iterator(); i
+ .hasNext();) {
+ Entry<ServiceEndpointDescription, Bundle> entry = i.next();
+ if (entry.getValue().equals(bundle)) {
+ serviceDescriptionRemoved(entry.getKey());
+ i.remove();
+ }
+ }
+ }
+
+ private void serviceDescriptionAdded(ServiceEndpointDescription sd) {
+ discoveredServiceChanged(sd, AVAILABLE);
+ }
+
+ private void serviceDescriptionRemoved(ServiceEndpointDescription sd) {
+ discoveredServiceChanged(sd, UNAVAILABLE);
+ }
+
+ public void stop() {
+ context.removeBundleListener(this);
+ super.stop();
+ }
+
+}
diff --git a/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/ServiceEndpointDescriptionImpl.java b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/ServiceEndpointDescriptionImpl.java
new file mode 100644
index 0000000000..949405472f
--- /dev/null
+++ b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/apache/tuscany/sca/dosgi/discovery/ServiceEndpointDescriptionImpl.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.tuscany.sca.dosgi.discovery;
+
+import static org.osgi.service.discovery.ServicePublication.ENDPOINT_ID;
+import static org.osgi.service.discovery.ServicePublication.ENDPOINT_LOCATION;
+import static org.osgi.service.discovery.ServicePublication.SERVICE_INTERFACE_NAME;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import org.osgi.service.discovery.ServiceEndpointDescription;
+
+public class ServiceEndpointDescriptionImpl implements ServiceEndpointDescription {
+
+ private static final Logger logger = Logger.getLogger(ServiceEndpointDescriptionImpl.class.getName());
+
+ private Set<String> interfaces;
+ private Map<String, Object> properties;
+
+ public ServiceEndpointDescriptionImpl(Collection<String> interfaceNames) {
+ this(interfaceNames, Collections.<String, Object> singletonMap(SERVICE_INTERFACE_NAME,
+ interfaceNames));
+ }
+
+ public ServiceEndpointDescriptionImpl(Collection<String> interfaceNames, Map<String, Object> remoteProperties) {
+ this.interfaces = new HashSet<String>(interfaceNames);
+ this.properties = remoteProperties;
+ }
+
+ public ServiceEndpointDescriptionImpl(String interfaceName) {
+ this(Collections.singleton(interfaceName));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ServiceEndpointDescriptionImpl other = (ServiceEndpointDescriptionImpl)obj;
+ if (interfaces == null) {
+ if (other.interfaces != null)
+ return false;
+ } else if (!interfaces.equals(other.interfaces))
+ return false;
+ if (properties == null) {
+ if (other.properties != null)
+ return false;
+ } else if (!properties.equals(other.properties))
+ return false;
+ return true;
+ }
+
+ public String getEndpointID() {
+ Object val = properties.get(ENDPOINT_ID);
+ if (val == null) {
+ return null;
+ } else {
+ return val.toString();
+ }
+ }
+
+ public String getEndpointInterfaceName(String interfaceName) {
+ return interfaceName;
+ }
+
+ public URI getLocation() {
+ Object value = properties.get(ENDPOINT_LOCATION);
+ if (value == null) {
+ return null;
+ }
+
+ try {
+ return new URI(value.toString());
+ } catch (URISyntaxException ex) {
+ logger.warning("Service document URL is malformed : " + value.toString());
+ }
+
+ return null;
+ }
+
+ public Map<String, Object> getProperties() {
+ return Collections.unmodifiableMap(properties);
+ }
+
+ public Object getProperty(String key) {
+ return properties.get(key);
+ }
+
+ public Collection<String> getPropertyKeys() {
+ return getProperties().keySet();
+ }
+
+ public Collection<String> getProvidedInterfaces() {
+ return interfaces;
+ }
+
+ public String getVersion(String interfaceName) {
+ return "0.0";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((interfaces == null) ? 0 : interfaces.hashCode());
+ result = prime * result + ((properties == null) ? 0 : properties.hashCode());
+ return result;
+ }
+}
diff --git a/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/DiscoveredServiceNotification.java b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/DiscoveredServiceNotification.java
new file mode 100644
index 0000000000..57de9c7c9d
--- /dev/null
+++ b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/DiscoveredServiceNotification.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 2009). All Rights Reserved.
+ *
+ * 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.
+ */
+
+package org.osgi.service.discovery;
+
+import java.util.Collection;
+
+/**
+ * Interface for notification on discovered services.
+ * <p>
+ * <code>DiscoveredServiceNotification</code> objects are immutable.
+ *
+ * @Immutable
+ */
+public interface DiscoveredServiceNotification {
+
+ /**
+ * Notification indicating that a service matching the listening criteria
+ * has been discovered.
+ * <p>
+ * The value of <code>AVAILABLE</code> is 0x00000001.
+ */
+ public final static int AVAILABLE = 0x00000001;
+
+ /**
+ * Notification indicating that the properties of a previously discovered
+ * service have changed.
+ * <p>
+ * The value of <code>MODIFIED</code> is 0x00000002.
+ */
+ public final static int MODIFIED = 0x00000002;
+
+ /**
+ * Notification indicating that a previously discovered service is no longer
+ * known to <code>Discovery</code>.
+ * <p>
+ * The value of <code>UNAVAILABLE</code> is 0x00000004.
+ */
+ public final static int UNAVAILABLE = 0x00000004;
+
+ /**
+ * Notification indicating that the properties of a previously discovered
+ * service have changed and the new properties no longer match the
+ * listener's filter.
+ * <p>
+ * The value of <code>MODIFIED_ENDMATCH</code> is 0x00000008.
+ */
+ public final static int MODIFIED_ENDMATCH = 0x00000008;
+
+ /**
+ * Returns information currently known to <code>Discovery</code> regarding
+ * the service endpoint.
+ *
+ * @return metadata of the service <code>Discovery</code> notifies about. Is
+ * never <code>null</code>.
+ */
+ ServiceEndpointDescription getServiceEndpointDescription();
+
+ /**
+ * Returns the type of notification. The type values are:
+ * <ul>
+ * <li>{@link #AVAILABLE}</li>
+ * <li>{@link #MODIFIED}</li>
+ * <li>{@link #MODIFIED_ENDMATCH}</li>
+ * <li>{@link #UNAVAILABLE}</li>
+ * </ul>
+ *
+ * @return Type of notification regarding known service metadata.
+ */
+ int getType();
+
+ /**
+ * Returns interface name criteria of the {@link DiscoveredServiceTracker}
+ * object matching with the interfaces of the
+ * <code>ServiceEndpointDescription</code> and thus caused the notification.
+ *
+ * @return <code>Collection (&lt;? extends String&gt;)</code> of matching interface name criteria of the
+ * <code>DiscoveredServiceTracker</code> object being notified, or
+ * an empty collection if notification hasn't been caused by a
+ * matching interface name criteria.
+ */
+ Collection/* <? extends String> */getInterfaces();
+
+ /**
+ * Returns filters of the <code>DiscoveredServiceTracker</code> object
+ * matching with the properties of the
+ * <code>ServiceEndpointDescription</code> and thus caused the notification.
+ *
+ * @return <code>Collection (&lt;? extends String&gt;)</code> of matching filters of the <code>DiscoveredServiceTracker</code>
+ * object being notified, or an empty collection if notification
+ * hasn't been caused by a matching filter criteria.
+ */
+ Collection/* <? extends String> */getFilters();
+}
diff --git a/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/DiscoveredServiceTracker.java b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/DiscoveredServiceTracker.java
new file mode 100644
index 0000000000..efd158a2f5
--- /dev/null
+++ b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/DiscoveredServiceTracker.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 2009). All Rights Reserved.
+ *
+ * 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.
+ */
+
+package org.osgi.service.discovery;
+
+/**
+ * Interface of trackers for discovered remote services.
+ * <p>
+ * When a service implementing this interface is registered with the framework,
+ * then <code>Discovery</code> will notify it about remote services matching one
+ * of the provided criteria and will keep notifying it on changes of information
+ * known to Discovery regarding this services.
+ *
+ * <code>Discovery</code> may deliver notifications on discovered services to a
+ * <code>DiscoveredServiceTracker</code> out of order and may concurrently call
+ * and/or reenter a <code>DiscoveredServiceTracker</code>.
+ *
+ * @ThreadSafe
+ */
+public interface DiscoveredServiceTracker {
+
+ /**
+ * Optional ServiceRegistration property which contains service interfaces
+ * this tracker is interested in.
+ * <p>
+ * Value of this property is of type
+ * <code>Collection (&lt;? extends String&gt;)</code>. May be
+ * <code>null</code> or empty.
+ */
+ public static final String INTERFACE_MATCH_CRITERIA = "osgi.remote.discovery.interest.interfaces";
+
+ /**
+ * Optional ServiceRegistration property which contains filters for services
+ * this tracker is interested in.
+ * <p>
+ * Note that these filters need to take into account service publication
+ * properties which are not necessarily the same as properties under which a
+ * service is registered. See {@link ServicePublication} for some standard
+ * properties used to publish service metadata.
+ * <p>
+ * The following sample filter will make <code>Discovery</code> notify the
+ * <code>DiscoveredServiceTracker</code> about services providing interface
+ * 'my.company.foo' of version '1.0.1.3':
+ * <code>"(&amp;(service.interface=my.company.foo)(service.interface.version=my.company.foo|1.0.1.3))"</code>.
+ * <p>
+ * Value of this property is of type
+ * <code>Collection (&lt;? extends String&gt;)</code>. May be
+ * <code>null</code>. or empty
+ */
+ public static final String FILTER_MATCH_CRITERIA = "osgi.remote.discovery.interest.filters";
+
+ /**
+ * Receives notification that information known to <code>Discovery</code>
+ * regarding a remote service has changed.
+ * <p>
+ * The tracker is only notified about remote services which fulfill the
+ * matching criteria, either one of the interfaces or one of the filters,
+ * provided as properties of this service.
+ * <p>
+ * If multiple criteria match, then the tracker is notified about each of
+ * them. This can be done either by a single notification callback or by
+ * multiple subsequent ones.
+ *
+ * @param notification the <code>DiscoveredServiceNotification</code> object
+ * describing the change. Is never <code>null</code>.
+ */
+ void serviceChanged(DiscoveredServiceNotification notification);
+}
diff --git a/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/Discovery.java b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/Discovery.java
new file mode 100644
index 0000000000..cca766a032
--- /dev/null
+++ b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/Discovery.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 2009). All Rights Reserved.
+ *
+ * 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.
+ */
+
+package org.osgi.service.discovery;
+
+/**
+ * Every Discovery Provider registers a service implementing this interface.
+ * This service is registered with extra properties identified at the beginning
+ * of this interface to denote the name of the product providing Discovery
+ * functionality, its version, vendor, used protocols etc..
+ * <p>
+ * Discovery allows to publish services exposed for remote access as well as to
+ * search for remote services. Register a {@link ServicePublication} service in
+ * order to publish service metadata and or a {@link DiscoveredServiceTracker}
+ * service in order to search for remote services.
+ * <p>
+ * Discovery service implementations usually rely on some discovery protocols or
+ * other information distribution means.
+ *
+ * @ThreadSafe
+ */
+public interface Discovery {
+
+ /**
+ * ServiceRegistration property for the name of the Discovery product.
+ * <p>
+ * Value of this property is of type <code>String</code>.
+ */
+ static final String PRODUCT_NAME = "osgi.remote.discovery.product";
+
+ /**
+ * ServiceRegistration property for the version of the Discovery product.
+ * <p>
+ * Value of this property is of type <code>String</code>.
+ */
+ static final String PRODUCT_VERSION = "osgi.remote.discovery.product.version";
+
+ /**
+ * ServiceRegistration property for the Discovery product vendor name.
+ * <p>
+ * Value of this property is of type <code>String</code>.
+ */
+ static final String VENDOR_NAME = "osgi.remote.discovery.vendor";
+
+ /**
+ * ServiceRegistration property that lists the discovery protocols used by
+ * this Discovery service.
+ * <p>
+ * Value of this property is of type
+ * <code>Collection (&lt;? extends String&gt;)</code>.
+ */
+ static final String SUPPORTED_PROTOCOLS = "osgi.remote.discovery.supported_protocols";
+}
diff --git a/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/ServiceEndpointDescription.java b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/ServiceEndpointDescription.java
new file mode 100644
index 0000000000..e1f389ea78
--- /dev/null
+++ b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/ServiceEndpointDescription.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 2009). All Rights Reserved.
+ *
+ * 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.
+ */
+
+package org.osgi.service.discovery;
+
+import java.net.URI;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * This interface describes an endpoint of a service. This class can be
+ * considered as a wrapper around the property map of a published service and
+ * its endpoint. It provides an API to conveniently access the most important
+ * properties of the service.
+ * <p>
+ * <code>ServiceEndpointDescription</code> objects are immutable.
+ *
+ * @Immutable
+ */
+public interface ServiceEndpointDescription {
+
+ /**
+ * Returns the value of the property with key
+ * {@link ServicePublication#SERVICE_INTERFACE_NAME}.
+ *
+ * @return <code>Collection (&lt;? extends String&gt;)</code> of service
+ * interface names provided by the advertised service endpoint. The
+ * collection is never <code>null</code> or empty but contains at
+ * least one service interface.
+ */
+ Collection /* <? extends String> */getProvidedInterfaces();
+
+ /**
+ * Returns non-Java endpoint interface name associated with the given
+ * interface.
+ * <p>
+ * Value of the property with key
+ * {@link ServicePublication#ENDPOINT_INTERFACE_NAME} is used by this
+ * operation.
+ *
+ * @param interfaceName for which its non-Java endpoint interface name
+ * should be returned.
+ * @return non-Java endpoint interface name, or <code>null</code> if it
+ * hasn't been provided or if given interface name is
+ * <code>null</code>.
+ */
+ String getEndpointInterfaceName(String interfaceName);
+
+ /**
+ * Returns version of the given interface.
+ * <p>
+ * Value of the property with key
+ * {@link ServicePublication#SERVICE_INTERFACE_VERSION} is used by this
+ * operation.
+ *
+ * @param interfaceName for which its version should be returned.
+ * @return Version of given service interface, or <code>null</code> if it
+ * hasn't been provided or if given interface name is
+ * <code>null</code>.
+ */
+ String getVersion(String interfaceName);
+
+ /**
+ * Returns the value of the property with key
+ * {@link ServicePublication#ENDPOINT_LOCATION}.
+ *
+ * @return The url of the service location, or <code>null</code> if it
+ * hasn't been provided.
+ */
+ URI getLocation();
+
+ /**
+ * Returns the value of the property with key
+ * {@link ServicePublication#ENDPOINT_ID}.
+ *
+ * @return Unique id of service endpoint, or <code>null</code> if it hasn't
+ * been provided.
+ */
+ String getEndpointID();
+
+ /**
+ * Getter method for the property value of a given key.
+ *
+ * @param key Name of the property
+ * @return The property value, or <code>null</code> if none is found for the
+ * given key or if provided key is <code>null</code>.
+ */
+ Object getProperty(String key);
+
+ /**
+ * Returns all names of service endpoint properties.
+ *
+ * @return a <code>Collection (&lt;? extends String&gt;)</code> of property
+ * names available in the ServiceEndpointDescription. The collection
+ * is never <code>null</code> or empty but contains at least names
+ * of mandatory <code>ServicePublication</code> properties. Since
+ * <code>ServiceEndpointDescription</code> objects are immutable,
+ * the returned collection is also not going to be updated at a
+ * later point of time.
+ */
+ Collection/* <? extends String> */getPropertyKeys();
+
+ /**
+ * Returns all service endpoint properties.
+ *
+ * @return all properties of the service as a
+ * <code>Map (&lt;String, Object&gt;)</code>. The map is never
+ * <code>null</code> or empty but contains at least mandatory
+ * <code>ServicePublication</code> properties. Since
+ * <code>ServiceEndpointDescription</code> objects are immutable,
+ * the returned map is also not going to be updated at a later point
+ * of time.
+ */
+ Map/* <String, Object> */getProperties();
+}
diff --git a/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/ServicePublication.java b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/ServicePublication.java
new file mode 100644
index 0000000000..9b4cb4c905
--- /dev/null
+++ b/java/sca/modules/implementation-osgi-runtime/src/main/java/org/osgi/service/discovery/ServicePublication.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 2009). All Rights Reserved.
+ *
+ * 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.
+ */
+
+package org.osgi.service.discovery;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Register a service implementing the <code>ServicePublication</code> interface
+ * in order to publish metadata of a particular service (endpoint) via
+ * Discovery. Metadata which has to be published is given in form of properties
+ * at registration.
+ * <p>
+ * In order to update published service metadata, update the properties
+ * registered with the <code>ServicePublication</code> service. Depending on
+ * Discovery's implementation and underlying protocol it may result in an update
+ * or new re-publication of the service.
+ * <p>
+ * In order to unpublish the previously published service metadata, unregister
+ * the <code>ServicePublication</code> service.
+ * <p>
+ * Please note that providing the {@link #SERVICE_INTERFACE_NAME} property is
+ * mandatory when a <code>ServicePublication</code> service is registered. Note
+ * also that a Discovery implementation may require provision of additional
+ * properties, e.g. some of the standard properties defined below, or may make
+ * special use of them in case they are provided. For example an SLP-based
+ * Discovery might use the value provided with the {@link #ENDPOINT_LOCATION}
+ * property for construction of a SLP-URL used to publish the service.
+ * <p>
+ * Also important is that it's not guaranteed that after registering a
+ * <code>ServicePublication</code> object its service metadata is actually
+ * published. Beside the fact that at least one Discovery service has to be
+ * present, the provided properties have to be valid, e.g. shouldn't contain
+ * case variants of the same key name, and the actual publication via Discovery
+ * mechanisms has to succeed.
+ *
+ * @ThreadSafe
+ */
+public interface ServicePublication {
+
+ /**
+ * Mandatory ServiceRegistration property which contains a collection of
+ * full qualified interface names offered by the advertised service
+ * endpoint.
+ * <p>
+ * Value of this property is of type
+ * <code>Collection (&lt;? extends String&gt;)</code>.
+ */
+ public static final String SERVICE_INTERFACE_NAME = "osgi.remote.service.interfaces";
+
+ /**
+ * Optional ServiceRegistration property which contains a collection of
+ * interface names with their associated version attributes separated by
+ * {@link #SEPARATOR} e.g. ["my.company.foo|1.3.5", "my.company.zoo|2.3.5"].
+ * In case no version has been provided for an interface, Discovery may use
+ * the String-value of <code>org.osgi.framework.Version.emptyVersion</code>
+ * constant.
+ * <p>
+ * Value of this property is of type
+ * <code>Collection (&lt;? extends String&gt;)</code>, may be
+ * <code>null</code> or empty.
+ */
+ public static final String SERVICE_INTERFACE_VERSION = "osgi.remote.service.interfaces.version";
+
+ /**
+ * Optional ServiceRegistration property which contains a collection of
+ * interface names with their associated (non-Java) endpoint interface names
+ * separated by {@link #SEPARATOR} e.g.:<br>
+ * ["my.company.foo|MyWebService", "my.company.zoo|MyWebService"].
+ * <p>
+ * This (non-Java) endpoint interface name is usually a communication
+ * protocol specific interface, for instance a web service interface name.
+ * Though this information is usually contained in accompanying properties
+ * e.g. a wsdl file, Discovery usually doesn't read and interprets such
+ * service meta-data. Providing this information explicitly, might allow
+ * external non-Java applications find services based on this endpoint
+ * interface.
+ * <p>
+ * Value of this property is of type
+ * <code>Collection (&lt;? extends String&gt;)</code>, may be
+ * <code>null</code> or empty.
+ */
+ public static final String ENDPOINT_INTERFACE_NAME = "osgi.remote.endpoint.interfaces";
+
+ /**
+ * Optional ServiceRegistration property which contains a map of properties
+ * of the published service.
+ * <p>
+ * Property keys are handled in a case insensitive manner (as OSGi Framework
+ * does).
+ * <p>
+ * Value of this property is of type <code>Map (String, Object)</code>, may
+ * be <code>null</code> or empty.
+ */
+ public static final String SERVICE_PROPERTIES = "osgi.remote.discovery.publication.service.properties";
+
+ /**
+ * Optional property of the published service identifying its location. This
+ * property is provided as part of the service property map referenced by
+ * the {@link #SERVICE_PROPERTIES} ServiceRegistration property.
+ * <p>
+ * Value of this property is of type <code>java.net.URI</code>, may be
+ * <code>null</code>.
+ */
+ public static final String ENDPOINT_LOCATION = "osgi.remote.endpoint.location";
+
+ /**
+ * Optional property of the published service uniquely identifying its
+ * endpoint. This property is provided as part of the service property map
+ * referenced by the {@link #SERVICE_PROPERTIES} ServiceRegistration
+ * property.
+ * <p>
+ * Value of this property is of type <code>String</code>, may be
+ * <code>null</code>.
+ */
+ public static final String ENDPOINT_ID = "osgi.remote.endpoint.id";
+
+ /**
+ * Separator constant for association of interface-specific values with the
+ * particular interface name. See also {@link #SERVICE_INTERFACE_VERSION}
+ * and {@link #ENDPOINT_INTERFACE_NAME} properties which describe such
+ * interface-specific values.
+ */
+ public static final String SEPARATOR = "|";
+
+ /**
+ * Returns the <code>ServiceReference</code> this publication metadata is
+ * associated with.
+ *
+ * @return the <code>ServiceReference</code> being published. Is never
+ * <code>null</code>.
+ */
+ ServiceReference getReference();
+}