From b264fae6da998eb032462b7287711d891498cd3e Mon Sep 17 00:00:00 2001 From: rfeng Date: Thu, 16 Jul 2009 06:52:55 +0000 Subject: Refactor the OSGi discovery service into node-implementation-osgi git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@794553 13f79535-47bb-0310-9956-ffa450edef68 --- .../dosgi/discovery/AbstractDiscoveryService.java | 344 +++++++++++++++++++++ .../DiscoveredServiceNotificationImpl.java | 67 ++++ .../sca/dosgi/discovery/DiscoveryActivator.java | 58 ++++ .../dosgi/discovery/DomainDiscoveryService.java | 122 ++++++++ .../sca/dosgi/discovery/EndpointDescription.java | 69 +++++ .../sca/dosgi/discovery/EndpointPublication.java | 63 ++++ .../sca/dosgi/discovery/LocalDiscoveryService.java | 122 ++++++++ .../discovery/ServiceEndpointDescriptionImpl.java | 135 ++++++++ .../tuscany/sca/node/osgi/impl/NodeActivator.java | 17 + .../tuscany/sca/node/osgi/impl/NodeManager.java | 2 +- .../sca/node/osgi/impl/OSGiServiceExporter.java | 127 ++++++++ .../discovery/DiscoveredServiceNotification.java | 107 +++++++ .../discovery/DiscoveredServiceTracker.java | 81 +++++ .../java/org/osgi/service/discovery/Discovery.java | 66 ++++ .../discovery/ServiceEndpointDescription.java | 128 ++++++++ .../osgi/service/discovery/ServicePublication.java | 147 +++++++++ 16 files changed, 1654 insertions(+), 1 deletion(-) create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/AbstractDiscoveryService.java create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveredServiceNotificationImpl.java create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveryActivator.java create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DomainDiscoveryService.java create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointDescription.java create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointPublication.java create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/LocalDiscoveryService.java create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/ServiceEndpointDescriptionImpl.java create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiServiceExporter.java create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/DiscoveredServiceNotification.java create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/DiscoveredServiceTracker.java create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/Discovery.java create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/ServiceEndpointDescription.java create mode 100644 java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/ServicePublication.java (limited to 'java/sca/modules/node-impl-osgi/src') diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/AbstractDiscoveryService.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/AbstractDiscoveryService.java new file mode 100644 index 0000000000..780fd73c95 --- /dev/null +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/AbstractDiscoveryService.java @@ -0,0 +1,344 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.assembly.Endpoint; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.node.NodeFactory; +import org.apache.tuscany.sca.node.impl.NodeFactoryImpl; +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.framework.ServiceRegistration; +import org.osgi.service.discovery.DiscoveredServiceNotification; +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; + protected ExtensionPointRegistry registry; + + private Map> filtersToTrackers = + new HashMap>(); + private Map> interfacesToTrackers = + new HashMap>(); + // 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 servicesInfo = + new ConcurrentHashMap(); + private Map> trackersToFilters = + new HashMap>(); + private Map> trackersToInterfaces = + new HashMap>(); + 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() { + NodeFactoryImpl factory = (NodeFactoryImpl) NodeFactory.newInstance(); + factory.init(); + ServiceTracker tracker = new ServiceTracker(context, ExtensionPointRegistry.class.getName(), null); + tracker.open(); + // tracker.waitForService(1000); + registry = (ExtensionPointRegistry)tracker.getService(); + tracker.close(); + return registry; + } + + private synchronized void cacheTracker(ServiceReference reference, Object service) { + if (service instanceof DiscoveredServiceTracker) { + DiscoveredServiceTracker tracker = (DiscoveredServiceTracker)service; + Collection interfaces = + addTracker(reference, tracker, INTERFACE_MATCH_CRITERIA, interfacesToTrackers, trackersToInterfaces); + Collection 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 oldInterfaces = removeTracker(tracker, interfacesToTrackers, trackersToInterfaces); + Collection oldFilters = removeTracker(tracker, filtersToTrackers, trackersToFilters); + + Collection newInterfaces = + addTracker(reference, tracker, INTERFACE_MATCH_CRITERIA, interfacesToTrackers, trackersToInterfaces); + Collection newFilters = + addTracker(reference, tracker, FILTER_MATCH_CRITERIA, filtersToTrackers, trackersToFilters); + + triggerCallbacks(oldInterfaces, newInterfaces, tracker, false); + triggerCallbacks(oldFilters, newFilters, tracker, true); + } + } + + private void triggerCallbacks(Collection oldInterest, + Collection 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 deltaInterest = new ArrayList(); + if (!isEmpty(newInterest)) { + if (isEmpty(oldInterest)) { + deltaInterest.addAll(newInterest); + } else { + Iterator 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 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()); + + DiscoveredServiceNotification notification = + isFilter ? (filterMatches(toMatch, sd) ? new DiscoveredServiceNotificationImpl(sd, true, toMatch, type) + : null) : (sd.getProvidedInterfaces().contains(toMatch) + ? new DiscoveredServiceNotificationImpl(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 getServiceProperties(String interfaceName, ServiceEndpointDescription sd) { + Dictionary d = new Hashtable(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 interfaceNames = sd.getProvidedInterfaces(); + if (interfaceName == null) { + return null; + } + + Iterator iNames = interfaceNames.iterator(); + while (iNames.hasNext()) { + if (iNames.next().equals(interfaceName)) { + return new String[] {interfaceName}; + } + } + return null; + } + + static Collection removeTracker(DiscoveredServiceTracker tracker, + Map> forwardMap, + Map> reverseMap) { + Collection collection = reverseMap.get(tracker); + if (!isEmpty(collection)) { + reverseMap.remove(tracker); + Iterator 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 addTracker(ServiceReference reference, + DiscoveredServiceTracker tracker, + String property, + Map> forwardMap, + Map> reverseMap) { + Collection collection = (Collection)reference.getProperty(property); + logger.info("adding tracker: " + tracker + + " collection: " + + collection + + " registered against prop: " + + property); + if (!isEmpty(collection)) { + reverseMap.put(tracker, new ArrayList(collection)); + Iterator i = collection.iterator(); + while (i.hasNext()) { + String element = i.next(); + if (forwardMap.containsKey(element)) { + forwardMap.get(element).add(tracker); + } else { + List trackerList = new ArrayList(); + trackerList.add(tracker); + forwardMap.put(element, trackerList); + } + } + } + return collection; + } + + protected void discoveredServiceChanged(ServiceEndpointDescription sd, int type) { + for (Map.Entry> entry : trackersToInterfaces.entrySet()) { + for (String match : entry.getValue()) { + triggerCallbacks(entry.getKey(), match, false, sd, type); + } + } + for (Map.Entry> entry : trackersToFilters.entrySet()) { + for (String match : entry.getValue()) { + triggerCallbacks(entry.getKey(), match, true, sd, type); + } + } + } + + /** + * Publish the OSGi services that are exposed to SCA. For SCA, the replicated endpoint registry + * serves are the discovery protocol. The OSGi services are added to endpoint registry first before + * the ServicePublication services are registered so that othe Discovery services can see them. + * @param ref + * @param endpoint + * @return + */ + protected ServiceRegistration localServicePublished(ServiceReference ref, Endpoint endpoint) { + EndpointPublication publication = new EndpointPublication(ref, endpoint); + ServiceRegistration registration = + ref.getBundle().getBundleContext().registerService(ServicePublication.class.getName(), + publication, + publication.getProperties()); + return registration; + } + +} diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveredServiceNotificationImpl.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveredServiceNotificationImpl.java new file mode 100644 index 0000000000..a110fa9528 --- /dev/null +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveredServiceNotificationImpl.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; + +public class DiscoveredServiceNotificationImpl implements DiscoveredServiceNotification { + + private ServiceEndpointDescription discription; + private Collection interfaces; + private Collection filters; + private int type; + + public DiscoveredServiceNotificationImpl(ServiceEndpointDescription sd, boolean isFilter, String match, int type) { + this.discription = sd; + if (isFilter) { + filters = new ArrayList(); + filters.add(match); + interfaces = Collections.emptySet(); + } else { + interfaces = new HashSet(); + interfaces.add(match); + filters = Collections.emptyList(); + } + + this.type = type; + } + + public ServiceEndpointDescription getServiceEndpointDescription() { + return discription; + } + + public int getType() { + return type; + } + + public Collection getInterfaces() { + return interfaces; + } + + public Collection getFilters() { + return filters; + } +} diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveryActivator.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveryActivator.java new file mode 100644 index 0000000000..9497619b5b --- /dev/null +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveryActivator.java @@ -0,0 +1,58 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.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 discoveryServices = new ArrayList(); + private List discoveryServiceRegistrations = new ArrayList(); + + public void start(BundleContext context) { + discoveryServices.add(new LocalDiscoveryService(context)); + + discoveryServices.add(new DomainDiscoveryService(context)); + + for (AbstractDiscoveryService service : discoveryServices) { + Hashtable props = new Hashtable(); + props.put(Discovery.PRODUCT_NAME, "Apache Tuscany SCA"); + props.put(Discovery.PRODUCT_VERSION, "2.0.0"); + props.put(Discovery.VENDOR_NAME, "Apache Software Foundation"); + ServiceRegistration registration = + context.registerService(Discovery.class.getName(), service, props); + 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/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DomainDiscoveryService.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DomainDiscoveryService.java new file mode 100644 index 0000000000..3136c4cbc9 --- /dev/null +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DomainDiscoveryService.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.tuscany.sca.assembly.Endpoint; +import org.apache.tuscany.sca.assembly.Implementation; +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.framework.ServiceRegistration; +import org.osgi.service.discovery.ServiceEndpointDescription; + +/** + * Discovery service based on the distributed SCA domain + */ +public class DomainDiscoveryService extends AbstractDiscoveryService implements EndpointListener { + private EndpointRegistry endpointRegistry; + + private Map endpointRegistrations = + new ConcurrentHashMap(); + + public DomainDiscoveryService(BundleContext context) { + super(context); + init(); + } + + private void init() { + 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(); + + if (!endpoint.isRemote()) { + + 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) { + ServiceRegistration registration = localServicePublished(ref, endpoint); + endpointRegistrations.put(endpoint.getURI(), registration); + } + } else { + // Remote endpoints + ServiceEndpointDescription description = new EndpointDescription(endpoint); + discoveredServiceChanged(description, AVAILABLE); + } + } + + public void endpointRemoved(Endpoint endpoint) { + if (!endpoint.isRemote()) { + // unregister the ServicePublication here + ServiceRegistration registration = endpointRegistrations.get(endpoint.getURI()); + if (registration != null) { + registration.unregister(); + } + } else { + // Remote endpoints + ServiceEndpointDescription description = new EndpointDescription(endpoint); + discoveredServiceChanged(description, UNAVAILABLE); + } + } + + public void endpointUpdated(Endpoint oldEndpoint, Endpoint newEndpoint) { + // FIXME: This is a quick and dirty way for the update + endpointRemoved(oldEndpoint); + endpointAdded(newEndpoint); + } + + public void stop() { + endpointRegistry.removeListener(this); + super.stop(); + } + +} diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointDescription.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointDescription.java new file mode 100644 index 0000000000..3f47183a56 --- /dev/null +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointDescription.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.dosgi.discovery; + +import static org.osgi.service.discovery.ServicePublication.ENDPOINT_ID; +import static org.osgi.service.discovery.ServicePublication.ENDPOINT_LOCATION; + +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.tuscany.sca.assembly.ComponentService; +import org.apache.tuscany.sca.assembly.Endpoint; +import org.apache.tuscany.sca.interfacedef.Interface; +import org.apache.tuscany.sca.interfacedef.InterfaceContract; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; + +/** + * + */ +public class EndpointDescription extends ServiceEndpointDescriptionImpl { + public EndpointDescription(Endpoint endpoint) { + super(Collections.singleton(getInterfaceName(endpoint)), getServiceProperties(endpoint)); + } + + static String getInterfaceName(Endpoint endpoint) { + ComponentService service = endpoint.getService(); + if (service == null) { + return null; + } + InterfaceContract contract = service.getInterfaceContract(); + if (contract == null) { + return null; + } + Interface intf = contract.getInterface(); + if (intf instanceof JavaInterface) { + JavaInterface javaInterface = (JavaInterface)intf; + return javaInterface.getName(); + } + + return null; + } + + static Map getServiceProperties(Endpoint endpoint) { + Map serviceProps = new HashMap(); + serviceProps.put(ENDPOINT_ID, endpoint.getURI()); + serviceProps.put(ENDPOINT_LOCATION, URI.create(endpoint.getBinding().getURI())); + // TODO: Populate the properties from the Endpoint object + return serviceProps; + } +} diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointPublication.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointPublication.java new file mode 100644 index 0000000000..184d3a12bf --- /dev/null +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointPublication.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.dosgi.discovery; + +import java.util.Collections; +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.Map; + +import org.apache.tuscany.sca.assembly.Endpoint; +import org.osgi.framework.ServiceReference; +import org.osgi.service.discovery.ServicePublication; + +/** + * Publication of an SCA endpoint + */ +public class EndpointPublication implements ServicePublication { + private Endpoint endpoint; + 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, Endpoint endpoint) { + super(); + this.reference = reference; + this.endpoint = endpoint; + } + + public ServiceReference getReference() { + return reference; + } + + public Dictionary getProperties() { + Dictionary props = new Hashtable(); + Map serviceProps = EndpointDescription.getServiceProperties(endpoint); + props.put(SERVICE_PROPERTIES, serviceProps); + // TODO: Populate the properties from the Endpoint object + String name = EndpointDescription.getInterfaceName(endpoint); + props.put(ENDPOINT_INTERFACE_NAME, Collections.singleton(name)); + return props; + } + +} diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/LocalDiscoveryService.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/LocalDiscoveryService.java new file mode 100644 index 0000000000..43a19f98cc --- /dev/null +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/LocalDiscoveryService.java @@ -0,0 +1,122 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.tuscany.sca.implementation.osgi.ServiceDescriptions; +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 { + + public LocalDiscoveryService(BundleContext context) { + super(context); + init(); + } + + private void init() { + context.addBundleListener(this); + 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) { + String path = (String)b.getHeaders().get(ServiceDescriptions.REMOTE_SERVICE_HEADER); + if (path == null) { + Enumeration files = b.findEntries(ServiceDescriptions.REMOTE_SERVICE_FOLDER, "*.xml", false); + if (files == null || !files.hasMoreElements()) { + return; + } + } + + ServiceDescriptions descriptions = null; + + // TODO: Use SCA contribution to load the service discription files + List 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> i = servicesInfo.entrySet().iterator(); i + .hasNext();) { + Entry 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/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/ServiceEndpointDescriptionImpl.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/ServiceEndpointDescriptionImpl.java new file mode 100644 index 0000000000..949405472f --- /dev/null +++ b/java/sca/modules/node-impl-osgi/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 interfaces; + private Map properties; + + public ServiceEndpointDescriptionImpl(Collection interfaceNames) { + this(interfaceNames, Collections. singletonMap(SERVICE_INTERFACE_NAME, + interfaceNames)); + } + + public ServiceEndpointDescriptionImpl(Collection interfaceNames, Map remoteProperties) { + this.interfaces = new HashSet(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 getProperties() { + return Collections.unmodifiableMap(properties); + } + + public Object getProperty(String key) { + return properties.get(key); + } + + public Collection getPropertyKeys() { + return getProperties().keySet(); + } + + public Collection 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/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeActivator.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeActivator.java index 43892d44d8..00b0b680be 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeActivator.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeActivator.java @@ -21,6 +21,7 @@ package org.apache.tuscany.sca.node.osgi.impl; import static org.apache.tuscany.sca.node.osgi.impl.NodeManager.isSCABundle; +import org.apache.tuscany.sca.dosgi.discovery.DiscoveryActivator; import org.osgi.framework.Bundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; @@ -34,6 +35,9 @@ public class NodeActivator implements BundleActivator, SynchronousBundleListener private static BundleContext bundleContext; private boolean inited; private NodeManager manager; + + private DiscoveryActivator discoveryActivator = new DiscoveryActivator(); + private OSGiServiceExporter exporter; private void init() { synchronized (this) { @@ -49,6 +53,15 @@ public class NodeActivator implements BundleActivator, SynchronousBundleListener public void start(BundleContext context) throws Exception { bundleContext = context; + + // FIXME: We should try to avoid aggressive initialization + init(); + + exporter = new OSGiServiceExporter(context); + exporter.start(); + + discoveryActivator.start(context); + boolean found = false; for (Bundle b : context.getBundles()) { if (isSCABundle(b)) { @@ -67,6 +80,10 @@ public class NodeActivator implements BundleActivator, SynchronousBundleListener public void stop(BundleContext context) throws Exception { context.removeBundleListener(this); bundleContext = null; + exporter.stop(); + exporter = null; + discoveryActivator.stop(context); + discoveryActivator = null; } public static BundleContext getBundleContext() { diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeManager.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeManager.java index 3f71117c1a..6914e09b8e 100644 --- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeManager.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeManager.java @@ -39,7 +39,7 @@ import org.osgi.framework.SynchronousBundleListener; public class NodeManager implements SynchronousBundleListener, ServiceListener { private static final Logger logger = Logger.getLogger(NodeManager.class.getName()); private BundleContext bundleContext; - private OSGiNodeFactoryImpl factory; + OSGiNodeFactoryImpl factory; public NodeManager(BundleContext bundleContext) { super(); diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiServiceExporter.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiServiceExporter.java new file mode 100644 index 0000000000..329ce09a5b --- /dev/null +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiServiceExporter.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.node.osgi.impl; + +import java.util.Collections; + +import org.apache.tuscany.sca.contribution.Contribution; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.implementation.osgi.introspection.ExportedServiceIntrospector; +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.node.NodeFactory; +import org.apache.tuscany.sca.node.configuration.NodeConfiguration; +import org.apache.tuscany.sca.node.impl.NodeFactoryImpl; +import org.apache.tuscany.sca.node.impl.NodeImpl; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Filter; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; +import org.osgi.util.tracker.ServiceTrackerCustomizer; + +/** + * Watching and exporting OSGi services + */ +public class OSGiServiceExporter implements ServiceTrackerCustomizer { + private ExtensionPointRegistry registry; + private BundleContext context; + private ServiceTracker serviceTracker; + private NodeFactoryImpl nodeFactory; + private ExportedServiceIntrospector introspector; + + /** + * @param context + * @param clazz + * @param customizer + */ + public OSGiServiceExporter(BundleContext context) { + this.context = context; + } + + private synchronized void init() { + if (nodeFactory == null) { + this.nodeFactory = (NodeFactoryImpl)NodeFactory.newInstance(); + this.nodeFactory.init(); + this.introspector = new ExportedServiceIntrospector(getExtensionPointRegistry()); + } + } + + public void start() { + String filterStr = "(& (osgi.remote.configuration.type=sca) (osgi.remote.interfaces=*) (!(osgi.remote=true)) )"; + try { + Filter filter = context.createFilter(filterStr); + serviceTracker = new ServiceTracker(context, filter, this); + serviceTracker.open(true); + } catch (InvalidSyntaxException e) { + // Ignore + } + } + + public void stop() { + if (serviceTracker != null) { + serviceTracker.close(); + serviceTracker = null; + } + } + + public Object addingService(ServiceReference reference) { + init(); + try { + Contribution contribution = introspector.introspect(reference); + if (contribution != null) { + + NodeConfiguration configuration = nodeFactory.createNodeConfiguration(); + configuration.setURI(String.valueOf(reference.getProperty("service.id"))); + configuration.getExtensions().add(reference.getBundle()); + // FIXME: Configure the domain and node URI + NodeImpl node = new NodeImpl(nodeFactory, configuration, Collections.singletonList(contribution)); + return node.start(); + } else { + return null; + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public void modifiedService(ServiceReference reference, Object service) { + Node node = (Node)service; + node.stop(); + node.start(); + } + + public void removedService(ServiceReference reference, Object service) { + Node node = (Node)service; + node.stop(); + } + + protected ExtensionPointRegistry getExtensionPointRegistry() { + if (registry == null) { + ServiceTracker tracker = new ServiceTracker(context, ExtensionPointRegistry.class.getName(), null); + tracker.open(); + // tracker.waitForService(1000); + registry = (ExtensionPointRegistry)tracker.getService(); + tracker.close(); + } + return registry; + } + +} diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/DiscoveredServiceNotification.java b/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/DiscoveredServiceNotification.java new file mode 100644 index 0000000000..57de9c7c9d --- /dev/null +++ b/java/sca/modules/node-impl-osgi/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. + *

+ * DiscoveredServiceNotification objects are immutable. + * + * @Immutable + */ +public interface DiscoveredServiceNotification { + + /** + * Notification indicating that a service matching the listening criteria + * has been discovered. + *

+ * The value of AVAILABLE is 0x00000001. + */ + public final static int AVAILABLE = 0x00000001; + + /** + * Notification indicating that the properties of a previously discovered + * service have changed. + *

+ * The value of MODIFIED is 0x00000002. + */ + public final static int MODIFIED = 0x00000002; + + /** + * Notification indicating that a previously discovered service is no longer + * known to Discovery. + *

+ * The value of UNAVAILABLE 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. + *

+ * The value of MODIFIED_ENDMATCH is 0x00000008. + */ + public final static int MODIFIED_ENDMATCH = 0x00000008; + + /** + * Returns information currently known to Discovery regarding + * the service endpoint. + * + * @return metadata of the service Discovery notifies about. Is + * never null. + */ + ServiceEndpointDescription getServiceEndpointDescription(); + + /** + * Returns the type of notification. The type values are: + *

    + *
  • {@link #AVAILABLE}
  • + *
  • {@link #MODIFIED}
  • + *
  • {@link #MODIFIED_ENDMATCH}
  • + *
  • {@link #UNAVAILABLE}
  • + *
+ * + * @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 + * ServiceEndpointDescription and thus caused the notification. + * + * @return Collection (<? extends String>) of matching interface name criteria of the + * DiscoveredServiceTracker object being notified, or + * an empty collection if notification hasn't been caused by a + * matching interface name criteria. + */ + Collection/* */getInterfaces(); + + /** + * Returns filters of the DiscoveredServiceTracker object + * matching with the properties of the + * ServiceEndpointDescription and thus caused the notification. + * + * @return Collection (<? extends String>) of matching filters of the DiscoveredServiceTracker + * object being notified, or an empty collection if notification + * hasn't been caused by a matching filter criteria. + */ + Collection/* */getFilters(); +} diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/DiscoveredServiceTracker.java b/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/DiscoveredServiceTracker.java new file mode 100644 index 0000000000..efd158a2f5 --- /dev/null +++ b/java/sca/modules/node-impl-osgi/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. + *

+ * When a service implementing this interface is registered with the framework, + * then Discovery 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. + * + * Discovery may deliver notifications on discovered services to a + * DiscoveredServiceTracker out of order and may concurrently call + * and/or reenter a DiscoveredServiceTracker. + * + * @ThreadSafe + */ +public interface DiscoveredServiceTracker { + + /** + * Optional ServiceRegistration property which contains service interfaces + * this tracker is interested in. + *

+ * Value of this property is of type + * Collection (<? extends String>). May be + * null 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. + *

+ * 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. + *

+ * The following sample filter will make Discovery notify the + * DiscoveredServiceTracker about services providing interface + * 'my.company.foo' of version '1.0.1.3': + * "(&(service.interface=my.company.foo)(service.interface.version=my.company.foo|1.0.1.3))". + *

+ * Value of this property is of type + * Collection (<? extends String>). May be + * null. or empty + */ + public static final String FILTER_MATCH_CRITERIA = "osgi.remote.discovery.interest.filters"; + + /** + * Receives notification that information known to Discovery + * regarding a remote service has changed. + *

+ * 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. + *

+ * 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 DiscoveredServiceNotification object + * describing the change. Is never null. + */ + void serviceChanged(DiscoveredServiceNotification notification); +} diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/Discovery.java b/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/Discovery.java new file mode 100644 index 0000000000..cca766a032 --- /dev/null +++ b/java/sca/modules/node-impl-osgi/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.. + *

+ * 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. + *

+ * 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. + *

+ * Value of this property is of type String. + */ + static final String PRODUCT_NAME = "osgi.remote.discovery.product"; + + /** + * ServiceRegistration property for the version of the Discovery product. + *

+ * Value of this property is of type String. + */ + static final String PRODUCT_VERSION = "osgi.remote.discovery.product.version"; + + /** + * ServiceRegistration property for the Discovery product vendor name. + *

+ * Value of this property is of type String. + */ + static final String VENDOR_NAME = "osgi.remote.discovery.vendor"; + + /** + * ServiceRegistration property that lists the discovery protocols used by + * this Discovery service. + *

+ * Value of this property is of type + * Collection (<? extends String>). + */ + static final String SUPPORTED_PROTOCOLS = "osgi.remote.discovery.supported_protocols"; +} diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/ServiceEndpointDescription.java b/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/ServiceEndpointDescription.java new file mode 100644 index 0000000000..e1f389ea78 --- /dev/null +++ b/java/sca/modules/node-impl-osgi/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. + *

+ * ServiceEndpointDescription objects are immutable. + * + * @Immutable + */ +public interface ServiceEndpointDescription { + + /** + * Returns the value of the property with key + * {@link ServicePublication#SERVICE_INTERFACE_NAME}. + * + * @return Collection (<? extends String>) of service + * interface names provided by the advertised service endpoint. The + * collection is never null or empty but contains at + * least one service interface. + */ + Collection /* */getProvidedInterfaces(); + + /** + * Returns non-Java endpoint interface name associated with the given + * interface. + *

+ * 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 null if it + * hasn't been provided or if given interface name is + * null. + */ + String getEndpointInterfaceName(String interfaceName); + + /** + * Returns version of the given interface. + *

+ * 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 null if it + * hasn't been provided or if given interface name is + * null. + */ + String getVersion(String interfaceName); + + /** + * Returns the value of the property with key + * {@link ServicePublication#ENDPOINT_LOCATION}. + * + * @return The url of the service location, or null 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 null 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 null if none is found for the + * given key or if provided key is null. + */ + Object getProperty(String key); + + /** + * Returns all names of service endpoint properties. + * + * @return a Collection (<? extends String>) of property + * names available in the ServiceEndpointDescription. The collection + * is never null or empty but contains at least names + * of mandatory ServicePublication properties. Since + * ServiceEndpointDescription objects are immutable, + * the returned collection is also not going to be updated at a + * later point of time. + */ + Collection/* */getPropertyKeys(); + + /** + * Returns all service endpoint properties. + * + * @return all properties of the service as a + * Map (<String, Object>). The map is never + * null or empty but contains at least mandatory + * ServicePublication properties. Since + * ServiceEndpointDescription objects are immutable, + * the returned map is also not going to be updated at a later point + * of time. + */ + Map/* */getProperties(); +} diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/ServicePublication.java b/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/ServicePublication.java new file mode 100644 index 0000000000..9b4cb4c905 --- /dev/null +++ b/java/sca/modules/node-impl-osgi/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 ServicePublication 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. + *

+ * In order to update published service metadata, update the properties + * registered with the ServicePublication service. Depending on + * Discovery's implementation and underlying protocol it may result in an update + * or new re-publication of the service. + *

+ * In order to unpublish the previously published service metadata, unregister + * the ServicePublication service. + *

+ * Please note that providing the {@link #SERVICE_INTERFACE_NAME} property is + * mandatory when a ServicePublication 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. + *

+ * Also important is that it's not guaranteed that after registering a + * ServicePublication 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. + *

+ * Value of this property is of type + * Collection (<? extends String>). + */ + 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 org.osgi.framework.Version.emptyVersion + * constant. + *

+ * Value of this property is of type + * Collection (<? extends String>), may be + * null 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.:
+ * ["my.company.foo|MyWebService", "my.company.zoo|MyWebService"]. + *

+ * 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. + *

+ * Value of this property is of type + * Collection (<? extends String>), may be + * null 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. + *

+ * Property keys are handled in a case insensitive manner (as OSGi Framework + * does). + *

+ * Value of this property is of type Map (String, Object), may + * be null 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. + *

+ * Value of this property is of type java.net.URI, may be + * null. + */ + 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. + *

+ * Value of this property is of type String, may be + * null. + */ + 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 ServiceReference this publication metadata is + * associated with. + * + * @return the ServiceReference being published. Is never + * null. + */ + ServiceReference getReference(); +} -- cgit v1.2.3