diff options
Diffstat (limited to 'java/sca/modules/node-impl-osgi/src')
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 (<? extends String>)</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 (<? extends String>)</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 (<? extends String>)</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 (<? extends String>)</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>"(&(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 (<? extends String>)</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 (<? extends String>)</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 (<? extends String>)</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 (<String, Object>)</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 (<? extends String>)</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 (<? extends String>)</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 (<? extends String>)</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(); -} |