summaryrefslogtreecommitdiffstats
path: root/java/sca/modules/node-impl-osgi/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/sca/modules/node-impl-osgi/src')
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/AbstractDiscoveryService.java344
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveredServiceNotificationImpl.java67
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointDescription.java69
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointPublication.java63
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/LocalDiscoveryService.java122
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/ServiceEndpointDescriptionImpl.java135
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/NodeActivator.java26
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiServiceExporter.java58
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiServiceImporter.java121
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/AbstractDiscoveryService.java280
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/Discovery.java (renamed from java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/Discovery.java)40
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DiscoveryActivator.java (renamed from java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveryActivator.java)11
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/DomainDiscoveryService.java (renamed from java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DomainDiscoveryService.java)59
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java208
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointDescription.java146
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointListener.java101
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointPermission.java172
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ExportRegistration.java71
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ImportRegistration.java70
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdmin.java101
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminEvent.java148
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminListener.java36
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteConstants.java143
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointDescriptionImpl.java150
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointIntrospector.java482
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/ExportRegistrationImpl.java89
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/ImportRegistrationImpl.java92
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteAdminHelper.java86
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteAdminImpl.java144
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteControllerImpl.java403
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/DiscoveredServiceNotification.java107
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/DiscoveredServiceTracker.java81
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/ServiceEndpointDescription.java128
-rw-r--r--java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/ServicePublication.java147
34 files changed, 3151 insertions, 1349 deletions
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
deleted file mode 100644
index fa60ed0ed2..0000000000
--- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/AbstractDiscoveryService.java
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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<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() {
- 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<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());
-
- 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<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);
- }
- }
- }
-
- /**
- * 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
deleted file mode 100644
index a50721fbfc..0000000000
--- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/DiscoveredServiceNotificationImpl.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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<String> interfaces;
- private Collection<String> filters;
- private int type;
-
- public DiscoveredServiceNotificationImpl(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/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
deleted file mode 100644
index 5cdb0a4a8a..0000000000
--- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointDescription.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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<String, Object> getServiceProperties(Endpoint endpoint) {
- Map<String, Object> serviceProps = new HashMap<String, Object>();
- 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
deleted file mode 100644
index 4c7cbee649..0000000000
--- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/EndpointPublication.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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<String, Object> getProperties() {
- Dictionary<String, Object> props = new Hashtable<String, Object>();
- Map<String, Object> 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
deleted file mode 100644
index 543298600d..0000000000
--- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/LocalDiscoveryService.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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<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/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
deleted file mode 100644
index cd3b8e7543..0000000000
--- a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/dosgi/discovery/ServiceEndpointDescriptionImpl.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT 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/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 00b0b680be..10667a3509 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,7 +21,9 @@ 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.apache.tuscany.sca.osgi.service.discovery.impl.DiscoveryActivator;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.impl.RemoteAdminImpl;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.impl.RemoteControllerImpl;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
@@ -37,7 +39,8 @@ public class NodeActivator implements BundleActivator, SynchronousBundleListener
private NodeManager manager;
private DiscoveryActivator discoveryActivator = new DiscoveryActivator();
- private OSGiServiceExporter exporter;
+ private RemoteAdminImpl remoteAdmin;
+ private RemoteControllerImpl controller;
private void init() {
synchronized (this) {
@@ -57,8 +60,14 @@ public class NodeActivator implements BundleActivator, SynchronousBundleListener
// FIXME: We should try to avoid aggressive initialization
init();
- exporter = new OSGiServiceExporter(context);
- exporter.start();
+ remoteAdmin = new RemoteAdminImpl(context);
+ remoteAdmin.start();
+
+ controller = new RemoteControllerImpl(context);
+ controller.start();
+
+// exporter = new OSGiServiceExporter(context);
+// exporter.start();
discoveryActivator.start(context);
@@ -80,10 +89,15 @@ public class NodeActivator implements BundleActivator, SynchronousBundleListener
public void stop(BundleContext context) throws Exception {
context.removeBundleListener(this);
bundleContext = null;
- exporter.stop();
- exporter = null;
+ controller.stop();
+ controller = null;
+// exporter.stop();
+// exporter = null;
discoveryActivator.stop(context);
discoveryActivator = null;
+
+ remoteAdmin.stop();
+ remoteAdmin = null;
}
public static BundleContext getBundleContext() {
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
index d4a6580842..016c88ba43 100644
--- 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
@@ -19,24 +19,27 @@
package org.apache.tuscany.sca.node.osgi.impl;
-import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_CONFIGS;
-import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTERFACES;
-import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_IMPORTED;
-
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
+import org.apache.tuscany.sca.assembly.Component;
+import org.apache.tuscany.sca.assembly.ComponentService;
+import org.apache.tuscany.sca.assembly.Endpoint;
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.core.LifeCycleListener;
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.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.ExportRegistration;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.impl.EndpointDescriptionImpl;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.impl.EndpointIntrospector;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.impl.ExportRegistrationImpl;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
-import org.osgi.framework.Filter;
-import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
@@ -44,12 +47,12 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* Watching and exporting OSGi services
*/
-public class OSGiServiceExporter implements ServiceTrackerCustomizer {
+public class OSGiServiceExporter implements ServiceTrackerCustomizer, LifeCycleListener {
private ExtensionPointRegistry registry;
private BundleContext context;
private ServiceTracker serviceTracker;
private NodeFactoryImpl nodeFactory;
- private ExportedServiceIntrospector introspector;
+ private EndpointIntrospector introspector;
/**
* @param context
@@ -64,11 +67,13 @@ public class OSGiServiceExporter implements ServiceTrackerCustomizer {
if (nodeFactory == null) {
this.nodeFactory = (NodeFactoryImpl)NodeFactory.newInstance();
this.nodeFactory.init();
- this.introspector = new ExportedServiceIntrospector(getExtensionPointRegistry());
+ this.introspector = new EndpointIntrospector(context, getExtensionPointRegistry());
}
}
public void start() {
+ init();
+ /*
String filterStr =
"(& (" + SERVICE_EXPORTED_CONFIGS
+ "=sca) ("
@@ -83,17 +88,23 @@ public class OSGiServiceExporter implements ServiceTrackerCustomizer {
} catch (InvalidSyntaxException e) {
// Ignore
}
+ */
}
public void stop() {
+ /*
if (serviceTracker != null) {
serviceTracker.close();
serviceTracker = null;
}
+ */
}
public Object addingService(ServiceReference reference) {
- init();
+ return exportService(reference);
+ }
+
+ public List<ExportRegistration> exportService(ServiceReference reference) {
try {
Contribution contribution = introspector.introspect(reference);
if (contribution != null) {
@@ -103,7 +114,17 @@ public class OSGiServiceExporter implements ServiceTrackerCustomizer {
configuration.getExtensions().add(reference.getBundle());
// FIXME: Configure the domain and node URI
NodeImpl node = new NodeImpl(nodeFactory, configuration, Collections.singletonList(contribution));
- return node.start();
+ node.start();
+ List<ExportRegistration> exportedServices = new ArrayList<ExportRegistration>();
+ Component component = contribution.getDeployables().get(0).getComponents().get(0);
+ ComponentService service = component.getServices().get(0);
+ for (Endpoint endpoint : service.getEndpoints()) {
+ EndpointDescription endpointDescription = new EndpointDescriptionImpl(endpoint);
+ ExportRegistration exportRegistration =
+ new ExportRegistrationImpl(node, reference, endpointDescription);
+ exportedServices.add(exportRegistration);
+ }
+ return exportedServices;
} else {
return null;
}
@@ -114,14 +135,15 @@ public class OSGiServiceExporter implements ServiceTrackerCustomizer {
}
public void modifiedService(ServiceReference reference, Object service) {
- Node node = (Node)service;
- node.stop();
- node.start();
+ removedService(reference, service);
+ exportService(reference);
}
public void removedService(ServiceReference reference, Object service) {
- Node node = (Node)service;
- node.stop();
+ List<ExportRegistration> exportedServices = (List<ExportRegistration>)service;
+ for(ExportRegistration exportRegistration: exportedServices) {
+ exportRegistration.close();
+ }
}
protected ExtensionPointRegistry getExtensionPointRegistry() {
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiServiceImporter.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiServiceImporter.java
new file mode 100644
index 0000000000..7a0f3bbbc0
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/impl/OSGiServiceImporter.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.assembly.Component;
+import org.apache.tuscany.sca.assembly.ComponentReference;
+import org.apache.tuscany.sca.contribution.Contribution;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+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.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.ImportRegistration;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.impl.EndpointIntrospector;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.impl.ImportRegistrationImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Watching and exporting OSGi services
+ */
+public class OSGiServiceImporter implements LifeCycleListener {
+ private ExtensionPointRegistry registry;
+ private BundleContext context;
+ private NodeFactoryImpl nodeFactory;
+ private EndpointIntrospector introspector;
+
+ /**
+ * @param context
+ * @param clazz
+ * @param customizer
+ */
+ public OSGiServiceImporter(BundleContext context) {
+ this.context = context;
+ }
+
+ private synchronized void init() {
+ if (nodeFactory == null) {
+ this.nodeFactory = (NodeFactoryImpl)NodeFactory.newInstance();
+ this.nodeFactory.init();
+ this.introspector = new EndpointIntrospector(context, getExtensionPointRegistry());
+ }
+ }
+
+ public void start() {
+ }
+
+ public void stop() {
+ }
+
+ public ImportRegistration importService(Bundle bundle, EndpointDescription endpointDescription) {
+ init();
+ try {
+ Contribution contribution = introspector.introspect(bundle, endpointDescription);
+ if (contribution != null) {
+
+ NodeConfiguration configuration = nodeFactory.createNodeConfiguration();
+ configuration.setURI("osgi.reference." + endpointDescription.getURI());
+ configuration.getExtensions().add(bundle);
+ // FIXME: Configure the domain and node URI
+ NodeImpl node = new NodeImpl(nodeFactory, configuration, Collections.singletonList(contribution));
+ node.start();
+
+ Component component = contribution.getDeployables().get(0).getComponents().get(0);
+ ComponentReference componentReference = component.getReferences().get(0);
+ ServiceReference serviceReference =
+ context.getServiceReference("(sca.reference=" + component.getURI()
+ + "#reference("
+ + componentReference.getName()
+ + ")");
+ return new ImportRegistrationImpl(node, serviceReference, endpointDescription);
+ } else {
+ return null;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public void unimportService(ImportRegistration importRegistration) {
+ Node node = (Node)importRegistration.getImportedService().getProperty("sca.node");
+ 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/apache/tuscany/sca/osgi/service/discovery/impl/AbstractDiscoveryService.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/AbstractDiscoveryService.java
new file mode 100644
index 0000000000..d56b25ef40
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/AbstractDiscoveryService.java
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.osgi.service.discovery.impl;
+
+import static org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointListener.ENDPOINT_LISTENER_SCOPE;
+
+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.Level;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.LifeCycleListener;
+import org.apache.tuscany.sca.node.NodeFactory;
+import org.apache.tuscany.sca.node.impl.NodeFactoryImpl;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointListener;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteConstants;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.impl.RemoteAdminHelper;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ *
+ */
+public abstract class AbstractDiscoveryService implements Discovery, LifeCycleListener {
+ protected final static int ADDED = 0x1;
+ protected final static int REMOVED = 0x2;
+ protected final static int MODIFIED = 0x4;
+
+ protected final static Logger logger = Logger.getLogger(AbstractDiscoveryService.class.getName());
+
+ protected BundleContext context;
+ protected ExtensionPointRegistry registry;
+
+ private Map<String, List<EndpointListener>> filtersToListeners = new HashMap<String, List<EndpointListener>>();
+ // 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<EndpointDescription, Bundle> servicesInfo = new ConcurrentHashMap<EndpointDescription, Bundle>();
+ private Map<EndpointListener, Collection<String>> listenersToFilters =
+ new HashMap<EndpointListener, Collection<String>>();
+ private ServiceTracker trackerTracker;
+
+ public AbstractDiscoveryService(BundleContext context) {
+ super();
+ this.context = context;
+ }
+
+ public void start() {
+ // track the registration of EndpointListener
+ trackerTracker = new ServiceTracker(this.context, EndpointListener.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;
+ }
+
+ protected Dictionary<String, Object> getProperties() {
+ Dictionary headers = context.getBundle().getHeaders();
+ Hashtable<String, Object> props = new Hashtable<String, Object>();
+ props.put(PRODUCT_NAME, "Apache Tuscany SCA");
+ props.put(PRODUCT_VERSION, headers.get(Constants.BUNDLE_VERSION));
+ props.put(VENDOR_NAME, headers.get(Constants.BUNDLE_VENDOR));
+ props.put(SUPPORTED_PROTOCOLS, new String[] {"local", "sca"});
+ return props;
+ }
+
+ private synchronized void cacheTracker(ServiceReference reference, Object service) {
+ if (service instanceof EndpointListener) {
+ EndpointListener listener = (EndpointListener)service;
+ Collection<String> filters =
+ addTracker(reference, listener, ENDPOINT_LISTENER_SCOPE, filtersToListeners, listenersToFilters);
+
+ triggerCallbacks(null, filters, listener, true);
+ }
+ }
+
+ private synchronized void clearTracker(Object service) {
+ if (service instanceof EndpointListener) {
+ removeTracker((EndpointListener)service, filtersToListeners, listenersToFilters);
+ }
+ }
+
+ private synchronized void updateTracker(ServiceReference reference, Object service) {
+ if (service instanceof EndpointListener) {
+ EndpointListener listener = (EndpointListener)service;
+ if (logger.isLoggable(Level.FINE)) {
+ logger.fine("updating listener: " + listener);
+ }
+ Collection<String> oldFilters = removeTracker(listener, filtersToListeners, listenersToFilters);
+
+ Collection<String> newFilters =
+ addTracker(reference, listener, ENDPOINT_LISTENER_SCOPE, filtersToListeners, listenersToFilters);
+
+ triggerCallbacks(oldFilters, newFilters, listener, true);
+ }
+ }
+
+ private void triggerCallbacks(Collection<String> oldInterest,
+ Collection<String> newInterest,
+ EndpointListener listener,
+ 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 (newInterest != null && !newInterest.isEmpty()) {
+ if (oldInterest == null || oldInterest.isEmpty()) {
+ 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 (logger.isLoggable(Level.FINE)) {
+ if (servicesInfo.size() > 0) {
+ logger.fine("search for matches to trigger callbacks with delta: " + deltaInterest);
+ } else {
+ logger.fine("nothing to search for matches to trigger callbacks with delta: " + deltaInterest);
+ }
+ }
+ Iterator<String> i = deltaInterest.iterator();
+ while (i.hasNext()) {
+ String next = i.next();
+ for (EndpointDescription sd : servicesInfo.keySet()) {
+ triggerCallbacks(listener, next, sd, ADDED);
+ }
+ }
+ }
+
+ private void triggerCallbacks(EndpointListener listener, String matchedFilter, EndpointDescription sd, int type) {
+ switch (type) {
+ case ADDED:
+ listener.addEndpoint(sd, matchedFilter);
+ break;
+ case REMOVED:
+ listener.removeEndpoint(sd);
+ break;
+ case MODIFIED:
+ listener.removeEndpoint(sd);
+ listener.addEndpoint(sd, matchedFilter);
+ break;
+ }
+ }
+
+ private boolean filterMatches(String filterValue, EndpointDescription sd) {
+ Filter filter = RemoteAdminHelper.createFilter(context, filterValue);
+ Hashtable<String, Object> props = new Hashtable<String, Object>(sd.getProperties());
+ // Add two faked properties to make the filter match
+ props.put(Constants.OBJECTCLASS, sd.getInterfaces());
+ props.put(RemoteConstants.SERVICE_IMPORTED, "true");
+ return filter != null ? filter.match(props) : false;
+ }
+
+ static Collection<String> removeTracker(EndpointListener listener,
+ Map<String, List<EndpointListener>> forwardMap,
+ Map<EndpointListener, Collection<String>> reverseMap) {
+ Collection<String> collection = reverseMap.get(listener);
+ if (collection != null && !collection.isEmpty()) {
+ reverseMap.remove(listener);
+ Iterator<String> i = collection.iterator();
+ while (i.hasNext()) {
+ String element = i.next();
+ if (forwardMap.containsKey(element)) {
+ forwardMap.get(element).remove(listener);
+ } 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;
+ }
+
+ @SuppressWarnings("unchecked")
+ static Collection<String> addTracker(ServiceReference reference,
+ EndpointListener listener,
+ String property,
+ Map<String, List<EndpointListener>> forwardMap,
+ Map<EndpointListener, Collection<String>> reverseMap) {
+ Collection<String> collection = RemoteAdminHelper.getStringCollection(reference, property);
+ if (logger.isLoggable(Level.FINE)) {
+ logger.fine("adding listener: " + listener
+ + " collection: "
+ + collection
+ + " registered against prop: "
+ + property);
+ }
+ if (collection != null && !collection.isEmpty()) {
+ reverseMap.put(listener, new ArrayList<String>(collection));
+ Iterator<String> i = collection.iterator();
+ while (i.hasNext()) {
+ String element = i.next();
+ if (forwardMap.containsKey(element)) {
+ forwardMap.get(element).add(listener);
+ } else {
+ List<EndpointListener> trackerList = new ArrayList<EndpointListener>();
+ trackerList.add(listener);
+ forwardMap.put(element, trackerList);
+ }
+ }
+ }
+ return collection;
+ }
+
+ protected void endpointChanged(EndpointDescription sd, int type) {
+ for (Map.Entry<EndpointListener, Collection<String>> entry : listenersToFilters.entrySet()) {
+ for (String filter : entry.getValue()) {
+ if (filterMatches(filter, sd)) {
+ triggerCallbacks(entry.getKey(), filter, sd, type);
+ }
+ }
+ }
+ }
+
+}
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/apache/tuscany/sca/osgi/service/discovery/impl/Discovery.java
index fd99b0282c..3ad1e53fee 100644
--- 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/apache/tuscany/sca/osgi/service/discovery/impl/Discovery.java
@@ -1,20 +1,22 @@
/*
- * Copyright (c) OSGi Alliance (2008, 2009). All Rights Reserved.
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
*
- * 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
*
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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;
+package org.apache.tuscany.sca.osgi.service.discovery.impl;
/**
* Every Discovery Provider registers a service implementing this interface.
@@ -23,9 +25,7 @@ package org.osgi.service.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.
+ * search for remote services.
* <p>
* Discovery service implementations usually rely on some discovery protocols or
* other information distribution means.
@@ -39,21 +39,21 @@ public interface Discovery {
* <p>
* Value of this property is of type <code>String</code>.
*/
- static final String PRODUCT_NAME = "osgi.remote.discovery.product";
+ 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";
+ 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";
+ String VENDOR_NAME = "osgi.remote.discovery.vendor";
/**
* ServiceRegistration property that lists the discovery protocols used by
@@ -62,5 +62,5 @@ public interface Discovery {
* Value of this property is of type
* <code>Collection (&lt;? extends String&gt;)</code>.
*/
- static final String SUPPORTED_PROTOCOLS = "osgi.remote.discovery.supported_protocols";
+ String SUPPORTED_PROTOCOLS = "osgi.remote.discovery.supported_protocols";
}
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/osgi/service/discovery/impl/DiscoveryActivator.java
index 92951321d0..9a48dd25f7 100644
--- 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/osgi/service/discovery/impl/DiscoveryActivator.java
@@ -16,16 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.tuscany.sca.dosgi.discovery;
+package org.apache.tuscany.sca.osgi.service.discovery.impl;
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>();
@@ -37,12 +35,9 @@ public class DiscoveryActivator implements BundleActivator {
discoveryServices.add(new DomainDiscoveryService(context));
for (AbstractDiscoveryService service : discoveryServices) {
- Hashtable<String, Object> props = new Hashtable<String, Object>();
- props.put(Discovery.PRODUCT_NAME, "Apache Tuscany SCA");
- props.put(Discovery.PRODUCT_VERSION, "2.0.0");
- props.put(Discovery.VENDOR_NAME, "Apache Software Foundation");
+ service.start();
ServiceRegistration registration =
- context.registerService(Discovery.class.getName(), service, props);
+ context.registerService(Discovery.class.getName(), service, service.getProperties());
discoveryServiceRegistrations.add(registration);
}
}
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/osgi/service/discovery/impl/DomainDiscoveryService.java
index 5f63e3c92d..cd091f3258 100644
--- 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/osgi/service/discovery/impl/DomainDiscoveryService.java
@@ -17,27 +17,17 @@
* 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;
+package org.apache.tuscany.sca.osgi.service.discovery.impl;
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.osgi.service.remoteadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.impl.EndpointDescriptionImpl;
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
@@ -45,15 +35,12 @@ import org.osgi.service.discovery.ServiceEndpointDescription;
public class DomainDiscoveryService extends AbstractDiscoveryService implements EndpointListener {
private EndpointRegistry endpointRegistry;
- private Map<String, ServiceRegistration> endpointRegistrations =
- new ConcurrentHashMap<String, ServiceRegistration>();
-
public DomainDiscoveryService(BundleContext context) {
super(context);
- init();
}
- private void init() {
+ public void start() {
+ super.start();
getExtensionPointRegistry();
this.endpointRegistry =
registry.getExtensionPoint(UtilityExtensionPoint.class).getUtility(EndpointRegistry.class);
@@ -69,8 +56,8 @@ public class DomainDiscoveryService extends AbstractDiscoveryService implements
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));
@@ -84,27 +71,31 @@ public class DomainDiscoveryService extends AbstractDiscoveryService implements
// 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);
+ } else
+ */
+ {
+ // Notify the endpoint listeners
+ EndpointDescription description = createEndpointDescription(endpoint);
+ endpointChanged(description, ADDED);
}
}
+ private EndpointDescription createEndpointDescription(Endpoint endpoint) {
+ EndpointDescription description = new EndpointDescriptionImpl(endpoint);
+ return description;
+ }
+
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);
+ // export services
+ } else
+ */
+ {
+ EndpointDescription description = createEndpointDescription(endpoint);
+ endpointChanged(description, REMOVED);
}
}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java
new file mode 100644
index 0000000000..0fbcbc8395
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/discovery/impl/LocalDiscoveryService.java
@@ -0,0 +1,208 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.osgi.service.discovery.impl;
+
+import static org.osgi.framework.Bundle.ACTIVE;
+import static org.osgi.framework.BundleEvent.STARTED;
+import static org.osgi.framework.BundleEvent.STOPPING;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+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 java.util.logging.Level;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.tuscany.sca.assembly.AssemblyFactory;
+import org.apache.tuscany.sca.contribution.processor.ExtensibleStAXArtifactProcessor;
+import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor;
+import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessorExtensionPoint;
+import org.apache.tuscany.sca.core.FactoryExtensionPoint;
+import org.apache.tuscany.sca.core.UtilityExtensionPoint;
+import org.apache.tuscany.sca.implementation.osgi.ServiceDescription;
+import org.apache.tuscany.sca.implementation.osgi.ServiceDescriptions;
+import org.apache.tuscany.sca.monitor.Monitor;
+import org.apache.tuscany.sca.monitor.MonitorFactory;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.impl.EndpointDescriptionImpl;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+
+public class LocalDiscoveryService extends AbstractDiscoveryService implements BundleListener {
+ private XMLInputFactory xmlInputFactory;
+ private XMLOutputFactory xmlOutputFactory;
+ private AssemblyFactory assemblyFactory;
+ private StAXArtifactProcessor processor;
+
+ public LocalDiscoveryService(BundleContext context) {
+ super(context);
+ }
+
+ public void start() {
+ super.start();
+ context.addBundleListener(this);
+ getExtensionPointRegistry();
+
+ FactoryExtensionPoint factories = registry.getExtensionPoint(FactoryExtensionPoint.class);
+ this.assemblyFactory = factories.getFactory(AssemblyFactory.class);
+ this.xmlInputFactory = factories.getFactory(XMLInputFactory.class);
+ this.xmlOutputFactory = factories.getFactory(XMLOutputFactory.class);
+ StAXArtifactProcessorExtensionPoint processors =
+ registry.getExtensionPoint(StAXArtifactProcessorExtensionPoint.class);
+ UtilityExtensionPoint utilities = this.registry.getExtensionPoint(UtilityExtensionPoint.class);
+ MonitorFactory monitorFactory = utilities.getUtility(MonitorFactory.class);
+ Monitor monitor = null;
+ if (monitorFactory != null) {
+ monitor = monitorFactory.createMonitor();
+ }
+ processor = new ExtensibleStAXArtifactProcessor(processors, xmlInputFactory, xmlOutputFactory, monitor);
+ 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) {
+ List<URL> urls = findServiceDescriptionsDocuments(b);
+ if (urls == null || urls.isEmpty()) {
+ return;
+ }
+
+ List<ServiceDescription> serviceDescriptions = new ArrayList<ServiceDescription>();
+
+ for (URL url : urls) {
+ ServiceDescriptions descriptions = null;
+ try {
+ descriptions = loadServiceDescriptions(url);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, e.getMessage(), e);
+ }
+ if (descriptions != null) {
+ serviceDescriptions.addAll(descriptions);
+ }
+ }
+
+ for (ServiceDescription sd : serviceDescriptions) {
+ EndpointDescription sed = createEndpointDescription(sd);
+ servicesInfo.put(sed, b);
+ serviceDescriptionAdded(sed);
+ }
+ }
+
+ private EndpointDescription createEndpointDescription(ServiceDescription sd) {
+ // Endpoint endpoint = assemblyFactory.createEndpoint();
+ EndpointDescription sed = new EndpointDescriptionImpl(sd.getInterfaces(), sd.getProperties());
+ return sed;
+ }
+
+ private List<URL> findServiceDescriptionsDocuments(Bundle b) {
+ List<URL> urls = null;
+ String path = (String)b.getHeaders().get(ServiceDescriptions.REMOTE_SERVICE_HEADER);
+ if (path == null) {
+ Enumeration<URL> files = b.findEntries(ServiceDescriptions.REMOTE_SERVICE_FOLDER, "*.xml", false);
+ if (files == null || !files.hasMoreElements()) {
+ return Collections.emptyList();
+ } else {
+ urls = new ArrayList<URL>();
+ while (files.hasMoreElements()) {
+ urls.add(files.nextElement());
+ }
+ }
+ } else {
+ URL url = b.getEntry(path);
+ if (url != null) {
+ urls = Collections.singletonList(url);
+ } else {
+ urls = Collections.emptyList();
+ }
+ }
+ return urls;
+ }
+
+ private ServiceDescriptions loadServiceDescriptions(URL url) throws Exception {
+ InputStream is = url.openStream();
+ try {
+ XMLStreamReader reader = xmlInputFactory.createXMLStreamReader(is);
+ reader.nextTag();
+ Object model = processor.read(reader);
+ if (model instanceof ServiceDescriptions) {
+ return (ServiceDescriptions)model;
+ } else {
+ return null;
+ }
+ } finally {
+ is.close();
+ }
+ }
+
+ private void removeServicesDeclaredInBundle(Bundle bundle) {
+ for (Iterator<Map.Entry<EndpointDescription, Bundle>> i = servicesInfo.entrySet().iterator(); i.hasNext();) {
+ Entry<EndpointDescription, Bundle> entry = i.next();
+ if (entry.getValue().equals(bundle)) {
+ serviceDescriptionRemoved(entry.getKey());
+ i.remove();
+ }
+ }
+ }
+
+ private void serviceDescriptionAdded(EndpointDescription endpointDescription) {
+ endpointChanged(endpointDescription, ADDED);
+ }
+
+ private void serviceDescriptionRemoved(EndpointDescription endpointDescription) {
+ endpointChanged(endpointDescription, REMOVED);
+ }
+
+ public void stop() {
+ context.removeBundleListener(this);
+ super.stop();
+ }
+
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointDescription.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointDescription.java
new file mode 100644
index 0000000000..482aa62a23
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointDescription.java
@@ -0,0 +1,146 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.service.remoteadmin;
+
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.Version;
+
+/**
+ * A description of an endpoint that provides sufficient information for a
+ * compatible distribution provider to create a connection to this endpoint An
+ * Endpoint Description is easy to transfer between different systems. This
+ * allows it to be used as a communications device to convey available endpoint
+ * information to nodes in a network. An Endpoint Description reflects the
+ * perspective of an importer. That is, the property keys have been chosen to
+ * match filters that are created by client bundles that need a service.
+ *
+ * @Immutable
+ */
+public interface EndpointDescription {
+ /**
+ * Returns the configuration types. A distribution provider exports a
+ * service with an endpoint. This endpoint uses some kind of communications
+ * protocol with a set of configuration parameters. There are many different
+ * types but each endpoint is configured by only one configuration type.
+ * However, a distribution provider can be aware of different configuration
+ * types and provide synonyms to increase the change a receiving distributon
+ * provider can create a connection to this endpoint. This value represents
+ * the RemoteConstants.SERVICE_IMPORTED_CONFIGS
+ *
+ * @return Returns The configuration type used for the associated endpoint
+ * and optionally synonyms.
+ */
+ public List<String> getConfigurationTypes();
+
+ /**
+ * Return the list of intents implemented by this endpoint. The intents are
+ * based on the service.intents on an imported service, except for any
+ * intents that are additionally provided by the importing distribution
+ * provider. All qualified intents must have been expanded. The property the
+ * intents come from is RemoteConstants.SERVICE_INTENTS
+ *
+ * @return A list of expanded intents that are provided by this endpoint.
+ */
+ public List<String> getIntents();
+
+ /**
+ * Answer the list of interfaces implemented by the exported service. If
+ * this Endpoint Description does not map to a service, then this List must
+ * be empty. The value of the interfaces is derived from the objectClass
+ * property.
+ *
+ * @return The list of Java interface names accessible by this endpoint
+ */
+ public List<String> getInterfaces();
+
+ /**
+ * Answer the version of the given interface. The version is encoded by
+ * prefixing the given interface name with endpoint.version., and then using
+ * this as a property key. The value must then be the Version object. For
+ * example: endpoint.version.com.acme.Foo
+ *
+ * @param name The name of the interface for which a version is requested
+ * @return Returns The version of the given interface or null if the
+ * interface has no version in this Endpoint Description
+ */
+ public Version getInterfaceVersion(String name);
+
+ /**
+ * Returns An immutable map referring to the properties of this Endpoint
+ * Description.
+ *
+ * @return Returns all endpoint properties.
+ */
+ public Map<String, Object> getProperties();
+
+ /**
+ * Returns the universally unique id for the service exported through this
+ * endpoint. Each service in the OSGi service registry has a universally
+ * unique id. The UUID can be used to detect that two Endpoint Descriptions
+ * really refer to the same registered service instance in some remote
+ * framework. This UUID can be used to filter out duplicate ways of
+ * communicating with the same service. The service UUID is constructed from
+ * two properties. It is first the org.osgi.framework.uuid System property
+ * set by the framework or through configuration. This property must
+ * uniquely represents the UUID of a framework instance. This UUID must not
+ * contain any dots ('.' .). This is suffixed with a dot and then the
+ * service.id service property of the service.
+ * <p>
+ * For example: 72dc5fd9-5f8f-4f8f-9821-9ebb433a5b72.121
+ * <p>
+ * If this Endpoint Description does not map to a remote OSGi service, for
+ * example some web service, then the Endpoint Description must not have a
+ * service UUID. If two endpoints have the same URI, then they must refer to
+ * the same OSGi service.
+ *
+ * @return Returns Unique id of a service or null if this Endpoint
+ * Description does not relate to an OSGi service
+ */
+ public String getRemoteServiceID();
+
+ /**
+ * Returns the endpoint's URI. The URI is an opaque id for an endpoint in
+ * URI form. No two different endpoints must have the same URI, two Endpoint
+ * Descriptions with the same URI must represent the same endpoint. The
+ * value of the URI is stored in the RemoteConstants.ENDPOINT_URI property.
+ *
+ * @return Returns The URI of the endpoint, never null.
+ */
+ public URI getURI();
+
+ /**
+ * Two endpoints are equal if their URIs are equal, the hash code is
+ * therefore derived from the URI.
+ */
+ public int hashCode();
+
+ /**
+ * Two endpoints are equal if their URIs are equal.
+ *
+ * @param other
+ * @return
+ */
+ public boolean equals(Object other);
+
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointListener.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointListener.java
new file mode 100644
index 0000000000..033897ba7b
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointListener.java
@@ -0,0 +1,101 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.osgi.service.remoteadmin;
+
+/**
+ * A whiteboard service that represents a listener for endpoints. An Endpoint
+ * Listener represents a participant in the distributed model that is interested
+ * in Endpoint Descriptions. This whiteboard service can be used in many
+ * different scenarios. However, the primary use case is to allow a remote
+ * controller to be informed of End Point Descriptions available in the network
+ * and inform the network about available End Point Descriptions. Both the
+ * network bundle and the controller bundle register a Endpoint Listener
+ * service. The controller informs the network bundle about End Points that it
+ * creates. The network bundles then uses a protocol like for example SLP to
+ * announce these local end-points to the network. If the network bundle
+ * discovers a new Endpoint through its discovery protocol, then it sends an End
+ * Point Description to all the End Point Listener services that are registered
+ * (except its own) that have specified an interest in that endpoint.
+ * <p>
+ * Endpoint Listener services can express their scope with the service property
+ * ENDPOINT_LISTENER_SCOPE. This service property is a list of filters. An
+ * Endpoint Description should only be given to a Endpoint Listener when there
+ * is at least one filter that matches the Endpoint Description properties.
+ * given to it. This filter model is quite flexible. For example, a discovery
+ * bundle is only interested in locally originating Endpoint Descriptions. The
+ * following filter ensure that it only sees local endpoints.
+ * <p>
+ * (org.osgi.framework.uuid=72dc5fd9-5f8f-4f8f-9821- 9ebb433a5b72)<br>
+ * In the same vein, a controller that is only interested in remote Endpoint
+ * Descriptions can use a filter like:
+ * <p>
+ * (!(org.osgi.framework.uuid=72dc5fd9-5f8f-4f8f-9821-9ebb433a5b72)) <br>
+ * Where in both cases, the given UUID is the UUID of the local framework that
+ * can be found in the Framework properties. The Endpoint Listener’s scope maps
+ * very well to the service hooks. A controller can just register all filters
+ * found from the Listener Hook as its scope. This will automatically provide it
+ * with all known endpoints that match the given scope, without having to
+ * inspect the filter string. In general, when an Endpoint Description is
+ * discovered, it should be dispatched to all registered Endpoint Listener
+ * services. If a new Endpoint Listener is registered, it should be informed
+ * about all currently known Endpoints that match its scope. If a getter of the
+ * Endpoint Listener service is unregistered, then all its registered Endpoint
+ * Description objects must be removed. The Endpoint Listener models a best
+ * effort approach. Participating bundles should do their utmost to keep the
+ * listeners up to date, but implementers should realize that many endpoints
+ * come through unreliable discovery processes.
+ *
+ * @ThreadSafe
+ */
+public interface EndpointListener {
+ /**
+ * Specifies the interest of this listener with filters. This listener is
+ * only interested in Endpoint Descriptions where its properties match the
+ * given filter. The type of this property must be String+.
+ */
+ public static final String ENDPOINT_LISTENER_SCOPE = "endpoint.listener.scope";
+
+ /**
+ * Register an endpoint with this listener. If the endpoint matches one of
+ * the filters registered with the ENDPOINT_LISTENER_SCOPE service property
+ * then this filter should be given as the matchedFilter parameter. When
+ * this service is first registered or it is modified, it should receive all
+ * known endpoints matching the filter.
+ *
+ * @param endpoint The Endpoint Description to be published
+ * @param matchedFilter matchedFilter The filter from the
+ * ENDPOINT_LISTENER_SCOPE that matched the endpoint, must not be
+ * null.
+ */
+ public void addEndpoint(EndpointDescription endpoint, String matchedFilter);
+
+ /**
+ * Remove the registration of an endpoint. If an endpoint that was
+ * registered with the addEndpoint method is no longer available then this
+ * method should be called. This will remove the endpoint from the listener.
+ * It is not necessary to remove endpoints when the service is unregistered
+ * or modified in such a way that not all endpoints match the interest
+ * filter anymore.
+ *
+ * @param endpoint The Endpoint Description that is no longer valid.
+ */
+ public void removeEndpoint(EndpointDescription endpoint);
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointPermission.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointPermission.java
new file mode 100644
index 0000000000..0658fa5ffc
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/EndpointPermission.java
@@ -0,0 +1,172 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.osgi.service.remoteadmin;
+
+import java.security.BasicPermission;
+import java.security.Permission;
+import java.security.PermissionCollection;
+
+/**
+ * A bundle’s authority to register or get a service.
+ * <ul>
+ * <li>The register action allows a bundle to register a service on the
+ * specified names.
+ * <li>The get action allows a bundle to detect a service and get it.
+ * EndpointPermission to get the specific service.
+ * </ul>
+ *
+ * @ThreadSafe
+ */
+public final class EndpointPermission extends BasicPermission {
+ private static final long serialVersionUID = 577543263888050488L;
+ /**
+ * The action string get.
+ */
+ public static final String EXPORT = "export";
+ /**
+ * The action string register.
+ */
+ public static final String IMPORT = "import";
+
+ private String actions;
+
+ /**
+ *Create a new EndpointPermission.
+ *
+ * @param name The service class name The name of the service is specified
+ * as a fully qualified class name. Wildcards may be used.
+ * <p>
+ * name ::= <class name> | <class name ending in ".*"> | *
+ * <p>
+ * Examples:
+ * <ul>
+ * <li>org.osgi.service.http.HttpService
+ * <li>org.osgi.service.http.*
+ * <li>*
+ * </ul>
+ * For the get action, the name can also be a filter expression.
+ * The filter gives access to the service properties as well as
+ * the following attributes:
+ * <p>
+ * <ul>
+ * <li>signer - A Distinguished Name chain used to sign the
+ * bundle publishing the service. Wildcards in a DN are not
+ * matched according to the filter string rules, but according to
+ * the rules defined for a DN chain.
+ * <li>location - The location of the bundle publishing the
+ * service.
+ * <li>id - The bundle ID of the bundle publishing the service.
+ * <li>name - The symbolic name of the bundle publishing the
+ * service.
+ * </ul>
+ * Since the above attribute names may conflict with service
+ * property names used by a service, you can prefix an attribute
+ * name with '@' in the filter expression to match against the
+ * service property and not one of the above attributes. Filter
+ * attribute names are processed in a case sensitive manner
+ * unless the attribute references a service property. Service
+ * properties names are case insensitive.
+ * <p>
+ * There are two possible actions: get and register. The get
+ * permission allows the owner of this permission to obtain a
+ * service with this name. The register permission allows the
+ * bundle to register a service under that name.
+ * @param actions actions get,register (canonical order)
+ * @throws IllegalArgumentException – If the specified name is a filter
+ * expression and either the specified action is not get or the
+ * filter has an invalid syntax.
+ */
+ public EndpointPermission(String name, String actions) {
+ super(name);
+ this.actions = actions;
+ }
+
+ /**
+ * Creates a new requested EndpointPermission object to be used by code that
+ * must perform checkPermission for the get action. EndpointPermission
+ * objects created with this constructor cannot be added to a
+ * EndpointPermission permission collection.
+ *
+ * @param endpoint The requested service.
+ * @param actions The action get.
+ * @throws IllegalArgumentException – If the specified action is not get or
+ * reference is null.
+ */
+ public EndpointPermission(EndpointDescription endpoint, String actions) {
+ super(null);
+ this.actions = actions;
+ }
+
+ /**
+ * Determines the equality of two EndpointPermission objects. Checks that
+ * specified object has the same class name and action as this
+ * EndpointPermission. obj The object to test for equality.
+ *
+ * @return true if obj is a EndpointPermission, and has the same class name
+ * and actions as this EndpointPermission object; false otherwise.
+ */
+ public boolean equals(Object obj) {
+ return super.equals(obj);
+ }
+
+ /**
+ * Returns the canonical string representation of the actions. Always
+ * returns present actions in the following order: get, register.
+ *
+ * @return The canonical string representation of the actions.
+ */
+ public String getActions() {
+ return actions;
+ }
+
+ /**
+ * Returns the hash code value for this object. Returns Hash code value for
+ * this object.
+ *
+ * @return
+ */
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ /**
+ *Determines if a EndpointPermission object "implies" the specified
+ * permission.
+ *
+ * @param p The target permission to check.
+ * @return true if the specified permission is implied by this object; false
+ * otherwise. newPermissionCollection()
+ */
+ public boolean implies(Permission p) {
+ return super.implies(p);
+ }
+
+ /**
+ * Returns a new PermissionCollection object for storing EndpointPermission
+ * objects.
+ *
+ * @return A new PermissionCollection object suitable for storing
+ * EndpointPermission objects.
+ */
+ public PermissionCollection newPermissionCollection() {
+ return super.newPermissionCollection();
+ }
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ExportRegistration.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ExportRegistration.java
new file mode 100644
index 0000000000..49c1d14550
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ExportRegistration.java
@@ -0,0 +1,71 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.service.remoteadmin;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * An Export Registration associates a service to a local endpoint. The Export
+ * Registration can be used to delete the endpoint associated with an this
+ * registration. It is created with the
+ * RemoteAdmin.exportService(ServiceReference) method. When this Export
+ * Registration has been unregistered, the methods must all return null.
+ *
+ * @ThreadSafe
+ */
+public interface ExportRegistration {
+ /**
+ * Delete the local endpoint and disconnect any remote distribution
+ * providers. After this method returns, all the methods must return null.
+ * This method has no effect when the endpoint is already destroyed or being
+ * destroyed.
+ */
+ public void close();
+
+ /**
+ *Return the Endpoint Description that is created for this registration.
+ *
+ * @return the local Endpoint Description
+ */
+ public EndpointDescription getEndpointDescription();
+
+ /**
+ * Exception for any error during the import process. If the Remote Admin
+ * for some reasons is unable to create a registration, then it must return
+ * a Throwable from this method. In this case, all other methods must return
+ * on this interface must thrown an Illegal State Exception. If no error
+ * occurred, this method must return null. The error must be set before this
+ * Import Registration is returned. Asynchronously occurring errors must be
+ * reported to the log.
+ *
+ * @return The exception that occurred during the creation of the
+ * registration or null if no exception occurred.
+ */
+ public Throwable getException();
+
+ /**
+ * Return the service being exported.
+ *
+ * @return The service being exported, must be null when this registration
+ * is unregistered.
+ */
+ public ServiceReference getExportedService();
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ImportRegistration.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ImportRegistration.java
new file mode 100644
index 0000000000..fdf70f849f
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/ImportRegistration.java
@@ -0,0 +1,70 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.service.remoteadmin;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * An Import Registration associates an active proxy service to a remote
+ * endpoint. The Import Registration can be used to delete the proxy associated
+ * with an endpoint. It is created with the RemoteAdmin.importService method.
+ *
+ * @ThreadSafe
+ */
+public interface ImportRegistration {
+
+ /**
+ * Unregister this Import Registration. This must close the connection to
+ * the end endpoint unregister the proxy. After this method returns, all
+ * other methods must return null. This method has no effect when the
+ * service is already unregistered or in the process off.
+ */
+ public void close();
+
+ /**
+ * Exception for any error during the import process. If the Remote Admin
+ * for some reasons is unable to create a registration, then it must return
+ * a Throwable from this method. In this case, all other methods must return
+ * on this interface must thrown an Illegal State Exception. If no error
+ * occurred, this method must return null. The error must be set before this
+ * Import Registration is returned. Asynchronously occurring errors must be
+ * reported to the log.
+ *
+ * @return The exception that occurred during the creation of the
+ * registration or null if no exception occurred.
+ */
+ public Throwable getException();
+
+ /**
+ * Answer the associated remote Endpoint Description.
+ *
+ * @return A Endpoint Description for the remote endpoint.
+ */
+ public EndpointDescription getImportedEndpointDescription();
+
+ /**
+ * Answer the associated Service Reference for the proxy to the endpoint.
+ *
+ * @return A Service Reference to the proxy for the endpoint.
+ */
+ public ServiceReference getImportedService();
+
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdmin.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdmin.java
new file mode 100644
index 0000000000..c1b2f3d826
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdmin.java
@@ -0,0 +1,101 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.osgi.service.remoteadmin;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * A Remote Service Admin manages the import and export of services. A
+ * Distribution Provider can expose a control interface. This interface allows
+ * the a remote controller to control the export and import of services. The API
+ * allows a remote controller to export a service, to import a service, and find
+ * out about the current imports and exports.
+ *
+ * @ThreadSafe
+ */
+
+public interface RemoteAdmin {
+ /**
+ * Export a service to an endpoint. The Remote Service Admin must create an
+ * endpoint that can be used by other Distrbution Providers to connect to
+ * this Remote Service Admin and use the exported service. This method can
+ * return null if the service could not be exported.
+ *
+ * @param ref The Service Reference to export
+ * @return Export Registration that combines the Endpoint Description and
+ * the Service Reference or <code>null</code if the service could
+ * not be exported
+ */
+ public List<ExportRegistration> exportService(ServiceReference ref);
+
+ /**
+ * Export a service to a given endpoint. The Remote Service Admin must
+ * create an endpoint from the given description that can be used by other
+ * Distrbution Providers to connect to this Remote Service Admin and use the
+ * exported service. This method can return null if the service could not be
+ * exported because the endpoint could not be implemented by this Remote
+ * Service Admin.
+ *
+ * @param ref The Service Reference to export
+ * @param properties The properties to create a local endpoint that can be
+ * implemented by this Remote Service Admin. If this is null, the
+ * endpoint will be determined by the properties on the service,
+ * @see exportService(ServiceReference). The properties are the same as
+ * given for an exported service. They are overlaid over any properties
+ * the service defines
+ * @return Export Registration that combines the Endpoint Description and
+ * the Service Reference or <code>null</code if the service could
+ * not be exported
+ */
+ public List<ExportRegistration> exportService(ServiceReference ref, Map<String, Object> properties);
+
+ /**
+ * Answer the currently active Export Registrations.
+ *
+ * @return Returns A collection of Export Registrations that are currently
+ * active.
+ */
+ public Collection<ExportRegistration> getExportedServices();
+
+ /**
+ * Answer the currently active Import Registrations.
+ *
+ * @return Returns A collection of Import Registrations that are currently
+ * active.
+ */
+ public Collection<ImportRegistration> getImportedEndpoints();
+
+ /**
+ * Import a service from an endpoint. The Remote Service Admin must use the
+ * given endpoint to create a proxy. This method can return null if the
+ * service could not be imported.
+ *
+ * @param endpoint The Endpoint Description to be used for import
+ * @return An Import Registration that combines the Endpoint Description and
+ * the Service Reference or <code>null</code if the endpoint could
+ * not be imported
+ */
+ public ImportRegistration importService(EndpointDescription endpoint);
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminEvent.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminEvent.java
new file mode 100644
index 0000000000..02ad9c81fb
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminEvent.java
@@ -0,0 +1,148 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.osgi.service.remoteadmin;
+
+import java.util.EventObject;
+
+import org.osgi.framework.Bundle;
+
+/**
+ * Provides the event information for a Remote Admin event.
+ *
+ * @Immutable
+ */
+public class RemoteAdminEvent extends EventObject {
+ /**
+ * A fatal exporting error occurred. The Export Registration has been
+ * closed.
+ */
+ public final static int EXPORT_ERROR = 0x1;
+
+ /**
+ * Add an export registration. The Remote Services Admin will call this
+ * method when it exports a service. When this service is registered,
+ * the Remote Service Admin must notify the listener of all existing
+ * Export Registrations.
+ */
+ public final static int EXPORT_REGISTRATION = 0x2;
+
+ /**
+ * Remove an export registration. The Remote Services Admin will call
+ * this method when it removes the export of a service.
+ */
+ public final static int EXPORT_UNREGISTRATION = 0x4;
+
+ /**
+ * A problematic situation occurred, the export is still active.
+ */
+ public final static int EXPORT_WARNING = 0x8;
+
+ /**
+ * A fatal importing error occurred. The Import Registration has been
+ * closed.
+ */
+ public final static int IMPORT_ERROR = 0x10;
+
+ /**
+ * Add an import registration. The Remote Services Admin will call this
+ * method when it imports a service. When this service is registered,
+ * the Remote Service Admin must notify the listener of all existing
+ * Import Registrations.
+ */
+ public final static int IMPORT_REGISTRATION = 0x20;
+
+ /**
+ * Remove an import registration. The Remote Services Admin will call
+ * this method when it removes the import of a service.
+ */
+ public final static int IMPORT_UNREGISTRATION = 0x40;
+
+ /**
+ * A problematic situation occurred, the import is still active.
+ */
+ public final static int IMPORT_WARNING = 0x80;
+
+ private static final long serialVersionUID = -6562225073284539118L;
+ private Throwable exception;
+ private ExportRegistration exportRegistration;
+ private ImportRegistration importRegistration;
+ private int type;
+
+ public RemoteAdminEvent(Bundle source, int type, ExportRegistration registration, Throwable exception) {
+ super(source);
+ this.type = type;
+ this.exportRegistration = registration;
+ this.exception = exception;
+ }
+
+ public RemoteAdminEvent(Bundle source, int type, ImportRegistration registration, Throwable exception) {
+ super(source);
+ this.type = type;
+ this.importRegistration = registration;
+ this.exception = exception;
+ }
+
+ /**
+ * Returns the exception
+ *
+ * @return
+ */
+ public Throwable getException() {
+ return exception;
+ }
+
+ /**
+ * Returns the exportRegistration
+ *
+ * @return
+ */
+ public ExportRegistration getExportRegistration() {
+ return exportRegistration;
+ }
+
+ /**
+ * Returns the importRegistration
+ *
+ * @return
+ */
+ public ImportRegistration getImportRegistration() {
+ return importRegistration;
+ }
+
+ /**
+ * Returns the source
+ *
+ * @return
+ */
+ public Bundle getSource() {
+ return (Bundle)source;
+ }
+
+ /**
+ * Returns the type
+ *
+ * @return
+ */
+ public int getType() {
+ return type;
+ }
+
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminListener.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminListener.java
new file mode 100644
index 0000000000..21ffee971f
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteAdminListener.java
@@ -0,0 +1,36 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.service.remoteadmin;
+
+/**
+ * A Remote Service Admin Listener is notified asynchronously of any export or
+ * import registrations and unregistrations.
+ *
+ * @ThreadSafe
+ */
+public interface RemoteAdminListener {
+ /**
+ * Notify of a remote admin event
+ *
+ * @param event The remote admin event
+ */
+ public void remoteAdminEvent(RemoteAdminEvent event);
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteConstants.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteConstants.java
new file mode 100644
index 0000000000..f55bb030d7
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/RemoteConstants.java
@@ -0,0 +1,143 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.osgi.service.remoteadmin;
+
+/**
+ * Provide the definition of the constants used in the Remote Services API.
+ */
+public class RemoteConstants {
+ /**
+ * The property key for the endpoint URI. This is a unique id for an
+ * endpoint following the URI syntax. As far as this specification is
+ * concerned, this unique id is opaque.
+ */
+ public static final String ENDPOINT_URI = "endpoint.uri";
+
+ /**
+ * The key for a framework property that defines the UUID of the framework.
+ * The property must be set by the framework or through configuration before
+ * the VM is started or some bundle. The value must be a Universally Unique
+ * Id, it must not contain any dots ('.' .).
+ */
+ public static final String FRAMEWORK_UUID = "org.osgi.framework.uuid";
+
+ /**
+ * The configuration types supported by this Distribution Provider. Services
+ * that are suitable for distribution list the configuration types that
+ * describe the configuration information for that service in the
+ * SERVICE_EXPORTED_CONFIGS or SERVICE_IMPORTED_CONFIGS property. A
+ * distribution provider must register a service that has this property and
+ * enumerate all configuration types that it supports. The type of this
+ * property String+
+ *
+ * @see SERVICE_EXPORTED_CONFIGS
+ * @see SERVICE_IMPORTED_CONFIGS
+ */
+ public static final String REMOTE_CONFIGS_SUPPORTED = "remote.configs.supported";
+
+ /**
+ * Service property that lists the intents supported by the distribution
+ * provider. Each distribution provider must register a service that has
+ * this property and enumerate all the supported intents, having any
+ * qualified intents expanded. The value of this property is of type
+ * String+.
+ *
+ * @see SERVICE_INTENTS
+ * @see SERVICE_EXPORTED_INTENTS
+ * @see SERVICE_EXPORTED_INTENTS_EXTRA
+ */
+ public static final String REMOTE_INTENTS_SUPPORTED = "remote.intents.supported";
+
+ /**
+ * A list of configuration types that should be used to export the service.
+ * Configuration types can be synonymous or alternatives. In principle, a
+ * distribution provider should create an endpoint for each recognized
+ * configuration type, the deployer is responsible that synonyms do not
+ * clash. Each configuration type has an associated specification that
+ * describes how the configuration data for the exported service is
+ * represented in an OSGi framework. The value of this property is of type
+ * String+.
+ */
+ public static final String SERVICE_EXPORTED_CONFIGS = "service.exported.configs";
+
+ /**
+ * A list of intents that the distribution provider must implement to
+ * distribute the service. Intents listed in this property are reserved for
+ * intents that are critical for the code to function correctly, for
+ * example, ordering of messages. These intents should not be configurable.
+ * The value of this property is of type String+.
+ */
+ public static final String SERVICE_EXPORTED_INTENTS = "service.exported.intents";
+
+ /**
+ * Extra intents configured in addition to the the intents specified in
+ * SERVICE_EXPORTED_INTENTS. These intents are merged with the service.
+ * exported.intents and therefore have the same semantics. They are extra,
+ * so that the SERVICE_EXPORTED_INTENTS can be set by the bundle developer
+ * and this property is then set by the administrator/deployer. Bundles
+ * should make this property configurable, for example through the
+ * Configuration Admin service. The value of this property is of type
+ * String+.
+ */
+ public static final String SERVICE_EXPORTED_INTENTS_EXTRA = "service.exported.intents.extra";
+
+ /**
+ * Defines the interfaces under which this service can be exported. This
+ * list must be a subset of the types listed in the objectClass service
+ * property. The single value of an asterisk ('*' *) indicates all
+ * interfaces in the registration's objectClass property (not classes). It
+ * is highly recommended to only export interfaces and not concrete classes
+ * due to the complexity of creating proxies for some type of classes. The
+ * value of this property is of type String+.
+ */
+ public static final String SERVICE_EXPORTED_INTERFACES = "service.exported.interfaces";
+
+ /**
+ * Must be set by a distribution provider to true when it registers the
+ * end-point proxy as an imported service. Can be used by a bundle to
+ * prevent it from getting an imported service. The value of this property
+ * is not defined, setting it is sufficient.
+ */
+ public static final String SERVICE_IMPORTED = "service.imported";
+
+ /**
+ * The configuration type used to import this services, as described in
+ * SERVICE_EXPORTED_CONFIGS. Any associated properties for this
+ * configuration types must be properly mapped to the importing system. For
+ * example, a URL in these properties must point to a valid resource when
+ * used in the importing framework. Configuration types in this property
+ * must be synonymous. The value of this property is of type String+.
+ */
+ public final String SERVICE_IMPORTED_CONFIGS = "service.imported.configs";
+
+ /**
+ * A list of intents that this service implements. This property has dual
+ * purpose. A bundle can use this service property to notify the
+ * distribution provider that these intents are already implemented by the
+ * exported service object. For an imported service, a distribution provider
+ * must use this property to convey the combined intents of the exporting
+ * service and the intents that the distribution providers add. To export a
+ * service, a distribution provider must recognize all these intents and
+ * expand any qualified intents. The value of this property is of type
+ * String+.
+ */
+ public static final String SERVICE_INTENTS = "service.intents";
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointDescriptionImpl.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointDescriptionImpl.java
new file mode 100644
index 0000000000..6557b185ee
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointDescriptionImpl.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.osgi.service.remoteadmin.impl;
+
+import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteConstants.SERVICE_EXPORTED_INTERFACES;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.interfacedef.Interface;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterface;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteConstants;
+import org.apache.tuscany.sca.policy.Intent;
+import org.osgi.framework.Version;
+
+/**
+ * Implementation of {@link EndpointDescription}
+ */
+public class EndpointDescriptionImpl implements EndpointDescription {
+ private Endpoint endpoint;
+
+ private static final Logger logger = Logger.getLogger(EndpointDescriptionImpl.class.getName());
+
+ private Collection<String> interfaces;
+ private Map<String, Object> properties;
+
+ public EndpointDescriptionImpl(Collection<String> interfaceNames) {
+ this(interfaceNames, Collections.<String, Object> singletonMap(SERVICE_EXPORTED_INTERFACES, interfaceNames));
+ }
+
+ public EndpointDescriptionImpl(Collection<String> interfaceNames, Map<String, Object> remoteProperties) {
+ this.interfaces = new HashSet<String>(interfaceNames);
+ this.properties =
+ remoteProperties == null ? new HashMap<String, Object>() : new HashMap<String, Object>(remoteProperties);
+ this.properties.put(RemoteConstants.SERVICE_EXPORTED_INTERFACES, interfaceNames);
+ this.endpoint = (Endpoint) properties.get(Endpoint.class.getName());
+ }
+
+ public EndpointDescriptionImpl(String interfaceName) {
+ this(Collections.singleton(interfaceName));
+ }
+
+ public EndpointDescriptionImpl(Endpoint endpoint) {
+ this(getInterfaces(endpoint), getProperties(endpoint));
+ this.endpoint = endpoint;
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription#getConfigurationTypes()
+ */
+ public List<String> getConfigurationTypes() {
+ return Collections.singletonList("sca");
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription#getIntents()
+ */
+ public List<String> getIntents() {
+ List<String> intents = new ArrayList<String>();
+ for (Intent intent : endpoint.getRequiredIntents()) {
+ intents.add(intent.getName().toString());
+ }
+ return intents;
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription#getInterfaceVersion(java.lang.String)
+ */
+ public Version getInterfaceVersion(String name) {
+ return Version.emptyVersion;
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription#getInterfaces()
+ */
+ public List<String> getInterfaces() {
+ return new ArrayList<String>(interfaces);
+ }
+
+ private static List<String> getInterfaces(Endpoint endpoint) {
+ Interface intf = endpoint.getInterfaceContract().getInterface();
+ JavaInterface javaInterface = (JavaInterface)intf;
+ return Collections.singletonList(javaInterface.getName());
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription#getProperties()
+ */
+ public Map<String, Object> getProperties() {
+ return Collections.unmodifiableMap(properties); // endpoint.getService().getExtensions();
+ }
+
+ private static Map<String, Object> getProperties(Endpoint endpoint) {
+ Map<String, Object> props = new HashMap<String, Object>();
+ props.put(RemoteConstants.ENDPOINT_URI, endpoint.getURI());
+ props.put(RemoteConstants.SERVICE_EXPORTED_CONFIGS, new String[] {"sca"});
+ props.put(Endpoint.class.getName(), endpoint);
+ return props;
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription#getRemoteServiceID()
+ */
+ public String getRemoteServiceID() {
+ return null; // endpoint.getService().getExtensions();
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription#getURI()
+ */
+ public URI getURI() {
+ if (endpoint != null) {
+ return URI.create(endpoint.getURI());
+ } else {
+ return URI.create("urn:" + UUID.randomUUID());
+ }
+ }
+
+ public Endpoint getEndpoint() {
+ return endpoint;
+ }
+
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointIntrospector.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointIntrospector.java
new file mode 100644
index 0000000000..59f8d3c07b
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointIntrospector.java
@@ -0,0 +1,482 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.osgi.service.remoteadmin.impl;
+
+import static org.apache.tuscany.sca.assembly.Base.SCA11_TUSCANY_NS;
+import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SCA_BINDINGS;
+import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTENTS;
+import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTENTS_EXTRA;
+import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTERFACES;
+import static org.osgi.framework.Constants.OBJECTCLASS;
+import static org.osgi.framework.Constants.SERVICE_ID;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.tuscany.sca.assembly.AssemblyFactory;
+import org.apache.tuscany.sca.assembly.Base;
+import org.apache.tuscany.sca.assembly.Binding;
+import org.apache.tuscany.sca.assembly.Component;
+import org.apache.tuscany.sca.assembly.ComponentReference;
+import org.apache.tuscany.sca.assembly.ComponentService;
+import org.apache.tuscany.sca.assembly.Composite;
+import org.apache.tuscany.sca.assembly.Endpoint;
+import org.apache.tuscany.sca.assembly.Reference;
+import org.apache.tuscany.sca.assembly.Service;
+import org.apache.tuscany.sca.contribution.Contribution;
+import org.apache.tuscany.sca.contribution.ContributionFactory;
+import org.apache.tuscany.sca.contribution.processor.ContributionReadException;
+import org.apache.tuscany.sca.contribution.processor.ExtensibleStAXArtifactProcessor;
+import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor;
+import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessorExtensionPoint;
+import org.apache.tuscany.sca.contribution.resolver.ExtensibleModelResolver;
+import org.apache.tuscany.sca.contribution.resolver.ModelResolver;
+import org.apache.tuscany.sca.contribution.resolver.ModelResolverExtensionPoint;
+import org.apache.tuscany.sca.core.ExtensionPointRegistry;
+import org.apache.tuscany.sca.core.FactoryExtensionPoint;
+import org.apache.tuscany.sca.core.UtilityExtensionPoint;
+import org.apache.tuscany.sca.implementation.osgi.BindingDescriptions;
+import org.apache.tuscany.sca.implementation.osgi.OSGiImplementation;
+import org.apache.tuscany.sca.implementation.osgi.OSGiImplementationFactory;
+import org.apache.tuscany.sca.implementation.osgi.OSGiProperty;
+import org.apache.tuscany.sca.implementation.osgi.ServiceDescriptionsFactory;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterface;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceContract;
+import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceFactory;
+import org.apache.tuscany.sca.monitor.Monitor;
+import org.apache.tuscany.sca.monitor.MonitorFactory;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription;
+import org.apache.tuscany.sca.policy.Intent;
+import org.apache.tuscany.sca.policy.PolicyFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Introspect an OSGi Service to create an SCA composite that contains a single component with
+ * implementation.osgi
+ */
+public class EndpointIntrospector {
+ private AssemblyFactory assemblyFactory;
+ private ContributionFactory contributionFactory;
+ private OSGiImplementationFactory implementationFactory;
+ private ServiceDescriptionsFactory serviceDescriptionsFactory;
+ private PolicyFactory policyFactory;
+ private ExtensionPointRegistry registry;
+ private FactoryExtensionPoint factories;
+ private ModelResolverExtensionPoint modelResolvers;
+ private XMLInputFactory xmlInputFactory;
+ private XMLOutputFactory xmlOutputFactory;
+ private JavaInterfaceFactory javaInterfaceFactory;
+ private StAXArtifactProcessor processor;
+
+ /**
+ * @param intentName
+ * @return
+ */
+ private static QName getQName(String intentName) {
+ QName qname;
+ if (intentName.startsWith("{")) {
+ int i = intentName.indexOf('}');
+ if (i != -1) {
+ qname = new QName(intentName.substring(1, i), intentName.substring(i + 1));
+ } else {
+ throw new IllegalArgumentException("Invalid intent: " + intentName);
+ }
+ } else {
+ // Default to SCA namespace
+ qname = new QName(Base.SCA11_NS, intentName);
+ }
+ return qname;
+ }
+
+ /**
+ * @param context TODO
+ * @param registry
+ */
+ public EndpointIntrospector(BundleContext context, ExtensionPointRegistry registry) {
+ super();
+ this.registry = registry;
+ this.factories = registry.getExtensionPoint(FactoryExtensionPoint.class);
+ this.modelResolvers = registry.getExtensionPoint(ModelResolverExtensionPoint.class);
+ this.assemblyFactory = factories.getFactory(AssemblyFactory.class);
+ this.contributionFactory = factories.getFactory(ContributionFactory.class);
+ this.policyFactory = factories.getFactory(PolicyFactory.class);
+ this.implementationFactory = factories.getFactory(OSGiImplementationFactory.class);
+ this.serviceDescriptionsFactory = factories.getFactory(ServiceDescriptionsFactory.class);
+ this.xmlInputFactory = factories.getFactory(XMLInputFactory.class);
+ this.xmlOutputFactory = factories.getFactory(XMLOutputFactory.class);
+ this.javaInterfaceFactory = factories.getFactory(JavaInterfaceFactory.class);
+ StAXArtifactProcessorExtensionPoint processors =
+ registry.getExtensionPoint(StAXArtifactProcessorExtensionPoint.class);
+ UtilityExtensionPoint utilities = this.registry.getExtensionPoint(UtilityExtensionPoint.class);
+ MonitorFactory monitorFactory = utilities.getUtility(MonitorFactory.class);
+ Monitor monitor = null;
+ if (monitorFactory != null) {
+ monitor = monitorFactory.createMonitor();
+ }
+ processor = new ExtensibleStAXArtifactProcessor(processors, xmlInputFactory, xmlOutputFactory, monitor);
+ }
+
+ private Intent getIntent(String intent) {
+ QName name = getQName(intent);
+ Intent i = policyFactory.createIntent();
+ i.setName(name);
+ return i;
+ }
+
+ private List<Intent> getIntents(String[] intents) {
+ if (intents == null || intents.length == 0) {
+ return Collections.emptyList();
+ }
+ List<Intent> intentList = new ArrayList<Intent>();
+ for (String i : intents) {
+ Intent intent = getIntent(i);
+ if (intent != null) {
+ intentList.add(intent);
+ }
+ }
+ return intentList;
+ }
+
+ private Map<String, Object> getProperties(ServiceReference reference) {
+ String[] names = reference.getPropertyKeys();
+ if (names != null) {
+ Map<String, Object> properties = new HashMap<String, Object>();
+ for (String name : names) {
+ properties.put(name, reference.getProperty(name));
+ }
+ return properties;
+ } else {
+ return Collections.emptyMap();
+ }
+ }
+
+ /**
+ * Parse the Stringp[] to support values that are separated by comma
+ * @param interfaces
+ * @return
+ */
+ private String[] parse(String[] interfaces) {
+ if (interfaces == null) {
+ return null;
+ }
+ List<String> names = new ArrayList<String>();
+ for (String i : interfaces) {
+ String[] parts = i.split(",");
+ for (String p : parts) {
+ names.add(p.trim());
+ }
+ }
+ return names.toArray(new String[names.size()]);
+ }
+
+ /**
+ * Introspect a local OSGi Service represented by the ServiceReference to create
+ * an SCA service with the required intents and bindings
+ * @param reference The service reference for a local OSGi service
+ * @return An SCA contribution with a deployable composite for the SCA service
+ * @throws Exception
+ */
+ public Contribution introspect(ServiceReference reference) throws Exception {
+ Map<String, Object> properties = getProperties(reference);
+
+ OSGiProperty serviceID = implementationFactory.createOSGiProperty();
+ serviceID.setName(SERVICE_ID);
+ // The service.id is Long
+ serviceID.setValue(String.valueOf(reference.getProperty(SERVICE_ID)));
+
+ String id = "osgi.service." + UUID.randomUUID();
+ Composite composite = assemblyFactory.createComposite();
+ composite.setName(new QName(SCA11_TUSCANY_NS, id));
+
+ Component component = assemblyFactory.createComponent();
+ component.setName(id);
+ component.setAutowire(Boolean.TRUE);
+
+ composite.getComponents().add(component);
+
+ Bundle bundle = reference.getBundle();
+ OSGiImplementation implementation = implementationFactory.createOSGiImplementation();
+
+ implementation.setBundle(bundle);
+ component.setImplementation(implementation);
+ implementation.setUnresolved(false);
+
+ String[] remoteInterfaces = getStrings(reference.getProperty(SERVICE_EXPORTED_INTERFACES));
+ if (remoteInterfaces == null || remoteInterfaces.length > 0 && "*".equals(remoteInterfaces[0])) {
+ remoteInterfaces = getStrings(reference.getProperty(OBJECTCLASS));
+ } else {
+ remoteInterfaces = parse(remoteInterfaces);
+ }
+ for (String intf : remoteInterfaces) {
+ Service service = assemblyFactory.createService();
+ JavaInterfaceContract interfaceContract = javaInterfaceFactory.createJavaInterfaceContract();
+ Class<?> interfaceClass = bundle.loadClass(intf);
+ JavaInterface javaInterface = javaInterfaceFactory.createJavaInterface(interfaceClass);
+ interfaceContract.setInterface(javaInterface);
+ if (javaInterface.getCallbackClass() != null) {
+ interfaceContract.setCallbackInterface(javaInterfaceFactory.createJavaInterface(javaInterface
+ .getCallbackClass()));
+ }
+
+ service.setName(interfaceClass.getSimpleName());
+ service.setInterfaceContract(interfaceContract);
+
+ service.getExtensions().add(serviceID);
+
+ implementation.getServices().add(service);
+
+ ComponentService componentService = assemblyFactory.createComponentService();
+ componentService.setName(service.getName());
+ component.getServices().add(componentService);
+ componentService.setService(service);
+ }
+
+ String[] requiredIntents = getStrings(properties.get(SERVICE_EXPORTED_INTENTS));
+ List<Intent> intents = getIntents(requiredIntents);
+ String[] requiredIntentsExtra = getStrings(properties.get(SERVICE_EXPORTED_INTENTS_EXTRA));
+ List<Intent> extraIntents = getIntents(requiredIntentsExtra);
+
+ String[] bindingDocuments = getStrings(properties.get(SCA_BINDINGS));
+ List<Binding> bindings = loadBindings(reference.getBundle(), bindingDocuments);
+
+ for (ComponentService componentService : component.getServices()) {
+ componentService.getRequiredIntents().addAll(intents);
+ componentService.getRequiredIntents().addAll(extraIntents);
+ componentService.getBindings().addAll(bindings);
+ }
+
+ // FIXME: Should we scan the owning bundle to create the SCA contribution?
+ Contribution contribution = contributionFactory.createContribution();
+ contribution.setURI("urn:" + id);
+ contribution.setLocation(bundle.getEntry("/").toString());
+ contribution.getDeployables().add(composite);
+ ModelResolver modelResolver = new ExtensibleModelResolver(contribution, modelResolvers, factories);
+ contribution.setModelResolver(modelResolver);
+ contribution.setUnresolved(true);
+ return contribution;
+ }
+
+ public Contribution introspect(Bundle bundle, EndpointDescription endpoint) throws Exception {
+ if (endpoint instanceof EndpointDescriptionImpl) {
+ EndpointDescriptionImpl impl = (EndpointDescriptionImpl)endpoint;
+ Endpoint ep = impl.getEndpoint();
+ if (ep != null) {
+ return introspect(bundle, ep);
+ }
+ }
+ Map<String, Object> properties = endpoint.getProperties();
+ List<String> remoteInterfaces = endpoint.getInterfaces();
+
+ String id = "osgi.reference." + UUID.randomUUID();
+ Composite composite = assemblyFactory.createComposite();
+ composite.setName(new QName(Base.SCA11_TUSCANY_NS, id));
+
+ Component component = assemblyFactory.createComponent();
+ component.setName(id);
+ // component.setAutowire(Boolean.TRUE);
+
+ composite.getComponents().add(component);
+
+ OSGiImplementation implementation = implementationFactory.createOSGiImplementation();
+
+ implementation.setBundle(bundle);
+ component.setImplementation(implementation);
+ implementation.setUnresolved(false);
+
+ int count = 0;
+ for (String intf : remoteInterfaces) {
+ Reference reference = assemblyFactory.createReference();
+ JavaInterfaceContract interfaceContract = javaInterfaceFactory.createJavaInterfaceContract();
+ Class<?> interfaceClass = bundle.loadClass(intf);
+ JavaInterface javaInterface = javaInterfaceFactory.createJavaInterface(interfaceClass);
+ interfaceContract.setInterface(javaInterface);
+ if (javaInterface.getCallbackClass() != null) {
+ interfaceContract.setCallbackInterface(javaInterfaceFactory.createJavaInterface(javaInterface
+ .getCallbackClass()));
+ }
+
+ reference.setName("ref" + (count++));
+ reference.setInterfaceContract(interfaceContract);
+
+ implementation.getReferences().add(reference);
+
+ ComponentReference componentReference = assemblyFactory.createComponentReference();
+ componentReference.setName(reference.getName());
+ component.getReferences().add(componentReference);
+ componentReference.setReference(reference);
+ componentReference.setWiredByImpl(true);
+ }
+
+ String[] requiredIntents = getStrings(properties.get(SERVICE_EXPORTED_INTENTS));
+ List<Intent> intents = getIntents(requiredIntents);
+
+ String[] bindingDocuments = getStrings(properties.get(SCA_BINDINGS));
+ List<Binding> bindings = loadBindings(bundle, bindingDocuments);
+
+ List<Binding> bindingList = new ArrayList<Binding>(bindings);
+ BindingDescriptions bindingDescriptions =
+ (BindingDescriptions)properties.get(BindingDescriptions.BINDINGS_QNAME.toString());
+ if (bindingDescriptions != null) {
+ bindingList.addAll(bindingDescriptions);
+ }
+
+ for (ComponentReference componentReference : component.getReferences()) {
+ componentReference.getRequiredIntents().addAll(intents);
+ componentReference.getBindings().addAll(bindingList);
+ }
+ // FIXME: Should we scan the owning bundle to create the SCA contribution?
+ Contribution contribution = contributionFactory.createContribution();
+ contribution.setURI("urn:" + id);
+ contribution.setLocation(bundle.getEntry("/").toString());
+ contribution.getDeployables().add(composite);
+ ModelResolver modelResolver = new ExtensibleModelResolver(contribution, modelResolvers, factories);
+ contribution.setModelResolver(modelResolver);
+ contribution.setUnresolved(true);
+ return contribution;
+ }
+
+ public Contribution introspect(Bundle bundle, Endpoint endpoint) throws Exception {
+ String id = "osgi.reference." + UUID.randomUUID();
+ Composite composite = assemblyFactory.createComposite();
+ composite.setName(new QName(Base.SCA11_TUSCANY_NS, id));
+
+ Component component = assemblyFactory.createComponent();
+ component.setName(id);
+ // component.setAutowire(Boolean.TRUE);
+
+ composite.getComponents().add(component);
+
+ OSGiImplementation implementation = implementationFactory.createOSGiImplementation();
+
+ implementation.setBundle(bundle);
+ component.setImplementation(implementation);
+ implementation.setUnresolved(false);
+
+ Reference reference = assemblyFactory.createReference();
+ Service service = endpoint.getService().getService();
+ reference.setInterfaceContract(service.getInterfaceContract());
+ reference.setName("ref");
+
+ reference.getBindings().add(endpoint.getBinding());
+
+ /*
+ reference.getRequiredIntents().addAll(service.getRequiredIntents());
+ reference.getPolicySets().addAll(service.getPolicySets());
+ */
+
+ implementation.getReferences().add(reference);
+
+ ComponentReference componentReference = assemblyFactory.createComponentReference();
+ component.getReferences().add(componentReference);
+ componentReference.setReference(reference);
+ componentReference.setName(reference.getName());
+ componentReference.setWiredByImpl(true);
+
+ // FIXME: Should we scan the owning bundle to create the SCA contribution?
+ Contribution contribution = contributionFactory.createContribution();
+ contribution.setURI("urn:" + id);
+ contribution.setLocation(bundle.getEntry("/").toString());
+ contribution.getDeployables().add(composite);
+ ModelResolver modelResolver = new ExtensibleModelResolver(contribution, modelResolvers, factories);
+ contribution.setModelResolver(modelResolver);
+ contribution.setUnresolved(true);
+ return contribution;
+ }
+
+ private List<Binding> loadBindings(Bundle bundle, String[] bindingDocuments) throws IOException,
+ ContributionReadException {
+ if (bindingDocuments == null || bindingDocuments.length == 0) {
+ return Collections.emptyList();
+ }
+ List<Binding> bindings = new ArrayList<Binding>();
+ for (String doc : bindingDocuments) {
+ URL url = locate(bundle, doc);
+ if (url == null) {
+ throw new IOException("Entry " + doc + " cannot be found in bundle " + bundle);
+ }
+ bindings.addAll(loadBindings(url));
+ }
+ return bindings;
+ }
+
+ private List<Binding> loadBindings(URL url) throws ContributionReadException, IOException {
+ InputStream is = url.openStream();
+ try {
+ XMLStreamReader reader = xmlInputFactory.createXMLStreamReader(is);
+ reader.nextTag();
+ Object model = processor.read(reader);
+ if (model instanceof BindingDescriptions) {
+ return ((BindingDescriptions)model);
+ } else {
+ return Collections.emptyList();
+ }
+ } catch (XMLStreamException e) {
+ throw new ContributionReadException(e);
+ } finally {
+ is.close();
+ }
+ }
+
+ private URL locate(Bundle bundle, String location) throws MalformedURLException {
+ URI uri = URI.create(location);
+ if (uri.isAbsolute()) {
+ return uri.toURL();
+ }
+ return bundle.getEntry(location);
+ }
+
+ /**
+ * In OSGi, the value of String+ can be a single String, String[] or Collection<String>
+ * @param value
+ * @return
+ */
+ private String[] getStrings(Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String) {
+ return new String[] {(String)value};
+ } else if (value instanceof Collection) {
+ Collection<String> collection = (Collection)value;
+ return collection.toArray(new String[collection.size()]);
+ }
+ return (String[])value;
+
+ }
+
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/ExportRegistrationImpl.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/ExportRegistrationImpl.java
new file mode 100644
index 0000000000..d7881681f0
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/ExportRegistrationImpl.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.osgi.service.remoteadmin.impl;
+
+import org.apache.tuscany.sca.node.Node;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.ExportRegistration;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Implementation of {@link ExportRegistration}
+ */
+public class ExportRegistrationImpl implements ExportRegistration {
+ private Node node;
+ private ServiceReference exportedService;
+ private EndpointDescription endpointDescription;
+ private Throwable exception;
+
+ /**
+ * @param exportedService
+ * @param endpointDescription
+ * @param exception
+ */
+ public ExportRegistrationImpl(Node node,
+ ServiceReference exportedService,
+ EndpointDescription endpointDescription,
+ Throwable exception) {
+ super();
+ this.node = node;
+ this.exportedService = exportedService;
+ this.endpointDescription = endpointDescription;
+ this.exception = exception;
+ }
+
+ /**
+ * @param exportedService
+ * @param endpointDescription
+ */
+ public ExportRegistrationImpl(Node node, ServiceReference exportedService, EndpointDescription endpointDescription) {
+ this(node, exportedService, endpointDescription, null);
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.ExportRegistration#close()
+ */
+ public void close() {
+ if (node != null) {
+ node.stop();
+ node = null;
+ }
+ exception = null;
+ endpointDescription = null;
+ exportedService = null;
+ }
+
+ public ServiceReference getExportedService() {
+ return exportedService;
+ }
+
+ public EndpointDescription getEndpointDescription() {
+ return endpointDescription;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public Node getNode() {
+ return node;
+ }
+
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/ImportRegistrationImpl.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/ImportRegistrationImpl.java
new file mode 100644
index 0000000000..fb145a95d0
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/ImportRegistrationImpl.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.service.remoteadmin.impl;
+
+import org.apache.tuscany.sca.node.Node;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.ImportRegistration;
+import org.osgi.framework.ServiceReference;
+
+/**
+ *
+ */
+public class ImportRegistrationImpl implements ImportRegistration {
+ private Node node;
+ private ServiceReference exportedService;
+ private EndpointDescription endpointDescription;
+ private Throwable exception;
+
+ /**
+ * @param exportedService
+ * @param endpointDescription
+ * @param exception
+ */
+ public ImportRegistrationImpl(Node node,
+ ServiceReference exportedService,
+ EndpointDescription endpointDescription,
+ Throwable exception) {
+ super();
+ this.node = node;
+ this.exportedService = exportedService;
+ this.endpointDescription = endpointDescription;
+ this.exception = exception;
+ }
+
+ /**
+ * @param exportedService
+ * @param endpointDescription
+ */
+ public ImportRegistrationImpl(Node node, ServiceReference exportedService, EndpointDescription endpointDescription) {
+ super();
+ this.node = node;
+ this.exportedService = exportedService;
+ this.endpointDescription = endpointDescription;
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.ImportRegistration#close()
+ */
+ public void close() {
+ if (node != null) {
+ node.stop();
+ node = null;
+ }
+ exception = null;
+ endpointDescription = null;
+ exportedService = null;
+ }
+
+ public ServiceReference getImportedService() {
+ return exportedService;
+ }
+
+ public EndpointDescription getImportedEndpointDescription() {
+ return endpointDescription;
+ }
+
+ public Throwable getException() {
+ return exception;
+ }
+
+ public Node getNode() {
+ return node;
+ }
+
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteAdminHelper.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteAdminHelper.java
new file mode 100644
index 0000000000..7f467854a0
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteAdminHelper.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.service.remoteadmin.impl;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ *
+ */
+public class RemoteAdminHelper {
+ private RemoteAdminHelper() {
+ }
+
+ /**
+ * In OSGi, the value of String+ can be a single String, String[] or Collection<String>
+ * @param value
+ * @return
+ */
+ public static String[] getStringArray(Object value) {
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof String) {
+ return new String[] {(String)value};
+ } else if (value instanceof Collection) {
+ Collection<String> collection = (Collection)value;
+ return collection.toArray(new String[collection.size()]);
+ }
+ return (String[])value;
+
+ }
+
+ public static Collection<String> getStringCollection(Object value) {
+ String[] values = getStringArray(value);
+ if (values == null) {
+ return null;
+ } else {
+ return Arrays.asList(values);
+ }
+ }
+
+ public static String[] getStringArray(ServiceReference serviceReference, String propertyName) {
+ Object propertyValue = serviceReference.getProperty(propertyName);
+ return getStringArray(propertyValue);
+ }
+
+ public static Collection<String> getStringCollection(ServiceReference serviceReference, String propertyName) {
+ Object propertyValue = serviceReference.getProperty(propertyName);
+ return getStringCollection(propertyValue);
+ }
+
+ public static Filter createFilter(BundleContext context, String filterValue) {
+ if (filterValue == null) {
+ return null;
+ }
+ try {
+ return context.createFilter(filterValue);
+ } catch (InvalidSyntaxException ex) {
+ throw new IllegalArgumentException("Invalid Filter: " + filterValue, ex);
+ }
+ }
+
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteAdminImpl.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteAdminImpl.java
new file mode 100644
index 0000000000..05e59844e9
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteAdminImpl.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.tuscany.sca.osgi.service.remoteadmin.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tuscany.sca.node.osgi.impl.OSGiServiceExporter;
+import org.apache.tuscany.sca.node.osgi.impl.OSGiServiceImporter;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.ExportRegistration;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.ImportRegistration;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdmin;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminListener;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * SCA Implementation of {@link RemoteAdmin}
+ */
+public class RemoteAdminImpl implements RemoteAdmin {
+ private BundleContext context;
+ private ServiceRegistration registration;
+ private ServiceTracker listeners;
+
+ private OSGiServiceExporter exporter;
+ private OSGiServiceImporter importer;
+
+ private Collection<ImportRegistration> importedEndpoints = new ArrayList<ImportRegistration>();
+ private Collection<ExportRegistration> exportedServices = new ArrayList<ExportRegistration>();
+
+ public RemoteAdminImpl(BundleContext context) {
+ this.context = context;
+ }
+
+ public void start() {
+ this.exporter = new OSGiServiceExporter(context);
+ this.importer = new OSGiServiceImporter(context);
+ exporter.start();
+ importer.start();
+ registration = context.registerService(RemoteAdmin.class.getName(), this, null);
+ listeners = new ServiceTracker(this.context, RemoteAdminListener.class.getName(), null);
+ listeners.open();
+ }
+
+ public void stop() {
+ if (registration != null) {
+ registration.unregister();
+ registration = null;
+ }
+ if (listeners != null) {
+ listeners.close();
+ listeners = null;
+ }
+ for (ExportRegistration exportRegistration : exportedServices) {
+ exportRegistration.close();
+ }
+ exportedServices.clear();
+ for (ImportRegistration importRegistration : importedEndpoints) {
+ importRegistration.close();
+ }
+ importedEndpoints.clear();
+ if (importer != null) {
+ importer.stop();
+ importer = null;
+ }
+ if (exporter != null) {
+ exporter.stop();
+ exporter = null;
+ }
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdmin#exportService(org.osgi.framework.ServiceReference)
+ */
+ public List<ExportRegistration> exportService(ServiceReference ref) {
+ List<ExportRegistration> exportRegistrations = exporter.exportService(ref);
+ if (exportRegistrations != null) {
+ exportedServices.addAll(exportRegistrations);
+ }
+ return exportRegistrations;
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdmin#exportService(org.osgi.framework.ServiceReference,
+ * java.util.Map)
+ */
+ public List<ExportRegistration> exportService(ServiceReference ref, Map<String, Object> properties) {
+ List<ExportRegistration> exportRegistrations = exporter.exportService(ref);
+ if (exportRegistrations != null) {
+ exportedServices.addAll(exportRegistrations);
+ }
+ return exportRegistrations;
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdmin#getExportedServices()
+ */
+ public Collection<ExportRegistration> getExportedServices() {
+ return exportedServices;
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdmin#getImportedEndpoints()
+ */
+ public Collection<ImportRegistration> getImportedEndpoints() {
+ return importedEndpoints;
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdmin#importService(org.apache.tuscany.sca.dosgi.discovery.EndpointDescription)
+ */
+ public ImportRegistration importService(EndpointDescription endpoint) {
+ Bundle bundle = (Bundle) endpoint.getProperties().get(Bundle.class.getName());
+ ImportRegistration importReg = importer.importService(bundle, endpoint);
+ if (importReg != null) {
+ importedEndpoints.add(importReg);
+ }
+ return importReg;
+ }
+
+}
diff --git a/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteControllerImpl.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteControllerImpl.java
new file mode 100644
index 0000000000..b7fe173c85
--- /dev/null
+++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/RemoteControllerImpl.java
@@ -0,0 +1,403 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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.osgi.service.remoteadmin.impl;
+
+import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTERFACES;
+import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent.EXPORT_ERROR;
+import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent.EXPORT_REGISTRATION;
+import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent.EXPORT_UNREGISTRATION;
+import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent.EXPORT_WARNING;
+import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent.IMPORT_ERROR;
+import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent.IMPORT_REGISTRATION;
+import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent.IMPORT_UNREGISTRATION;
+import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent.IMPORT_WARNING;
+import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteConstants.SERVICE_EXPORTED_CONFIGS;
+import static org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteConstants.SERVICE_IMPORTED;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import org.apache.tuscany.sca.core.LifeCycleListener;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointListener;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.ExportRegistration;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.ImportRegistration;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdmin;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent;
+import org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminListener;
+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.framework.hooks.service.ListenerHook;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * Implementation of Remote Controller
+ */
+public class RemoteControllerImpl implements ListenerHook, RemoteAdminListener, EndpointListener,
+ ServiceTrackerCustomizer, LifeCycleListener /*, EventHook */{
+ private final static Logger logger = Logger.getLogger(RemoteControllerImpl.class.getName());
+ public final static String ENDPOINT_LOCAL = "service.local";
+
+ private BundleContext context;
+ private ServiceTracker remoteAdmins;
+
+ private ServiceRegistration registration;
+ private ServiceRegistration endpointListener;
+
+ private ServiceTracker remotableServices;
+
+ // Service listeners keyed by the filter
+ private MappedCollections<String, ListenerInfo> serviceListeners = new MappedCollections<String, ListenerInfo>();
+
+ private MappedCollections<ServiceReference, ExportRegistration> exportedServices =
+ new MappedCollections<ServiceReference, ExportRegistration>();
+ private MappedCollections<EndpointDescription, ImportRegistration> importedServices =
+ new MappedCollections<EndpointDescription, ImportRegistration>();
+
+ private Filter remotableServiceFilter;
+
+ public RemoteControllerImpl(BundleContext context) {
+ this.context = context;
+ }
+
+ public void start() {
+ String filter =
+ "(& (!(" + SERVICE_IMPORTED
+ + "=*)) ("
+ + SERVICE_EXPORTED_INTERFACES
+ + "=*) ("
+ + SERVICE_EXPORTED_CONFIGS
+ + "=sca) )";
+ try {
+ remotableServiceFilter = context.createFilter(filter);
+ } catch (InvalidSyntaxException e) {
+ // Ignore
+ }
+
+ endpointListener = context.registerService(EndpointListener.class.getName(), this, null);
+ remoteAdmins = new ServiceTracker(this.context, RemoteAdmin.class.getName(), null);
+ remoteAdmins.open();
+
+ // DO NOT register EventHook.class.getName() as it cannot report existing services
+ String interfaceNames[] = new String[] {ListenerHook.class.getName(), RemoteAdminListener.class.getName()};
+ // The registration will trigger the added() method before registration is assigned
+ registration = context.registerService(interfaceNames, this, null);
+
+ remotableServices = new ServiceTracker(context, remotableServiceFilter, this);
+ remotableServices.open(true);
+
+ }
+
+ /**
+ * @see org.osgi.framework.hooks.service.EventHook#event(org.osgi.framework.ServiceEvent,
+ * java.util.Collection)
+ */
+ /*
+ public void event(ServiceEvent event, Collection contexts) {
+ ServiceReference reference = event.getServiceReference();
+ if (!remotableServiceFilter.match(reference)) {
+ // Only export remotable services that are for SCA
+ return;
+ }
+ switch (event.getType()) {
+ case ServiceEvent.REGISTERED:
+ exportService(reference);
+ break;
+ case ServiceEvent.UNREGISTERING:
+ unexportService(reference);
+ break;
+ case ServiceEvent.MODIFIED:
+ case ServiceEvent.MODIFIED_ENDMATCH:
+ // First check if the property changes will impact
+ // Call remote admin to update the service
+ unexportService(reference);
+ exportService(reference);
+ break;
+ }
+ }
+ */
+
+ public Object addingService(ServiceReference reference) {
+ exportService(reference);
+ return reference.getBundle().getBundleContext().getService(reference);
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ unexportService(reference);
+ exportService(reference);
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ unexportService(reference);
+ }
+
+ private void unexportService(ServiceReference reference) {
+ // Call remote admin to unexport the service
+ Collection<ExportRegistration> exportRegistrations = exportedServices.get(reference);
+ if (exportRegistrations != null) {
+ for (Iterator<ExportRegistration> i = exportRegistrations.iterator(); i.hasNext();) {
+ ExportRegistration exported = i.next();
+ exported.close();
+ i.remove();
+ }
+ }
+ }
+
+ private void exportService(ServiceReference reference) {
+ // Call remote admin to export the service
+ Object[] admins = remoteAdmins.getServices();
+ if (admins == null) {
+ // Ignore
+ logger.warning("No RemoteAdmin services are available.");
+ } else {
+ for (Object ra : admins) {
+ RemoteAdmin remoteAdmin = (RemoteAdmin)ra;
+ List<ExportRegistration> exportRegistrations = remoteAdmin.exportService(reference);
+ if (exportRegistrations != null && !exportRegistrations.isEmpty()) {
+ exportedServices.putValue(reference, exportRegistrations);
+ }
+ }
+ }
+ }
+
+ /**
+ * @see org.osgi.framework.hooks.service.ListenerHook#added(java.util.Collection)
+ */
+ public void added(Collection listeners) {
+ Collection<ListenerInfo> listenerInfos = (Collection<ListenerInfo>)listeners;
+ boolean changed = false;
+ for (ListenerInfo l : listenerInfos) {
+ if (!l.isRemoved() && l.getBundleContext() != context) {
+ String key = l.getFilter();
+ if (key == null) {
+ // key = "";
+ // FIXME: It should always match, let's ignore it for now
+ logger.warning("Service listner without a filter is skipped: " + l);
+ continue;
+ }
+ Collection<ListenerInfo> infos = serviceListeners.get(key);
+ if (infos == null) {
+ infos = new HashSet<ListenerInfo>();
+ serviceListeners.put(key, infos);
+ }
+ infos.add(l);
+ changed = true;
+ }
+ }
+ if (changed) {
+ updateEndpointListenerScope();
+ }
+ }
+
+ private void updateEndpointListenerScope() {
+ Set<String> filterSet = serviceListeners.keySet();
+
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(ENDPOINT_LISTENER_SCOPE, filterSet.toArray(new String[filterSet.size()]));
+ endpointListener.setProperties(props);
+ }
+
+ private MappedCollections<Class<?>, ListenerInfo> findServiceListeners(EndpointDescription endpointDescription,
+ String matchedFilter) {
+ // First find all the listeners that have the matching filter
+ Collection<ListenerInfo> listeners = serviceListeners.get(matchedFilter);
+ if (listeners == null) {
+ return null;
+ }
+
+ // Try to partition the listeners by the interface classes
+ List<String> interfaceNames = endpointDescription.getInterfaces();
+ MappedCollections<Class<?>, ListenerInfo> interfaceToListeners =
+ new MappedCollections<Class<?>, ListenerInfo>();
+ for (String i : interfaceNames) {
+ for (ListenerInfo listener : listeners) {
+ try {
+ Class<?> interfaceClass = listener.getBundleContext().getBundle().loadClass(i);
+ interfaceToListeners.putValue(interfaceClass, listener);
+ } catch (ClassNotFoundException e) {
+ // Ignore the listener as it cannot load the interface class
+ }
+ }
+ }
+ return interfaceToListeners;
+ }
+
+ /**
+ * @see org.osgi.framework.hooks.service.ListenerHook#removed(java.util.Collection)
+ */
+ public void removed(Collection listeners) {
+ Collection<ListenerInfo> listenerInfos = (Collection<ListenerInfo>)listeners;
+ boolean changed = false;
+ for (ListenerInfo l : listenerInfos) {
+ if (registration != null && l.getBundleContext() != context) {
+ String key = l.getFilter();
+ if (key == null) {
+ continue;
+ }
+ if (serviceListeners.removeValue(key, l)) {
+ changed = true;
+ }
+ }
+ }
+ if (changed) {
+ updateEndpointListenerScope();
+ }
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminListener#remoteAdminEvent(org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent)
+ */
+ public void remoteAdminEvent(RemoteAdminEvent event) {
+ switch (event.getType()) {
+ case EXPORT_ERROR:
+ case EXPORT_REGISTRATION:
+ case EXPORT_UNREGISTRATION:
+ case EXPORT_WARNING:
+ break;
+ case IMPORT_ERROR:
+ case IMPORT_REGISTRATION:
+ case IMPORT_UNREGISTRATION:
+ case IMPORT_WARNING:
+ break;
+ }
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointListener#addEndpoint(org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription,
+ * java.lang.String)
+ */
+ public void addEndpoint(EndpointDescription endpoint, String matchedFilter) {
+ importService(endpoint, matchedFilter);
+ }
+
+ /**
+ * @see org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointListener#removeEndpoint(org.apache.tuscany.sca.osgi.service.remoteadmin.EndpointDescription)
+ */
+ public void removeEndpoint(EndpointDescription endpoint) {
+ unimportService(endpoint);
+ }
+
+ private void importService(EndpointDescription endpoint, String matchedFilter) {
+ Object[] admins = remoteAdmins.getServices();
+ if (admins == null) {
+ logger.warning("No RemoteAdmin services are available.");
+ return;
+ }
+
+ MappedCollections<Class<?>, ListenerInfo> interfaceToListeners = findServiceListeners(endpoint, matchedFilter);
+ for (Map.Entry<Class<?>, Collection<ListenerInfo>> e : interfaceToListeners.entrySet()) {
+ Class<?> interfaceClass = e.getKey();
+ Collection<ListenerInfo> listeners = e.getValue();
+ // Get a listener
+ ListenerInfo listener = listeners.iterator().next();
+ Bundle bundle = listener.getBundleContext().getBundle();
+
+ Map<String, Object> props = new HashMap<String, Object>(endpoint.getProperties());
+ props.put(Bundle.class.getName(), bundle);
+ EndpointDescription description =
+ new EndpointDescriptionImpl(Collections.singletonList(interfaceClass.getName()), props);
+
+ if (admins != null) {
+ for (Object ra : admins) {
+ RemoteAdmin remoteAdmin = (RemoteAdmin)ra;
+ ImportRegistration importRegistration = remoteAdmin.importService(description);
+ if (importRegistration != null) {
+ importedServices.putValue(endpoint, importRegistration);
+ }
+ }
+ }
+ }
+ }
+
+ private void unimportService(EndpointDescription endpoint) {
+ // Call remote admin to unimport the service
+ Collection<ImportRegistration> importRegistrations = importedServices.get(endpoint);
+ if (importRegistrations != null) {
+ for (Iterator<ImportRegistration> i = importRegistrations.iterator(); i.hasNext();) {
+ ImportRegistration imported = i.next();
+ imported.close();
+ i.remove();
+ }
+ }
+ }
+
+ public void stop() {
+ remotableServices.close();
+
+ if (registration != null) {
+ registration.unregister();
+ registration = null;
+ }
+ if (remoteAdmins != null) {
+ remoteAdmins.close();
+ remoteAdmins = null;
+ }
+ for (Collection<ListenerInfo> infos : serviceListeners.values()) {
+ }
+ serviceListeners.clear();
+ }
+
+ private static class MappedCollections<K, V> extends HashMap<K, Collection<V>> {
+ private static final long serialVersionUID = -8926174610229029369L;
+
+ public boolean putValue(K key, V value) {
+ Collection<V> collection = get(key);
+ if (collection == null) {
+ collection = new ArrayList<V>();
+ put(key, collection);
+ }
+ return collection.add(value);
+ }
+
+ public boolean putValue(K key, Collection<? extends V> value) {
+ Collection<V> collection = get(key);
+ if (collection == null) {
+ collection = new ArrayList<V>();
+ put(key, collection);
+ }
+ return collection.addAll(value);
+ }
+
+ public boolean removeValue(K key, V value) {
+ Collection<V> collection = get(key);
+ if (collection == null) {
+ return false;
+ }
+ return collection.remove(value);
+ }
+
+ }
+
+}
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
deleted file mode 100644
index d4431954b8..0000000000
--- a/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/DiscoveredServiceNotification.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * 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/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
deleted file mode 100644
index 0cfa023d95..0000000000
--- a/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/DiscoveredServiceTracker.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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/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
deleted file mode 100644
index 8f8b5d9f16..0000000000
--- a/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/ServiceEndpointDescription.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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/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
deleted file mode 100644
index d490394bc3..0000000000
--- a/java/sca/modules/node-impl-osgi/src/main/java/org/osgi/service/discovery/ServicePublication.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * 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();
-}