diff options
author | rfeng <rfeng@13f79535-47bb-0310-9956-ffa450edef68> | 2009-07-24 17:53:23 +0000 |
---|---|---|
committer | rfeng <rfeng@13f79535-47bb-0310-9956-ffa450edef68> | 2009-07-24 17:53:23 +0000 |
commit | 61fd7d871ecc003913e6bc31670e47d5e7eb4097 (patch) | |
tree | b3fba81202522ccf530c97467a312d1e4f4f0afb /java/sca | |
parent | f233e515188f75286b600ed5d4d567b6a3203b2f (diff) |
Add the support for dynamic OSGi remote service import and export based on OSGi properties
Add two samples to demonstrate the OSGi property driven remote services
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@797580 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to '')
94 files changed, 5890 insertions, 1439 deletions
diff --git a/java/sca/modules/implementation-osgi/META-INF/MANIFEST.MF b/java/sca/modules/implementation-osgi/META-INF/MANIFEST.MF index c6921b4c4e..f417c651b2 100755 --- a/java/sca/modules/implementation-osgi/META-INF/MANIFEST.MF +++ b/java/sca/modules/implementation-osgi/META-INF/MANIFEST.MF @@ -19,7 +19,6 @@ Import-Package: javax.xml.namespace, org.apache.tuscany.sca.contribution.resolver;version="2.0.0",
org.apache.tuscany.sca.core;version="2.0.0",
org.apache.tuscany.sca.implementation.osgi;version="2.0.0",
- org.apache.tuscany.sca.implementation.osgi.introspection;version="2.0.0",
org.apache.tuscany.sca.interfacedef;version="2.0.0",
org.apache.tuscany.sca.interfacedef.java;version="2.0.0",
org.apache.tuscany.sca.interfacedef.java.impl;version="2.0.0",
@@ -32,6 +31,5 @@ Import-Package: javax.xml.namespace, org.osgi.service.packageadmin;version="1.2.0",
org.osgi.util.tracker;version="1.3.3"
Bundle-DocURL: http://www.apache.org/
-Export-Package: org.apache.tuscany.sca.implementation.osgi;version="2.0.0",
- org.apache.tuscany.sca.implementation.osgi.introspection;version="2.0.0"
+Export-Package: org.apache.tuscany.sca.implementation.osgi;version="2.0.0"
Bundle-RequiredExecutionEnvironment: J2SE-1.5,JavaSE-1.6 diff --git a/java/sca/modules/implementation-osgi/src/main/java/org/apache/tuscany/sca/implementation/osgi/xml/ServiceDescriptionsProcessor.java b/java/sca/modules/implementation-osgi/src/main/java/org/apache/tuscany/sca/implementation/osgi/xml/ServiceDescriptionsProcessor.java index bef07d159d..38e2730df8 100644 --- a/java/sca/modules/implementation-osgi/src/main/java/org/apache/tuscany/sca/implementation/osgi/xml/ServiceDescriptionsProcessor.java +++ b/java/sca/modules/implementation-osgi/src/main/java/org/apache/tuscany/sca/implementation/osgi/xml/ServiceDescriptionsProcessor.java @@ -19,16 +19,21 @@ package org.apache.tuscany.sca.implementation.osgi.xml; +import java.util.StringTokenizer; + import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; +import org.apache.tuscany.sca.contribution.processor.BaseStAXArtifactProcessor; +import org.apache.tuscany.sca.contribution.processor.ContributionReadException; import org.apache.tuscany.sca.contribution.processor.ContributionResolveException; import org.apache.tuscany.sca.contribution.processor.ContributionWriteException; import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor; import org.apache.tuscany.sca.contribution.resolver.ModelResolver; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; import org.apache.tuscany.sca.core.FactoryExtensionPoint; import org.apache.tuscany.sca.implementation.osgi.ServiceDescription; import org.apache.tuscany.sca.implementation.osgi.ServiceDescriptions; @@ -56,16 +61,22 @@ import org.apache.tuscany.sca.monitor.Monitor; </service-description> </service-descriptions> */ -public class ServiceDescriptionsProcessor implements StAXArtifactProcessor<ServiceDescriptions> { +public class ServiceDescriptionsProcessor extends BaseStAXArtifactProcessor implements + StAXArtifactProcessor<ServiceDescriptions> { private ServiceDescriptionsFactory factory; + private StAXArtifactProcessor processor; private Monitor monitor; - public ServiceDescriptionsProcessor(FactoryExtensionPoint modelFactories, Monitor monitor) { + public ServiceDescriptionsProcessor(ExtensionPointRegistry registry, + StAXArtifactProcessor processor, + Monitor monitor) { this.monitor = monitor; + this.processor = processor; + FactoryExtensionPoint modelFactories = registry.getExtensionPoint(FactoryExtensionPoint.class); this.factory = modelFactories.getFactory(ServiceDescriptionsFactory.class); } - public ServiceDescriptions read(XMLStreamReader reader) throws XMLStreamException { + public ServiceDescriptions read(XMLStreamReader reader) throws XMLStreamException, ContributionReadException { int event = reader.getEventType(); ServiceDescriptions sds = factory.createServiceDescriptions(); ServiceDescription sd = null; @@ -112,7 +123,18 @@ public class ServiceDescriptionsProcessor implements StAXArtifactProcessor<Servi } else if ("Boolean".equals(propType)) { prop = Boolean.valueOf(propValue); } + if (propName.endsWith(".intents")) { + prop = toQNames(reader, propValue); + } sd.getProperties().put(propName, prop); + } else { + name = reader.getName(); + if (!ServiceDescriptions.SERVICE_DESCRIPTIONS_QNAME.equals(name)) { + Object ext = processor.read(reader); + if (sd != null) { + sd.getProperties().put(name.toString(), ext); + } + } } break; case XMLStreamConstants.END_ELEMENT: @@ -134,6 +156,24 @@ public class ServiceDescriptionsProcessor implements StAXArtifactProcessor<Servi } } + /** + * Convert ns1:e1 ns2:e2 to {http://ns1}e1 {http://ns2}e2 + * @param reader + * @param value + * @return + */ + private String toQNames(XMLStreamReader reader, String value) { + if (value == null) { + return null; + } + StringBuffer sb = new StringBuffer(); + for (StringTokenizer tokens = new StringTokenizer(value, " \t\n\r\f,"); tokens.hasMoreTokens();) { + QName qname = getQNameValue(reader, tokens.nextToken()); + sb.append(qname.toString()).append(' '); + } + return sb.toString().trim(); + } + public QName getArtifactType() { return ServiceDescriptions.SERVICE_DESCRIPTIONS_QNAME; } diff --git a/java/sca/modules/implementation-osgi/src/test/java/org/apache/tuscany/sca/implementation/osgi/xml/ServiceDescriptionsTestCase.java b/java/sca/modules/implementation-osgi/src/test/java/org/apache/tuscany/sca/implementation/osgi/xml/ServiceDescriptionsTestCase.java index d6728f8e1f..e0284b9219 100644 --- a/java/sca/modules/implementation-osgi/src/test/java/org/apache/tuscany/sca/implementation/osgi/xml/ServiceDescriptionsTestCase.java +++ b/java/sca/modules/implementation-osgi/src/test/java/org/apache/tuscany/sca/implementation/osgi/xml/ServiceDescriptionsTestCase.java @@ -25,8 +25,11 @@ import java.util.List; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamReader; +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.DefaultExtensionPointRegistry; -import org.apache.tuscany.sca.core.DefaultFactoryExtensionPoint; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; import org.apache.tuscany.sca.implementation.osgi.ServiceDescription; import org.junit.AfterClass; import org.junit.Assert; @@ -40,7 +43,7 @@ public class ServiceDescriptionsTestCase { private static final String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<service-descriptions xmlns=\"http://www.osgi.org/xmlns/sd/v1.0.0\" " - +"xmlns:sca=\"http://docs.oasis-open.org/ns/opencsa/sca/200903\">" + + "xmlns:sca=\"http://docs.oasis-open.org/ns/opencsa/sca/200903\">" + "<service-description>" + "<provide interface=\"calculator.operations.AddService\"/>" + "<property name=\"service.intents\">sca:SOAP sca:HTTP</property>" @@ -62,9 +65,10 @@ public class ServiceDescriptionsTestCase { + "<property name=\"osgi.remote.configuration.sca.reference\">" + "subtractService" + "</property>" - + "</service-description>" + + "</service-description>" + "</service-descriptions>"; + private static ServiceDescriptionsProcessor processor; private static XMLStreamReader reader; /** @@ -72,15 +76,20 @@ public class ServiceDescriptionsTestCase { */ @BeforeClass public static void setUpBeforeClass() throws Exception { + ExtensionPointRegistry extensionPoints = new DefaultExtensionPointRegistry(); + XMLInputFactory factory = XMLInputFactory.newInstance(); + StAXArtifactProcessorExtensionPoint staxProcessors = + extensionPoints.getExtensionPoint(StAXArtifactProcessorExtensionPoint.class); + StAXArtifactProcessor staxProcessor = new ExtensibleStAXArtifactProcessor(staxProcessors, factory, null, null); + + processor = new ServiceDescriptionsProcessor(extensionPoints, staxProcessor, null); + reader = factory.createXMLStreamReader(new StringReader(xml)); } @Test public void testLoad() throws Exception { - ServiceDescriptionsProcessor processor = - new ServiceDescriptionsProcessor(new DefaultFactoryExtensionPoint(new DefaultExtensionPointRegistry()), - null); List<ServiceDescription> descriptions = processor.read(reader); Assert.assertEquals(2, descriptions.size()); System.out.println(descriptions); diff --git a/java/sca/modules/node-impl-osgi/META-INF/MANIFEST.MF b/java/sca/modules/node-impl-osgi/META-INF/MANIFEST.MF index d950198a5e..71a5f1ae46 100644 --- a/java/sca/modules/node-impl-osgi/META-INF/MANIFEST.MF +++ b/java/sca/modules/node-impl-osgi/META-INF/MANIFEST.MF @@ -23,13 +23,13 @@ Import-Package: javax.xml.namespace, org.apache.tuscany.sca.definitions.xml;version="2.0.0",
org.apache.tuscany.sca.extensibility.equinox;version="2.0.0",
org.apache.tuscany.sca.implementation.osgi;version="2.0.0",
- org.apache.tuscany.sca.implementation.osgi.introspection;version="2.0.0",
org.apache.tuscany.sca.interfacedef;version="2.0.0",
org.apache.tuscany.sca.interfacedef.java;version="2.0.0",
org.apache.tuscany.sca.monitor;version="2.0.0",
org.apache.tuscany.sca.node;version="2.0.0",
org.apache.tuscany.sca.node.configuration;version="2.0.0",
org.apache.tuscany.sca.node.impl;version="2.0.0",
+ org.apache.tuscany.sca.osgi.service.remoteadmin;version="2.0.0",
org.apache.tuscany.sca.policy;version="2.0.0",
org.apache.tuscany.sca.provider;version="2.0.0",
org.apache.tuscany.sca.runtime;version="2.0.0",
@@ -44,4 +44,4 @@ Bundle-SymbolicName: org.apache.tuscany.sca.node.osgi.impl Bundle-DocURL: http://www.apache.org/
Bundle-RequiredExecutionEnvironment: J2SE-1.5,JavaSE-1.6 Bundle-ActivationPolicy: lazy
-Export-Package: org.osgi.service.discovery;version="1.0"
+Export-Package: org.apache.tuscany.sca.osgi.service.remoteadmin;version="2.0.0"
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/implementation-osgi/src/main/java/org/apache/tuscany/sca/implementation/osgi/introspection/ExportedServiceIntrospector.java b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointIntrospector.java index 409e14bdd9..59f8d3c07b 100644 --- a/java/sca/modules/implementation-osgi/src/main/java/org/apache/tuscany/sca/implementation/osgi/introspection/ExportedServiceIntrospector.java +++ b/java/sca/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/service/remoteadmin/impl/EndpointIntrospector.java @@ -17,15 +17,13 @@ * under the License. */ -package org.apache.tuscany.sca.implementation.osgi.introspection; +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.REMOTE_CONFIG_SCA; 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.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_IMPORTED_CONFIGS; import static org.osgi.framework.Constants.OBJECTCLASS; import static org.osgi.framework.Constants.SERVICE_ID; @@ -37,9 +35,7 @@ import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Dictionary; import java.util.HashMap; -import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.UUID; @@ -57,6 +53,7 @@ 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; @@ -81,18 +78,18 @@ 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.Filter; -import org.osgi.framework.InvalidSyntaxException; +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 ExportedServiceIntrospector { +public class EndpointIntrospector { private AssemblyFactory assemblyFactory; private ContributionFactory contributionFactory; private OSGiImplementationFactory implementationFactory; @@ -127,9 +124,10 @@ public class ExportedServiceIntrospector { } /** + * @param context TODO * @param registry */ - public ExportedServiceIntrospector(ExtensionPointRegistry registry) { + public EndpointIntrospector(BundleContext context, ExtensionPointRegistry registry) { super(); this.registry = registry; this.factories = registry.getExtensionPoint(FactoryExtensionPoint.class); @@ -221,7 +219,7 @@ public class ExportedServiceIntrospector { // The service.id is Long serviceID.setValue(String.valueOf(reference.getProperty(SERVICE_ID))); - String id = "osgi.service." + serviceID.getValue(); + String id = "osgi.service." + UUID.randomUUID(); Composite composite = assemblyFactory.createComposite(); composite.setName(new QName(SCA11_TUSCANY_NS, id)); @@ -293,36 +291,24 @@ public class ExportedServiceIntrospector { return contribution; } - /** - * Introspect an OSGi filter to create an SCA reference - * - * @param bundle - * @param filterStr - * @param properties - * @return - * @throws Exception - */ - public Contribution introspect(Bundle bundle, String filterStr, Map<String, Object> properties) throws Exception { - Filter filter = null; - try { - filter = bundle.getBundleContext().createFilter(filterStr); - } catch (InvalidSyntaxException e) { - e.printStackTrace(); - return null; + 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(); - Dictionary<String, Object> props = new Hashtable<String, Object>(); - props.put(SERVICE_IMPORTED_CONFIGS, new String[] {REMOTE_CONFIG_SCA}); - if (!filter.match(props)) { - return null; - } 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); + // component.setAutowire(Boolean.TRUE); composite.getComponents().add(component); @@ -332,12 +318,7 @@ public class ExportedServiceIntrospector { component.setImplementation(implementation); implementation.setUnresolved(false); - String[] remoteInterfaces = getStrings(properties.get(SERVICE_EXPORTED_INTERFACES)); - if (remoteInterfaces == null || remoteInterfaces.length > 0 && "*".equals(remoteInterfaces[0])) { - remoteInterfaces = getStrings(properties.get(OBJECTCLASS)); - } else { - remoteInterfaces = parse(remoteInterfaces); - } + int count = 0; for (String intf : remoteInterfaces) { Reference reference = assemblyFactory.createReference(); JavaInterfaceContract interfaceContract = javaInterfaceFactory.createJavaInterfaceContract(); @@ -349,14 +330,13 @@ public class ExportedServiceIntrospector { .getCallbackClass())); } - reference.setName(id); + reference.setName("ref" + (count++)); reference.setInterfaceContract(interfaceContract); - reference.getExtensions().add(filter); - implementation.getReferences().add(reference); ComponentReference componentReference = assemblyFactory.createComponentReference(); + componentReference.setName(reference.getName()); component.getReferences().add(componentReference); componentReference.setReference(reference); componentReference.setWiredByImpl(true); @@ -368,10 +348,64 @@ public class ExportedServiceIntrospector { 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(bindings); + 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(); 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(); -} diff --git a/java/sca/samples/dosgi-calculator-operations/src/main/java/calculator/dosgi/operations/impl/OperationsActivator.java b/java/sca/samples/dosgi-calculator-operations/src/main/java/calculator/dosgi/operations/impl/OperationsActivator.java index 5348d19f34..da2ce9063a 100644 --- a/java/sca/samples/dosgi-calculator-operations/src/main/java/calculator/dosgi/operations/impl/OperationsActivator.java +++ b/java/sca/samples/dosgi-calculator-operations/src/main/java/calculator/dosgi/operations/impl/OperationsActivator.java @@ -44,7 +44,7 @@ public class OperationsActivator implements BundleActivator { logger.info("Starting " + context.getBundle()); Dictionary<String, Object> props = new Hashtable<String, Object>(); - + logger.info("Registering " + AddService.class.getName()); props.put("sca.service", "AddComponent#service-name(Add)"); context.registerService(AddService.class.getName(), new AddServiceImpl(), props); diff --git a/java/sca/samples/dosgi-calculator/OSGI-INF/sca/bundle.composite b/java/sca/samples/dosgi-calculator/OSGI-INF/sca/bundle.composite index 2bcd44e970..481fd67e09 100644 --- a/java/sca/samples/dosgi-calculator/OSGI-INF/sca/bundle.composite +++ b/java/sca/samples/dosgi-calculator/OSGI-INF/sca/bundle.composite @@ -40,17 +40,6 @@ <reference name="divideService"> <tuscany:binding.rmi uri="rmi://localhost:8085/DivideService"/> </reference> - - <!-- - <reference name="addService" target="OperationsComponent/AddService"> - </reference> - <reference name="subtractService" target="OperationsComponent/SubtractService"> - </reference> - <reference name="multiplyService" target="OperationsComponent/MultiplyService"> - </reference> - <reference name="divideService" target="OperationsComponent/DivideService"> - </reference> - --> </component> </composite> diff --git a/java/sca/samples/dosgi-calculator/OSGI-INF/sca/calculator-service.bindings b/java/sca/samples/dosgi-calculator/OSGI-INF/sca/calculator-service.bindings index 65a5a76194..90e55f7377 100644 --- a/java/sca/samples/dosgi-calculator/OSGI-INF/sca/calculator-service.bindings +++ b/java/sca/samples/dosgi-calculator/OSGI-INF/sca/calculator-service.bindings @@ -17,7 +17,7 @@ * specific language governing permissions and limitations
* under the License.
-->
-<!-- A consumer-side service description file for RFC 119 -->
+<!-- SCA bindings for calculator service -->
<bindings xmlns="http://www.osgi.org/xmlns/sd/v1.0.0"
xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200903">
<sca:binding.ws uri="http://localhost:8086/CalculatorService"/>
diff --git a/java/sca/samples/dosgi-calculator/src/main/java/calculator/dosgi/impl/CalculatorActivator.java b/java/sca/samples/dosgi-calculator/src/main/java/calculator/dosgi/impl/CalculatorActivator.java index 40388144ee..6b42645f69 100644 --- a/java/sca/samples/dosgi-calculator/src/main/java/calculator/dosgi/impl/CalculatorActivator.java +++ b/java/sca/samples/dosgi-calculator/src/main/java/calculator/dosgi/impl/CalculatorActivator.java @@ -59,9 +59,7 @@ public class CalculatorActivator implements BundleActivator { Dictionary<String, Object> props = new Hashtable<String, Object>(); props.put("sca.service", "CalculatorComponent#service-name(Calculator)"); props.put("calculator", "Calculator"); - props.put("service.exported.configs", new String[] {"sca"}); - props.put("sca.bindings", new String[] {"OSGI-INF/sca/calculator-service.bindings"}); - props.put("service.exported.interfaces", new String[] {"*"}); + logger.info("Registering " + CalculatorService.class.getName()); CalculatorService calculator = new CalculatorServiceImpl(context); context.registerService(CalculatorService.class.getName(), calculator, props); diff --git a/java/sca/samples/dosgi-calculator/src/main/java/calculator/dosgi/impl/CalculatorServiceImpl.java b/java/sca/samples/dosgi-calculator/src/main/java/calculator/dosgi/impl/CalculatorServiceImpl.java index 932c5fcf15..a9ea37585a 100644 --- a/java/sca/samples/dosgi-calculator/src/main/java/calculator/dosgi/impl/CalculatorServiceImpl.java +++ b/java/sca/samples/dosgi-calculator/src/main/java/calculator/dosgi/impl/CalculatorServiceImpl.java @@ -60,6 +60,12 @@ public class CalculatorServiceImpl implements CalculatorService { } private <T> T getService(Class<T> cls) { + try { + // Wait for 10 seconds until the remote services are imported + remoteServices.waitForService(10000); + } catch (InterruptedException e) { + throw new IllegalStateException(cls.getSimpleName() + " is not available"); + } Object[] remoteObjects = remoteServices.getServices(); if (remoteObjects != null) { for (Object s : remoteObjects) { @@ -70,13 +76,14 @@ public class CalculatorServiceImpl implements CalculatorService { } } Object[] localObjects = localServices.getServices(); - if (localObjects != null) + if (localObjects != null) { for (Object s : localObjects) { if (cls.isInstance(s)) { System.out.println("Local service: " + s); return cls.cast(s); } } + } throw new IllegalStateException(cls.getSimpleName() + " is not available"); } diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/LICENSE b/java/sca/samples/dosgi-dynamic-calculator-operations/LICENSE new file mode 100644 index 0000000000..6e529a25c4 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/LICENSE @@ -0,0 +1,205 @@ +
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+
diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/META-INF/MANIFEST.MF b/java/sca/samples/dosgi-dynamic-calculator-operations/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..9dee7d5f4a --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/META-INF/MANIFEST.MF @@ -0,0 +1,22 @@ +Manifest-Version: 1.0
+Export-Package: calculator.dosgi.operations;version="1.0.1"
+Bundle-Version: 1.0.0
+Bundle-Name: calculator.dosgi.dynamic.operations
+Bundle-Activator: calculator.dosgi.operations.impl.OperationsActivator
+Bundle-ManifestVersion: 2
+Import-Package: calculator.dosgi.operations;version="1.0.1",
+ org.oasisopen.sca.annotation;version="2.0.0",
+ org.osgi.framework,
+ org.osgi.service.component;resolution:=optional,
+ org.osgi.service.packageadmin
+Bundle-SymbolicName: calculator.dosgi.dynamic.operations
+Bundle-Vendor: The Apache Software Foundation
+Bundle-ActivationPolicy: lazy
+Eclipse-LazyStart: true
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-DocURL: http://www.apache.org/
+Service-Component: OSGI-INF/add-component.xml,
+ OSGI-INF/subtract-component.xml,
+ OSGI-INF/multiply-component.xml,
+ OSGI-INF/divide-component.xml
+Bundle-RequiredExecutionEnvironment: J2SE-1.5,JavaSE-1.6 diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/NOTICE b/java/sca/samples/dosgi-dynamic-calculator-operations/NOTICE new file mode 100644 index 0000000000..51042eab05 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/NOTICE @@ -0,0 +1,6 @@ +${pom.name}
+Copyright (c) 2005 - 2009 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/add-component.xml b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/add-component.xml new file mode 100644 index 0000000000..99845257ff --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/add-component.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<scr:component name="AddComponent" xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0"> + <implementation class="calculator.dosgi.operations.impl.AddServiceImpl" /> + <service> + <provide interface="calculator.dosgi.operations.AddService" /> + </service> +</scr:component> diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/blueprint/operations-module.xml b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/blueprint/operations-module.xml new file mode 100644 index 0000000000..f6b5f4690e --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/blueprint/operations-module.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<!-- A sample module-context.xml for OSGI RFC 124 (BluePrint Service) --> +<components xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <component id="AddComponent" class="calculator.dosgi.operations.impl.AddServiceImpl"> + </component> + <component id="SubtractComponent" class="calculator.dosgi.operations.impl.SubtractServiceImpl"> + </component> + <component id="MultiplyComponent" class="calculator.dosgi.operations.impl.MultiplyServiceImpl"> + </component> + <component id="DivideComponent" class="calculator.dosgi.operations.impl.DivideServiceImpl"> + </component> + + <!-- We can derive the SCA services for the implementation.osgi --> + <service id="AddService" ref="AddComponent" interface="calculator.dosgi.operations.AddService"> + </service> + <service id="SubtractService" ref="SubtractComponent" interface="calculator.dosgi.operations.SubtractService"> + </service> + <service id="MultiplyService" ref="MultiplyComponent" interface="calculator.dosgi.operations.MultiplyService"> + </service> + <service id="DivideService" ref="DivideComponent" interface="calculator.dosgi.operations.DivideService"> + </service> +</components>
\ No newline at end of file diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/divide-component.xml b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/divide-component.xml new file mode 100644 index 0000000000..322d4daf2f --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/divide-component.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<scr:component name="DivideComponent" xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0"> + <implementation class="calculator.dosgi.operations.impl.DivideServiceImpl" /> + <service> + <provide interface="calculator.dosgi.operations.DivideService" /> + </service> +</scr:component> diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/multiply-component.xml b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/multiply-component.xml new file mode 100644 index 0000000000..b9ca777bd8 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/multiply-component.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<scr:component name="MultiplyComponent" xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0"> + <implementation class="calculator.dosgi.operations.impl.MultiplyServiceImpl" /> + <service> + <provide interface="calculator.dosgi.operations.MultiplyService" /> + </service> +</scr:component> diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/sca/add-service.bindings b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/sca/add-service.bindings new file mode 100644 index 0000000000..d51878c562 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/sca/add-service.bindings @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+-->
+<bindings xmlns="http://www.osgi.org/xmlns/sd/v1.0.0"
+ xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/AddService"/>
+</bindings>
\ No newline at end of file diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/sca/divide-service.bindings b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/sca/divide-service.bindings new file mode 100644 index 0000000000..a664456474 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/sca/divide-service.bindings @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+-->
+<bindings xmlns="http://www.osgi.org/xmlns/sd/v1.0.0"
+ xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/DivideService"/>
+</bindings>
\ No newline at end of file diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/sca/multiply-service.bindings b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/sca/multiply-service.bindings new file mode 100644 index 0000000000..fb1b40e39c --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/sca/multiply-service.bindings @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+-->
+<bindings xmlns="http://www.osgi.org/xmlns/sd/v1.0.0"
+ xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/MultiplyService"/>
+</bindings>
\ No newline at end of file diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/sca/subtract-service.bindings b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/sca/subtract-service.bindings new file mode 100644 index 0000000000..2f44802d1f --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/sca/subtract-service.bindings @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+-->
+<bindings xmlns="http://www.osgi.org/xmlns/sd/v1.0.0"
+ xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1">
+ <tuscany:binding.rmi uri="rmi://localhost:8085/SubtractService"/>
+</bindings>
\ No newline at end of file diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/subtract-component.xml b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/subtract-component.xml new file mode 100644 index 0000000000..1472f5a976 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/OSGI-INF/subtract-component.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<scr:component name="SubtractComponent" xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0"> + <implementation class="calculator.dosgi.operations.impl.SubtractServiceImpl" /> + <service> + <provide interface="calculator.dosgi.operations.SubtractService" /> + </service> +</scr:component> diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/README b/java/sca/samples/dosgi-dynamic-calculator-operations/README new file mode 100644 index 0000000000..9e616c1f50 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/README @@ -0,0 +1,183 @@ +Distributed OSGi Calculator Sample
+==================================
+This sample implements a distributed calculator using Distributed OSGi (RFC 119) over SCA.
+
+The README in the samples directory (the directory above this) provides
+general instructions about building and running samples. Take a look there
+first.
+
+On Windows, run
+java -jar ..\..\modules\osgi-3.4.0-v20080605-1900.jar -configuration ..\..\features\configuration -clean -console
+
+On *Unix, run
+java -jar ../../modules/osgi-3.4.0-v20080605-1900.jar -configuration ../../features/configuration -clean -console
+
+You should see the osgi console:
+
+osgi>
+
+osgi> Jun 22, 2009 1:32:27 PM org.apache.tuscany.sca.extensibility.equinox.EquinoxServiceDiscoveryActivator start
+INFO: Equinox-based service discoverer is now configured.
+
+You can run "ss" command under the osgi> to see the status of the bundles.
+osgi> ss
+
+Then you can install and start the calculator.dosgi bundle:
+
+osgi> install file:./target/sample-dosgi-calculator-operations.jar
+Bundle id is 181
+
+osgi> start 181
+Jun 22, 2009 2:09:21 PM calculator.dosgi.operations.impl.OperationsActivator sta
+rt
+INFO: Starting file:./target/sample-dosgi-calculator-operations.jar [181]
+Jun 22, 2009 2:09:21 PM calculator.dosgi.operations.impl.OperationsActivator sta
+rt
+INFO: Registering calculator.dosgi.operations.AddService
+Jun 22, 2009 2:09:21 PM calculator.dosgi.operations.impl.OperationsActivator sta
+rt
+INFO: Registering calculator.dosgi.operations.SubtractService
+Jun 22, 2009 2:09:21 PM calculator.dosgi.operations.impl.OperationsActivator sta
+rt
+INFO: Registering calculator.dosgi.operations.MultiplyService
+Jun 22, 2009 2:09:21 PM calculator.dosgi.operations.impl.OperationsActivator sta
+rt
+INFO: Registering calculator.dosgi.operations.DivideService
+Jun 22, 2009 2:09:21 PM calculator.dosgi.operations.impl.OperationsActivator get
+Bundle
+INFO: calculator.dosgi.operations.AddService is loaded by bundle: calculator.dos
+gi.operations
+Jun 22, 2009 2:09:21 PM org.apache.tuscany.sca.node.impl.NodeImpl start
+INFO: Starting node: calculator.dosgi.operations
+Jun 22, 2009 2:09:21 PM org.apache.tuscany.sca.node.impl.NodeFactoryImpl configu
+reNode
+INFO: Loading contribution: bundleentry://181/
+Jun 22, 2009 2:09:21 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry addEndpoint
+INFO: EndpointRegistry: Add endpoint - Endpoint: URI = OperationsComponent#serv
+ice-binding(AddService/AddService)
+Jun 22, 2009 2:09:21 PM org.apache.tuscany.sca.host.rmi.DefaultRMIHost registerS
+ervice
+INFO: RMI service registered: rmi://localhost:8085/AddService
+Jun 22, 2009 2:09:21 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry addEndpoint
+INFO: EndpointRegistry: Add endpoint - Endpoint: URI = OperationsComponent#serv
+ice-binding(SubtractService/SubtractService)
+Jun 22, 2009 2:09:21 PM org.apache.tuscany.sca.host.rmi.DefaultRMIHost registerS
+ervice
+INFO: RMI service registered: rmi://localhost:8085/SubtractService
+Jun 22, 2009 2:09:21 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry addEndpoint
+INFO: EndpointRegistry: Add endpoint - Endpoint: URI = OperationsComponent#serv
+ice-binding(MultiplyService/MultiplyService)
+Jun 22, 2009 2:09:21 PM org.apache.tuscany.sca.host.rmi.DefaultRMIHost registerS
+ervice
+INFO: RMI service registered: rmi://localhost:8085/MultiplyService
+Jun 22, 2009 2:09:21 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry addEndpoint
+INFO: EndpointRegistry: Add endpoint - Endpoint: URI = OperationsComponent#serv
+ice-binding(DivideService/DivideService)
+Jun 22, 2009 2:09:21 PM org.apache.tuscany.sca.host.rmi.DefaultRMIHost registerS
+ervice
+INFO: RMI service registered: rmi://localhost:8085/DivideService
+
+osgi>
+
+To stop the bundle:
+
+osgi> stop 181
+Jun 22, 2009 2:09:48 PM org.apache.tuscany.sca.node.impl.NodeImpl stop
+INFO: Stopping node: calculator.dosgi.operations
+Jun 22, 2009 2:09:48 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry removeEndpoint
+INFO: EndpointRegistry: Remove endpoint - Endpoint: URI = OperationsComponent#s
+ervice-binding(AddService/AddService)
+Jun 22, 2009 2:09:48 PM org.apache.tuscany.sca.host.rmi.DefaultRMIHost unregiste
+rService
+INFO: RMI service unregistered: rmi://localhost:8085/AddService
+Jun 22, 2009 2:09:48 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry removeEndpoint
+INFO: EndpointRegistry: Remove endpoint - Endpoint: URI = OperationsComponent#s
+ervice-binding(SubtractService/SubtractService)
+Jun 22, 2009 2:09:48 PM org.apache.tuscany.sca.host.rmi.DefaultRMIHost unregiste
+rService
+INFO: RMI service unregistered: rmi://localhost:8085/SubtractService
+Jun 22, 2009 2:09:48 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry removeEndpoint
+INFO: EndpointRegistry: Remove endpoint - Endpoint: URI = OperationsComponent#s
+ervice-binding(MultiplyService/MultiplyService)
+Jun 22, 2009 2:09:48 PM org.apache.tuscany.sca.host.rmi.DefaultRMIHost unregiste
+rService
+INFO: RMI service unregistered: rmi://localhost:8085/MultiplyService
+Jun 22, 2009 2:09:48 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry removeEndpoint
+INFO: EndpointRegistry: Remove endpoint - Endpoint: URI = OperationsComponent#s
+ervice-binding(DivideService/DivideService)
+Jun 22, 2009 2:09:48 PM org.apache.tuscany.sca.host.rmi.DefaultRMIHost unregiste
+rService
+INFO: RMI service unregistered: rmi://localhost:8085/DivideService
+Jun 22, 2009 2:09:48 PM calculator.dosgi.operations.impl.OperationsActivator sto
+p
+INFO: Stopping file:./target/sample-dosgi-calculator-operations.jar [181]
+
+osgi>
+
+To exit the console, run:
+osgi> exit
+
+Sample Overview
+---------------
+The application consists of two OSGi bundles:
+ * The calculator bundle: It provides the calculator service. The service is implemented by a java class that
+ consumes other services to perform the “add”, “subtract”, “multiply” and “divide” operations.
+ * The operations bundle: It provides the add/subtract/multiply/divide services.
+ (See ../samples/dosgi-calculator-operations)
+
+
+dosgi-calculator-operations/
+ src/
+ main/
+ java/
+ calculator/
+ dosgi/
+ operations/
+ AddService.java - Interface for Add
+ SubtractService.java - Interface for Subtract
+ MultiplyService.java - Interface for Multiply
+ DivideService.java - Interface for Divide
+ impl/
+ OperationsActivator.java - OSGi bundle activator
+ AddServiceImpl.java - Implementation for Add
+ SubtractServiceImpl.java - Implementation for Subtract
+ MultiplyServiceImpl.java - Implementation for Multiply
+ DivideServiceImpl.java - Implementation for Divide
+ resources/
+ META-INF/
+ sca-contribution.xml
+ OSGI-INF/
+ sca/
+ bundle.componentType - The component type for implementation.osgi of this bundle
+ bundle.composite - The composite file
+ test/
+ java/
+ src/
+ calculator/
+ dosgi/
+ operations/
+ test/
+ OperationsOSGiNodeTestCase.java - The JUNIT test case that tests this bundle using a RMI client
+
+ META-INF/
+ MANIFEST.MF - The OSGi manifest for this bundle
+ pom.xml - the Maven build file
+
+
+
+Building And Running The Test Case Using Maven
+-------------------------------------------
+With either the binary or source distributions the sample can be built and run
+using Maven as follows.
+
+cd dosgi-calculator-operations
+mvn
+
diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/pom.xml b/java/sca/samples/dosgi-dynamic-calculator-operations/pom.xml new file mode 100644 index 0000000000..037782997f --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/pom.xml @@ -0,0 +1,134 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<project> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.tuscany.sca</groupId> + <artifactId>tuscany-sca</artifactId> + <version>2.0-SNAPSHOT</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <artifactId>sample-dosgi-dynamic-calculator-operations</artifactId> + <name>Apache Tuscany SCA OSGi RemoteService Dynamic Caculator Operations Sample</name> + + <dependencies> + <dependency> + <groupId>org.apache.tuscany.sca</groupId> + <artifactId>tuscany-feature-ejava</artifactId> + <version>2.0-SNAPSHOT</version> + <type>pom</type> + </dependency> + + <dependency> + <groupId>org.apache.tuscany.sca</groupId> + <artifactId>tuscany-node-launcher-equinox</artifactId> + <version>2.0-SNAPSHOT</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.apache.tuscany.sca</groupId> + <artifactId>tuscany-node-impl-osgi</artifactId> + <version>2.0-SNAPSHOT</version> + <scope>runtime</scope> + </dependency> + + <dependency> + <groupId>org.eclipse.osgi</groupId> + <artifactId>services</artifactId> + <version>3.2.0-v20090520-1800</version> + <scope>test</scope> + </dependency> + + <!-- Equinox Declarative Services --> + <dependency> + <groupId>org.eclipse.equinox</groupId> + <artifactId>ds</artifactId> + <version>1.1.0-v20090601</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.eclipse.equinox</groupId> + <artifactId>util</artifactId> + <version>1.0.100-v20090520-1800</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.5</version> + <scope>test</scope> + </dependency> + + </dependencies> + + <build> + <finalName>${artifactId}</finalName> + <plugins> + <plugin> + <artifactId>maven-eclipse-plugin</artifactId> + <version>2.5.1</version> + <configuration> + <buildcommands> + <buildcommand>org.eclipse.pde.ManifestBuilder</buildcommand> + <buildcommand>org.eclipse.jdt.core.javabuilder</buildcommand> + </buildcommands> + <projectnatures> + <projectnature>org.eclipse.jdt.core.javanature</projectnature> + <projectnature>org.eclipse.pde.PluginNature</projectnature> + </projectnatures> + <classpathContainers> + <classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER + </classpathContainer> + </classpathContainers> + </configuration> + </plugin> + + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifestFile>${basedir}/META-INF/MANIFEST.MF</manifestFile> + </archive> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.tuscany.maven.plugins</groupId> + <artifactId>maven-osgi-junit-plugin</artifactId> + <version>1.0-SNAPSHOT</version> + <executions> + <execution> + <id>osgi-test</id> + <phase>test</phase> + <goals> + <goal>test</goal> + </goals> + <configuration></configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/AddService.java b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/AddService.java new file mode 100644 index 0000000000..971500782f --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/AddService.java @@ -0,0 +1,31 @@ +/* + * 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 calculator.dosgi.operations; + +import org.oasisopen.sca.annotation.Remotable; + +/** + * The interface for the add service + */ +@Remotable +public interface AddService { + + double add(double n1, double n2); + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/DivideService.java b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/DivideService.java new file mode 100644 index 0000000000..49b8a1c0bf --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/DivideService.java @@ -0,0 +1,31 @@ +/* + * 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 calculator.dosgi.operations; + +import org.oasisopen.sca.annotation.Remotable; + +/** + * The interface for the divide service + */ +@Remotable +public interface DivideService { + + double divide(double n1, double n2); + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/MultiplyService.java b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/MultiplyService.java new file mode 100644 index 0000000000..f4e59d12ea --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/MultiplyService.java @@ -0,0 +1,31 @@ +/* + * 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 calculator.dosgi.operations; + +import org.oasisopen.sca.annotation.Remotable; + +/** + * The interface for the multiply service + */ +@Remotable +public interface MultiplyService { + + double multiply(double n1, double n2); + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/SubtractService.java b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/SubtractService.java new file mode 100644 index 0000000000..bfb9b820f7 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/SubtractService.java @@ -0,0 +1,31 @@ +/* + * 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 calculator.dosgi.operations; + +import org.oasisopen.sca.annotation.Remotable; + +/** + * The interface for the subtract service + */ +@Remotable +public interface SubtractService { + + double subtract(double n1, double n2); + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/AddServiceImpl.java b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/AddServiceImpl.java new file mode 100644 index 0000000000..66b2977241 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/AddServiceImpl.java @@ -0,0 +1,37 @@ +/* + * 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 calculator.dosgi.operations.impl; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import calculator.dosgi.operations.AddService; + +/** + * An implementation of the Add service + */ +public class AddServiceImpl implements AddService { + + public double add(double n1, double n2) { + Logger logger = Logger.getLogger("calculator"); + logger.log(Level.INFO, "Adding " + n1 + " and " + n2); + return n1 + n2; + } + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/DivideServiceImpl.java b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/DivideServiceImpl.java new file mode 100644 index 0000000000..a3c21b2b96 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/DivideServiceImpl.java @@ -0,0 +1,37 @@ +/* + * 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 calculator.dosgi.operations.impl; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import calculator.dosgi.operations.DivideService; + +/** + * An implementation of the Divide service. + */ +public class DivideServiceImpl implements DivideService { + + public double divide(double n1, double n2) { + Logger logger = Logger.getLogger("calculator"); + logger.log(Level.INFO, "Dividing " + n1 + " with " + n2); + return n1 / n2; + } + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/MultiplyServiceImpl.java b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/MultiplyServiceImpl.java new file mode 100644 index 0000000000..7922d2d392 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/MultiplyServiceImpl.java @@ -0,0 +1,37 @@ +/* + * 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 calculator.dosgi.operations.impl; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import calculator.dosgi.operations.MultiplyService; + +/** + * An implementation of the Multiply service. + */ +public class MultiplyServiceImpl implements MultiplyService { + + public double multiply(double n1, double n2) { + Logger logger = Logger.getLogger("calculator"); + logger.log(Level.INFO, "Multiplying " + n1 + " with " + n2); + return n1 * n2; + } + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/OperationsActivator.java b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/OperationsActivator.java new file mode 100644 index 0000000000..a9d727b7e9 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/OperationsActivator.java @@ -0,0 +1,94 @@ +/* + * 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 calculator.dosgi.operations.impl; + +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.logging.Logger; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.packageadmin.PackageAdmin; + +import calculator.dosgi.operations.AddService; +import calculator.dosgi.operations.DivideService; +import calculator.dosgi.operations.MultiplyService; +import calculator.dosgi.operations.SubtractService; + +/** + * + */ +public class OperationsActivator implements BundleActivator { + private Logger logger = Logger.getLogger(OperationsActivator.class.getName()); + + public void start(BundleContext context) throws Exception { + logger.info("Starting " + context.getBundle()); + + Dictionary<String, Object> props = new Hashtable<String, Object>(); + props.put("service.exported.configs", new String[] {"sca"}); + props.put("service.exported.interfaces", new String[] {"*"}); + + logger.info("Registering " + AddService.class.getName()); + props.put("sca.service", "AddComponent#service-name(Add)"); + props.put("sca.bindings", new String[] {"OSGI-INF/sca/add-service.bindings"}); + context.registerService(AddService.class.getName(), new AddServiceImpl(), props); + + logger.info("Registering " + SubtractService.class.getName()); + props.put("sca.service", "SubtractComponent#service-name(Subtract)"); + props.put("sca.bindings", new String[] {"OSGI-INF/sca/subtract-service.bindings"}); + context.registerService(SubtractService.class.getName(), new SubtractServiceImpl(), props); + + logger.info("Registering " + MultiplyService.class.getName()); + props.put("sca.service", "MultiplyComponent#service-name(Multiply)"); + props.put("sca.bindings", new String[] {"OSGI-INF/sca/multiply-service.bindings"}); + context.registerService(MultiplyService.class.getName(), new MultiplyServiceImpl(), props); + + logger.info("Registering " + DivideService.class.getName()); + props.put("sca.service", "DivideComponent#service-name(Divide)"); + props.put("sca.bindings", new String[] {"OSGI-INF/sca/divide-service.bindings"}); + context.registerService(DivideService.class.getName(), new DivideServiceImpl(), props); + + getBundle(context, AddService.class); + } + + public void stop(BundleContext context) throws Exception { + logger.info("Stopping " + context.getBundle()); + // Registered services will be automatically unregistered + } + + private Bundle getBundle(BundleContext bundleContext, Class<?> cls) { + PackageAdmin packageAdmin = null; + // PackageAdmin is used to resolve bundles + ServiceReference ref = bundleContext.getServiceReference("org.osgi.service.packageadmin.PackageAdmin"); + if (ref != null) { + packageAdmin = (PackageAdmin)bundleContext.getService(ref); + Bundle bundle = packageAdmin.getBundle(cls); + if (bundle != null) { + logger.info(cls.getName() + " is loaded by bundle: " + bundle.getSymbolicName()); + } + bundleContext.ungetService(ref); + return bundle; + } + return null; + } + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/SubtractServiceImpl.java b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/SubtractServiceImpl.java new file mode 100644 index 0000000000..4bbe83b14f --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/src/main/java/calculator/dosgi/operations/impl/SubtractServiceImpl.java @@ -0,0 +1,37 @@ +/* + * 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 calculator.dosgi.operations.impl; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import calculator.dosgi.operations.SubtractService; + +/** + * An implementation of the subtract service. + */ +public class SubtractServiceImpl implements SubtractService { + + public double subtract(double n1, double n2) { + Logger logger = Logger.getLogger("calculator"); + logger.log(Level.INFO, "Subtracting " + n1 + " from " + n2); + return n1 - n2; + } + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/src/test/java/calculator/dosgi/operations/test/OSGiTestUtils.java b/java/sca/samples/dosgi-dynamic-calculator-operations/src/test/java/calculator/dosgi/operations/test/OSGiTestUtils.java new file mode 100644 index 0000000000..cd92989da1 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/src/test/java/calculator/dosgi/operations/test/OSGiTestUtils.java @@ -0,0 +1,105 @@ +/* + * 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 calculator.dosgi.operations.test; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import org.osgi.framework.Bundle; + +/** + * + * Utility class to create OSGi bundles + * + * @version $Rev$ $Date$ + */ +public class OSGiTestUtils { + private static class InvocationHandlerImpl implements InvocationHandler { + private Object instance; + + public InvocationHandlerImpl(Object instance) { + super(); + this.instance = instance; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Method m = instance.getClass().getMethod(method.getName(), method.getParameterTypes()); + return m.invoke(instance, args); + } + + } + + /** + * Returns a string representation of the given bundle. + * + * @param b + * @param verbose + * @return + */ + public static String bundleStatus(Bundle bundle, boolean verbose) { + StringBuffer sb = new StringBuffer(); + sb.append(bundle.getBundleId()).append(" ").append(bundle.getSymbolicName()); + int s = bundle.getState(); + if ((s & Bundle.UNINSTALLED) != 0) { + sb.append(" UNINSTALLED"); + } + if ((s & Bundle.INSTALLED) != 0) { + sb.append(" INSTALLED"); + } + if ((s & Bundle.RESOLVED) != 0) { + sb.append(" RESOLVED"); + } + if ((s & Bundle.STARTING) != 0) { + sb.append(" STARTING"); + } + if ((s & Bundle.STOPPING) != 0) { + sb.append(" STOPPING"); + } + if ((s & Bundle.ACTIVE) != 0) { + sb.append(" ACTIVE"); + } + + if (verbose) { + sb.append(" ").append(bundle.getLocation()); + sb.append(" ").append(bundle.getHeaders()); + } + return sb.toString(); + } + + /** + * A utility to cast the object to the given interface. If the class for the object + * is loaded by a different classloader, a proxy will be created. + * + * @param <T> + * @param obj + * @param cls + * @return + */ + public static <T> T cast(Object obj, Class<T> cls) { + if (cls.isInstance(obj)) { + return cls.cast(obj); + } else { + return cls.cast(Proxy.newProxyInstance(cls.getClassLoader(), + new Class<?>[] {cls}, + new InvocationHandlerImpl(obj))); + } + } +} diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/src/test/java/calculator/dosgi/operations/test/OperationsNode.java b/java/sca/samples/dosgi-dynamic-calculator-operations/src/test/java/calculator/dosgi/operations/test/OperationsNode.java new file mode 100644 index 0000000000..02007c385c --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/src/test/java/calculator/dosgi/operations/test/OperationsNode.java @@ -0,0 +1,43 @@ +/* + * 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 calculator.dosgi.operations.test; + +import org.apache.tuscany.sca.node.equinox.launcher.NodeLauncher; + +/** + * + */ +public class OperationsNode { + + /** + * @param args + */ + public static void main(String[] args) { + if (args.length == 0) { + args = new String[] {"-bundles"}; + } + try { + NodeLauncher.main(args); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator-operations/src/test/java/calculator/dosgi/operations/test/OperationsOSGiNodeTestCase.java b/java/sca/samples/dosgi-dynamic-calculator-operations/src/test/java/calculator/dosgi/operations/test/OperationsOSGiNodeTestCase.java new file mode 100644 index 0000000000..eee64bf86f --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator-operations/src/test/java/calculator/dosgi/operations/test/OperationsOSGiNodeTestCase.java @@ -0,0 +1,104 @@ +/* + * 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 calculator.dosgi.operations.test; + +import static calculator.dosgi.operations.test.OSGiTestUtils.bundleStatus; + +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; + +import org.apache.tuscany.sca.node.equinox.launcher.EquinoxHost; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; + +import calculator.dosgi.operations.AddService; + +/** + * + */ +public class OperationsOSGiNodeTestCase { + private static EquinoxHost host; + private static BundleContext context; + private static Bundle operationsBundle; + + /** + * @throws java.lang.Exception + */ + @BeforeClass + public static void setUpBeforeClass() throws Exception { + try { + host = new EquinoxHost(); + context = host.start(); + + for (Bundle b : context.getBundles()) { + if (b.getSymbolicName().equals("org.eclipse.equinox.ds") || b.getSymbolicName() + .startsWith("org.apache.tuscany.sca.")) { + try { + if (b.getHeaders().get(Constants.FRAGMENT_HOST) == null) { + // Start the non-fragment bundle + b.start(); + } + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println(bundleStatus(b, false)); + } + if ("calculator.dosgi.dynamic.operations".equals(b.getSymbolicName())) { + operationsBundle = b; + } + } + + if (operationsBundle != null) { + operationsBundle.start(); + System.out.println(bundleStatus(operationsBundle, false)); + } + + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + @Test + public void testOSGi() throws Exception { + Registry registry = LocateRegistry.getRegistry(8085); + Object add = registry.lookup("AddService"); + AddService addService = OSGiTestUtils.cast(add, AddService.class); + double sum = addService.add(1.0, 2.0); + Assert.assertEquals(3.0, sum, 0.0); + } + + /** + * @throws java.lang.Exception + */ + @AfterClass + public static void tearDownAfterClass() throws Exception { + if (host != null) { + host.stop(); + context = null; + } + } + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/LICENSE b/java/sca/samples/dosgi-dynamic-calculator/LICENSE new file mode 100644 index 0000000000..6e529a25c4 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/LICENSE @@ -0,0 +1,205 @@ +
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+
diff --git a/java/sca/samples/dosgi-dynamic-calculator/META-INF/MANIFEST.MF b/java/sca/samples/dosgi-dynamic-calculator/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..f943dae97a --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/META-INF/MANIFEST.MF @@ -0,0 +1,20 @@ +Manifest-Version: 1.0
+Export-Package: calculator.dosgi;version="1.0.1",
+ calculator.dosgi.operations;version="1.0.1"
+Bundle-Version: 1.0.0
+Bundle-Name: calculator.dosgi.dynamic
+Bundle-Activator: calculator.dosgi.impl.CalculatorActivator
+Bundle-ManifestVersion: 2
+Import-Package: org.oasisopen.sca.annotation;version="2.0.0",
+ org.osgi.framework,
+ org.osgi.service.component;resolution:=optional,
+ org.osgi.service.packageadmin,
+ org.osgi.util.tracker
+Bundle-SymbolicName: calculator.dosgi.dynamic
+Bundle-Vendor: The Apache Software Foundation
+Bundle-ActivationPolicy: lazy
+Eclipse-LazyStart: true
+Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
+Bundle-DocURL: http://www.apache.org/
+Service-Component: OSGI-INF/calculator-component.xml
+Bundle-RequiredExecutionEnvironment: J2SE-1.5,JavaSE-1.6 diff --git a/java/sca/samples/dosgi-dynamic-calculator/NOTICE b/java/sca/samples/dosgi-dynamic-calculator/NOTICE new file mode 100644 index 0000000000..51042eab05 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/NOTICE @@ -0,0 +1,6 @@ +${pom.name}
+Copyright (c) 2005 - 2009 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
diff --git a/java/sca/samples/dosgi-dynamic-calculator/OSGI-INF/blueprint/calculator-module.xml b/java/sca/samples/dosgi-dynamic-calculator/OSGI-INF/blueprint/calculator-module.xml new file mode 100644 index 0000000000..fd834e12ef --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/OSGI-INF/blueprint/calculator-module.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<!-- A sample module-context.xml for OSGI RFC 124 (BluePrint Service) --> +<components xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <component id="CalculatorComponent" class="calculator.dosgi.impl.CalculatorServiceImpl"> + <property name="addService" ref="AddService" /> + <property name="subtractService" ref="SubtractService" /> + <property name="multiplyService" ref="MultiplyService" /> + <property name="divideService" ref="DivideService" /> + </component> + + <!-- We can derive the SCA services for the implementation.osgi --> + <service id="CalculatorService" ref="CalculatorComponent" interface="calculator.dosgi.CalculatorService"> + </service> + + <!-- We can derive the SCA references for the implementation.osgi --> + <reference id="AddService" interface="calculator.dosgi.operations.AddService"> + </reference> + <reference id="SubtractService" interface="calculator.dosgi.operations.SubtractService"> + </reference> + <reference id="MultiplyService" interface="calculator.dosgi.operations.MultiplyService"> + </reference> + <reference id="DivideService" interface="calculator.dosgi.operations.DivideService"> + </reference> + +</components>
\ No newline at end of file diff --git a/java/sca/samples/dosgi-dynamic-calculator/OSGI-INF/calculator-component.xml b/java/sca/samples/dosgi-dynamic-calculator/OSGI-INF/calculator-component.xml new file mode 100644 index 0000000000..5daaa59aae --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/OSGI-INF/calculator-component.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<scr:component name="CalculatorComponent" + xmlns:scr="http://www.osgi.org/xmlns/scr/v1.0.0"> + <implementation class="calculator.dosgi.impl.CalculatorServiceDSImpl" /> + <service> + <provide interface="calculator.dosgi.CalculatorService" /> + </service> + + <reference name="addService" interface="calculator.dosgi.operations.AddService" bind="setAddService" unbind="unsetAddService" + policy="dynamic" /> + <reference name="subtractService" interface="calculator.dosgi.operations.SubtractService" bind="setSubtractService" + unbind="unsetSubtractService" policy="dynamic" /> + <reference name="multiplyService" interface="calculator.dosgi.operations.MultiplyService" bind="setMultiplyService" + unbind="unsetMultiplyService" policy="dynamic" /> + <reference name="divideService" interface="calculator.dosgi.operations.DivideService" bind="setDivideService" + unbind="unsetDivideService" policy="dynamic" /> + +</scr:component> diff --git a/java/sca/samples/dosgi-calculator/OSGI-INF/remote-service/calculator-service-descriptions.xml b/java/sca/samples/dosgi-dynamic-calculator/OSGI-INF/remote-service/calculator-service-descriptions.xml index da76d00024..53e83bf72c 100644 --- a/java/sca/samples/dosgi-calculator/OSGI-INF/remote-service/calculator-service-descriptions.xml +++ b/java/sca/samples/dosgi-dynamic-calculator/OSGI-INF/remote-service/calculator-service-descriptions.xml @@ -18,50 +18,64 @@ * under the License. --> <!-- A consumer-side service description file for RFC 119 --> -<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0" xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200903"> +<service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0" + xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200903" + xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1"> <!-- Describe a remote OSGi service --> <service-description> <provide interface="calculator.dosgi.operations.AddService" /> - <property name="service.intents">sca:SOAP sca:HTTP</property> - <property name="osgi.remote.configuration.type">sca</property> - <property name="osgi.remote.configuration.sca.componentType"> + <property name="service.exported.intents">sca:SOAP sca:HTTP</property> + <property name="service.exported.configs">sca</property> + <property name="sca.componentType"> OSGI-INF/sca/bundle.componentType </property> - <property name="osgi.remote.configuration.sca.reference"> + <property name="sca.reference"> addService </property> + <bindings> + <tuscany:binding.rmi uri="rmi://localhost:8085/AddService" /> + </bindings> </service-description> <service-description> <provide interface="calculator.dosgi.operations.SubtractService" /> - <property name="service.intents">sca:SOAP sca:HTTP</property> - <property name="osgi.remote.configuration.type">sca</property> - <property name="osgi.remote.configuration.sca.componentType"> + <property name="service.exported.intents">sca:SOAP sca:HTTP</property> + <property name="service.exported.configs">sca</property> + <property name="sca.componentType"> OSGI-INF/sca/bundle.componentType </property> - <property name="osgi.remote.configuration.sca.reference"> + <property name="sca.reference"> subtractService </property> + <bindings> + <tuscany:binding.rmi uri="rmi://localhost:8085/SubtractService" /> + </bindings> </service-description> <service-description> <provide interface="calculator.dosgi.operations.MultiplyService" /> - <property name="service.intents">sca:SOAP sca:HTTP</property> - <property name="osgi.remote.configuration.type">sca</property> - <property name="osgi.remote.configuration.sca.componentType"> + <property name="service.exported.intents">sca:SOAP sca:HTTP</property> + <property name="service.exported.configs">sca</property> + <property name="sca.componentType"> OSGI-INF/sca/bundle.componentType </property> - <property name="osgi.remote.configuration.sca.reference"> + <property name="sca.reference"> multiplyService </property> + <bindings> + <tuscany:binding.rmi uri="rmi://localhost:8085/MultiplyService" /> + </bindings> </service-description> <service-description> <provide interface="calculator.dosgi.operations.DivideService" /> - <property name="service.intents">sca:SOAP sca:HTTP</property> - <property name="osgi.remote.configuration.type">sca</property> - <property name="osgi.remote.configuration.sca.componentType"> + <property name="service.exported.intents">sca:SOAP sca:HTTP</property> + <property name="service.exported.configs">sca</property> + <property name="sca.componentType"> OSGI-INF/sca/bundle.componentType </property> - <property name="osgi.remote.configuration.sca.reference"> + <property name="sca.reference"> divideService </property> + <bindings> + <tuscany:binding.rmi uri="rmi://localhost:8085/DivideService" /> + </bindings> </service-description> </service-descriptions>
\ No newline at end of file diff --git a/java/sca/samples/dosgi-dynamic-calculator/OSGI-INF/sca/calculator-service.bindings b/java/sca/samples/dosgi-dynamic-calculator/OSGI-INF/sca/calculator-service.bindings new file mode 100644 index 0000000000..90e55f7377 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/OSGI-INF/sca/calculator-service.bindings @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+-->
+<!-- SCA bindings for calculator service -->
+<bindings xmlns="http://www.osgi.org/xmlns/sd/v1.0.0"
+ xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200903">
+ <sca:binding.ws uri="http://localhost:8086/CalculatorService"/>
+</bindings>
\ No newline at end of file diff --git a/java/sca/samples/dosgi-dynamic-calculator/README b/java/sca/samples/dosgi-dynamic-calculator/README new file mode 100644 index 0000000000..374c0f0a8b --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/README @@ -0,0 +1,155 @@ +Distributed OSGi Calculator Sample
+==================================
+This sample implements a distributed calculator using Distributed OSGi (RFC 119) over SCA.
+
+The README in the samples directory (the directory above this) provides
+general instructions about building and running samples. Take a look there
+first.
+
+On Windows, run
+java -jar ..\..\modules\osgi-3.4.0-v20080605-1900.jar -configuration ..\..\features\configuration -clean -console
+
+On *Unix, run
+java -jar ../../modules/osgi-3.4.0-v20080605-1900.jar -configuration ../../features/configuration -clean -console
+
+You should see the osgi console:
+
+osgi>
+
+osgi> Jun 22, 2009 1:32:27 PM org.apache.tuscany.sca.extensibility.equinox.EquinoxServiceDiscoveryActivator start
+INFO: Equinox-based service discoverer is now configured.
+
+You can run "ss" command under the osgi> to see the status of the bundles.
+osgi> ss
+
+Then you can install and start the calculator.dosgi bundle:
+
+osgi> install file:./target/sample-dosgi-calculator.jar
+Bundle id is 181
+
+osgi> start 181
+Jun 22, 2009 1:37:21 PM calculator.dosgi.impl.CalculatorActivator start
+INFO: Starting file:./target/sample-dosgi-calculator.jar [181]
+Jun 22, 2009 1:37:21 PM calculator.dosgi.impl.CalculatorActivator start
+INFO: Registering calculator.dosgi.CalculatorService
+Jun 22, 2009 1:37:21 PM calculator.dosgi.impl.CalculatorActivator getBundle
+INFO: calculator.dosgi.operations.AddService is loaded by bundle: calculator.dos
+gi
+Jun 22, 2009 1:37:21 PM org.apache.tuscany.sca.node.impl.NodeImpl start
+INFO: Starting node: calculator.dosgi
+Jun 22, 2009 1:37:21 PM org.apache.tuscany.sca.node.impl.NodeFactoryImpl configu
+reNode
+INFO: Loading contribution: bundleentry://181/
+Jun 22, 2009 1:37:21 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry addEndpoint
+INFO: EndpointRegistry: Add endpoint - Endpoint: URI = CalculatorComponent#serv
+ice-binding(CalculatorService/CalculatorService)
+2009-06-22 13:37:21.953::INFO: jetty-6.1.x
+2009-06-22 13:37:21.953::INFO: Started SelectChannelConnector@0.0.0.0:8086
+Jun 22, 2009 1:37:21 PM org.apache.tuscany.sca.http.jetty.JettyServer addServlet
+Mapping
+INFO: Added Servlet mapping: http://rfengt61p:8086/CalculatorService
+Jun 22, 2009 1:37:21 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry addEndpointReference
+INFO: EndpointRegistry: Add endpoint reference - Endpoint Reference: URI = Calc
+ulatorComponent#reference-binding(addService/addService) Target = Endpoint:
+Jun 22, 2009 1:37:21 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry addEndpointReference
+INFO: EndpointRegistry: Add endpoint reference - Endpoint Reference: URI = Calc
+ulatorComponent#reference-binding(subtractService/subtractService) Target = Endp
+oint:
+Jun 22, 2009 1:37:21 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry addEndpointReference
+INFO: EndpointRegistry: Add endpoint reference - Endpoint Reference: URI = Calc
+ulatorComponent#reference-binding(multiplyService/multiplyService) Target = Endp
+oint:
+Jun 22, 2009 1:37:21 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry addEndpointReference
+INFO: EndpointRegistry: Add endpoint reference - Endpoint Reference: URI = Calc
+ulatorComponent#reference-binding(divideService/divideService) Target = Endpoint
+:
+
+osgi>
+
+You can point your browser to http://localhost:8086/CalculatorService?wsdl to see
+the WSDL.
+
+You can also use the WebService Explorer from Eclipse WTP to test the Web Service.
+
+To stop the bundle:
+
+osgi> stop 181
+Jun 22, 2009 1:39:09 PM org.apache.tuscany.sca.node.impl.NodeImpl stop
+INFO: Stopping node: calculator.dosgi
+Jun 22, 2009 1:39:09 PM org.apache.tuscany.sca.endpoint.tribes.ReplicatedEndpoin
+tRegistry removeEndpoint
+INFO: EndpointRegistry: Remove endpoint - Endpoint: URI = CalculatorComponent#s
+ervice-binding(CalculatorService/CalculatorService)
+Jun 22, 2009 1:39:09 PM org.apache.tuscany.sca.http.jetty.JettyServer removeServ
+letMapping
+INFO: Removed Servlet mapping: /CalculatorService
+Jun 22, 2009 1:39:09 PM calculator.dosgi.impl.CalculatorActivator stop
+INFO: Stopping file:./target/sample-dosgi-calculator.jar [181]
+
+To exit the console, run:
+osgi> exit
+
+Sample Overview
+---------------
+The application consists of two OSGi bundles:
+ * The calculator bundle: It provides the calculator service. The service is implemented by a java class that
+ consumes other services to perform the “add”, “subtract”, “multiply” and “divide” operations.
+ * The operations bundle: It provides the add/subtract/multiply/divide services.
+ (See ../samples/dosgi-calculator-operations)
+
+
+dosgi-calculator/
+ src/
+ main/
+ java/
+ calculator/
+ dosgi/
+ CalculatorService.java - The interface for Calculator service
+ impl/
+ CalculatorActivator.java - OSGi bundle activator for Calculator bundle
+ CalculatorServiceDSImpl.java - OSGi declarative service based implementation
+ CalculatorServiceImpl.java - Basic OSGi implementation
+ operations/
+ AddService.java - Interface for Add
+ SubtractService.java - Interface for Subtract
+ MultiplyService.java - Interface for Multiply
+ DivideService.java - Interface for Divide
+ rmi/
+ OperationsRemote.java - RMI remote interface for operations
+ OperationsRMIServer_Stub.java - RMI stub
+ OperationsRMIServer.java - RMI server implementation of the operations
+ resources/
+ META-INF/
+ sca-contribution.xml
+ OSGI-INF/
+ sca/
+ bundle.componentType - The component type for implementation.osgi of this bundle
+ bundle.composite - The composite file
+ test/
+ java/
+ src/
+ calculator/
+ dosgi/
+ test/
+ CalculatorOSGiNodeTestCase.java - The JUNIT test case that tests this bundle against a RMI service
+
+ META-INF/
+ MANIFEST.MF - The OSGi manifest for this bundle
+ dosig-calculator.png - a pictorial representation of the sample
+ pom.xml - the Maven build file
+
+
+
+Building And Running The Test Case Using Maven
+-------------------------------------------
+With either the binary or source distributions the sample can be built and run
+using Maven as follows.
+
+cd dosgi-calculator
+mvn
+
diff --git a/java/sca/samples/dosgi-dynamic-calculator/dosgi-calculator.png b/java/sca/samples/dosgi-dynamic-calculator/dosgi-calculator.png Binary files differnew file mode 100644 index 0000000000..805baa54d2 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/dosgi-calculator.png diff --git a/java/sca/samples/dosgi-dynamic-calculator/pom.xml b/java/sca/samples/dosgi-dynamic-calculator/pom.xml new file mode 100644 index 0000000000..3151c7fef6 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/pom.xml @@ -0,0 +1,139 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<project> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.tuscany.sca</groupId> + <artifactId>tuscany-sca</artifactId> + <version>2.0-SNAPSHOT</version> + <relativePath>../../pom.xml</relativePath> + </parent> + + <artifactId>sample-dosgi-dynamic-calculator</artifactId> + <name>Apache Tuscany SCA OSGi RemoteService Dynamic Caculator Sample</name> + + <dependencies> + <dependency> + <groupId>org.apache.tuscany.sca</groupId> + <artifactId>tuscany-feature-ejava</artifactId> + <version>2.0-SNAPSHOT</version> + <type>pom</type> + </dependency> + <dependency> + <groupId>org.apache.tuscany.sca</groupId> + <artifactId>tuscany-feature-webservice</artifactId> + <version>2.0-SNAPSHOT</version> + <type>pom</type> + <scope>runtime</scope> + </dependency> + <dependency> + <groupId>org.apache.tuscany.sca</groupId> + <artifactId>tuscany-node-launcher-equinox</artifactId> + <version>2.0-SNAPSHOT</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.apache.tuscany.sca</groupId> + <artifactId>tuscany-node-impl-osgi</artifactId> + <version>2.0-SNAPSHOT</version> + <scope>runtime</scope> + </dependency> + + <dependency> + <groupId>org.eclipse.osgi</groupId> + <artifactId>services</artifactId> + <version>3.2.0-v20090520-1800</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.5</version> + <scope>test</scope> + </dependency> + + <!-- Equinox Declarative Services --> + <dependency> + <groupId>org.eclipse.equinox</groupId> + <artifactId>ds</artifactId> + <version>1.1.0-v20090601</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.eclipse.equinox</groupId> + <artifactId>util</artifactId> + <version>1.0.100-v20090520-1800</version> + <scope>test</scope> + </dependency> + + </dependencies> + + <build> + <finalName>${artifactId}</finalName> + <plugins> + <plugin> + <artifactId>maven-eclipse-plugin</artifactId> + <version>2.5.1</version> + <configuration> + <buildcommands> + <buildcommand>org.eclipse.pde.ManifestBuilder</buildcommand> + <buildcommand>org.eclipse.jdt.core.javabuilder</buildcommand> + </buildcommands> + <projectnatures> + <projectnature>org.eclipse.jdt.core.javanature</projectnature> + <projectnature>org.eclipse.pde.PluginNature</projectnature> + </projectnatures> + <classpathContainers> + <classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER + </classpathContainer> + </classpathContainers> + </configuration> + </plugin> + + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifestFile>${basedir}/META-INF/MANIFEST.MF</manifestFile> + </archive> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.tuscany.maven.plugins</groupId> + <artifactId>maven-osgi-junit-plugin</artifactId> + <version>1.0-SNAPSHOT</version> + <executions> + <execution> + <id>osgi-test</id> + <phase>test</phase> + <goals> + <goal>test</goal> + </goals> + <configuration></configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/CalculatorService.java b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/CalculatorService.java new file mode 100644 index 0000000000..cc562b7c2f --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/CalculatorService.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 calculator.dosgi; + +import org.oasisopen.sca.annotation.Remotable; + +/** + * The Calculator service interface. + */ +@Remotable +public interface CalculatorService { + + double add(double n1, double n2); + + double subtract(double n1, double n2); + + double multiply(double n1, double n2); + + double divide(double n1, double n2); +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/impl/CalculatorActivator.java b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/impl/CalculatorActivator.java new file mode 100644 index 0000000000..40388144ee --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/impl/CalculatorActivator.java @@ -0,0 +1,78 @@ +/* + * 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 calculator.dosgi.impl; + +import java.util.Dictionary; +import java.util.Hashtable; +import java.util.logging.Logger; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.packageadmin.PackageAdmin; + +import calculator.dosgi.CalculatorService; +import calculator.dosgi.operations.AddService; + +/** + * + */ +public class CalculatorActivator implements BundleActivator { + private Logger logger = Logger.getLogger(CalculatorActivator.class.getName()); + + private Bundle getBundle(BundleContext bundleContext, Class<?> cls) { + PackageAdmin packageAdmin = null; + // PackageAdmin is used to resolve bundles + ServiceReference ref = bundleContext.getServiceReference("org.osgi.service.packageadmin.PackageAdmin"); + if (ref != null) { + packageAdmin = (PackageAdmin)bundleContext.getService(ref); + Bundle bundle = packageAdmin.getBundle(cls); + if (bundle != null) { + logger.info(cls.getName() + " is loaded by bundle: " + bundle.getSymbolicName()); + } + bundleContext.ungetService(ref); + return bundle; + } + return null; + } + + public void start(BundleContext context) throws Exception { + logger.info("Starting " + context.getBundle()); + Dictionary<String, Object> props = new Hashtable<String, Object>(); + props.put("sca.service", "CalculatorComponent#service-name(Calculator)"); + props.put("calculator", "Calculator"); + props.put("service.exported.configs", new String[] {"sca"}); + props.put("sca.bindings", new String[] {"OSGI-INF/sca/calculator-service.bindings"}); + props.put("service.exported.interfaces", new String[] {"*"}); + logger.info("Registering " + CalculatorService.class.getName()); + CalculatorService calculator = new CalculatorServiceImpl(context); + context.registerService(CalculatorService.class.getName(), calculator, props); + + getBundle(context, AddService.class); + + } + + public void stop(BundleContext context) throws Exception { + logger.info("Stopping " + context.getBundle()); + // Registered services will be automatically unregistered + } + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/impl/CalculatorServiceDSImpl.java b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/impl/CalculatorServiceDSImpl.java new file mode 100644 index 0000000000..5f9db16ca9 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/impl/CalculatorServiceDSImpl.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package calculator.dosgi.impl; + +import org.osgi.service.component.ComponentContext; + +import calculator.dosgi.CalculatorService; +import calculator.dosgi.operations.AddService; +import calculator.dosgi.operations.DivideService; +import calculator.dosgi.operations.MultiplyService; +import calculator.dosgi.operations.SubtractService; + +/** + * An implementation of the Calculator service. + */ +public class CalculatorServiceDSImpl implements CalculatorService { + private AddService addService; + private SubtractService subtractService; + private MultiplyService multiplyService; + private DivideService divideService; + + public CalculatorServiceDSImpl() { + super(); + System.out.println("CalculatorServiceDSImpl()"); + } + + protected void activate(ComponentContext context) { + System.out.println("Activating " + context); + } + + protected void deactivate(ComponentContext context) { + System.out.println("Deactivating " + context); + } + + /* + * The following setters can be used for DS injection + */ + public void setAddService(AddService addService) { + System.out.println("setAddService()"); + this.addService = addService; + } + + public void setSubtractService(SubtractService subtractService) { + this.subtractService = subtractService; + } + + public void setDivideService(DivideService divideService) { + this.divideService = divideService; + } + + public void setMultiplyService(MultiplyService multiplyService) { + this.multiplyService = multiplyService; + } + + /* + * The following setters can be used for DS injection + */ + public void unsetAddService(AddService addService) { + System.out.println("unsetAddService()"); + this.addService = null; + } + + public void unsetSubtractService(SubtractService subtractService) { + this.subtractService = null; + } + + public void unsetDivideService(DivideService divideService) { + this.divideService = null; + } + + public void unsetMultiplyService(MultiplyService multiplyService) { + this.multiplyService = null; + } + private <T> T getService(Class<T> cls) { + for (Object s : new Object[] {addService, subtractService, multiplyService, divideService}) { + if (cls.isInstance(s)) { + return cls.cast(s); + } + } + throw new IllegalStateException(cls.getSimpleName() + " is not available"); + } + + public double add(double n1, double n2) { + return getService(AddService.class).add(n1, n2); + } + + public double subtract(double n1, double n2) { + return getService(SubtractService.class).subtract(n1, n2); + } + + public double multiply(double n1, double n2) { + return getService(MultiplyService.class).multiply(n1, n2); + } + + public double divide(double n1, double n2) { + return getService(DivideService.class).divide(n1, n2); + } +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/impl/CalculatorServiceImpl.java b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/impl/CalculatorServiceImpl.java new file mode 100644 index 0000000000..a9ea37585a --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/impl/CalculatorServiceImpl.java @@ -0,0 +1,105 @@ +/* + * 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 calculator.dosgi.impl; + +import static org.osgi.framework.Constants.OBJECTCLASS; + +import org.osgi.framework.BundleContext; +import org.osgi.framework.Filter; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.util.tracker.ServiceTracker; + +import calculator.dosgi.CalculatorService; +import calculator.dosgi.operations.AddService; +import calculator.dosgi.operations.DivideService; +import calculator.dosgi.operations.MultiplyService; +import calculator.dosgi.operations.SubtractService; + +/** + * An implementation of the Calculator service. + */ +public class CalculatorServiceImpl implements CalculatorService { + private ServiceTracker remoteServices; + private ServiceTracker localServices; + + public CalculatorServiceImpl() { + super(); + } + + public CalculatorServiceImpl(BundleContext context) { + super(); + Filter remoteFilter = null, localFilter = null; + try { + remoteFilter = + context.createFilter("(&(" + OBJECTCLASS + "=calculator.dosgi.operations.*) (service.imported=*))"); + localFilter = + context.createFilter("(&(" + OBJECTCLASS + "=calculator.dosgi.operations.*) (!(service.imported=*)))"); + } catch (InvalidSyntaxException e) { + e.printStackTrace(); + } + this.remoteServices = new ServiceTracker(context, remoteFilter, null); + remoteServices.open(); + this.localServices = new ServiceTracker(context, localFilter, null); + localServices.open(); + } + + private <T> T getService(Class<T> cls) { + try { + // Wait for 10 seconds until the remote services are imported + remoteServices.waitForService(10000); + } catch (InterruptedException e) { + throw new IllegalStateException(cls.getSimpleName() + " is not available"); + } + Object[] remoteObjects = remoteServices.getServices(); + if (remoteObjects != null) { + for (Object s : remoteObjects) { + if (cls.isInstance(s)) { + System.out.println("Remote service: " + s); + return cls.cast(s); + } + } + } + Object[] localObjects = localServices.getServices(); + if (localObjects != null) { + for (Object s : localObjects) { + if (cls.isInstance(s)) { + System.out.println("Local service: " + s); + return cls.cast(s); + } + } + } + throw new IllegalStateException(cls.getSimpleName() + " is not available"); + } + + public double add(double n1, double n2) { + return getService(AddService.class).add(n1, n2); + } + + public double subtract(double n1, double n2) { + return getService(SubtractService.class).subtract(n1, n2); + } + + public double multiply(double n1, double n2) { + return getService(MultiplyService.class).multiply(n1, n2); + } + + public double divide(double n1, double n2) { + return getService(DivideService.class).divide(n1, n2); + } +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/operations/AddService.java b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/operations/AddService.java new file mode 100644 index 0000000000..971500782f --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/operations/AddService.java @@ -0,0 +1,31 @@ +/* + * 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 calculator.dosgi.operations; + +import org.oasisopen.sca.annotation.Remotable; + +/** + * The interface for the add service + */ +@Remotable +public interface AddService { + + double add(double n1, double n2); + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/operations/DivideService.java b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/operations/DivideService.java new file mode 100644 index 0000000000..49b8a1c0bf --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/operations/DivideService.java @@ -0,0 +1,31 @@ +/* + * 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 calculator.dosgi.operations; + +import org.oasisopen.sca.annotation.Remotable; + +/** + * The interface for the divide service + */ +@Remotable +public interface DivideService { + + double divide(double n1, double n2); + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/operations/MultiplyService.java b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/operations/MultiplyService.java new file mode 100644 index 0000000000..f4e59d12ea --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/operations/MultiplyService.java @@ -0,0 +1,31 @@ +/* + * 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 calculator.dosgi.operations; + +import org.oasisopen.sca.annotation.Remotable; + +/** + * The interface for the multiply service + */ +@Remotable +public interface MultiplyService { + + double multiply(double n1, double n2); + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/operations/SubtractService.java b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/operations/SubtractService.java new file mode 100644 index 0000000000..bfb9b820f7 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/dosgi/operations/SubtractService.java @@ -0,0 +1,31 @@ +/* + * 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 calculator.dosgi.operations; + +import org.oasisopen.sca.annotation.Remotable; + +/** + * The interface for the subtract service + */ +@Remotable +public interface SubtractService { + + double subtract(double n1, double n2); + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/rmi/OperationsRMIServer.java b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/rmi/OperationsRMIServer.java new file mode 100644 index 0000000000..a4fc52694e --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/rmi/OperationsRMIServer.java @@ -0,0 +1,93 @@ +/* + * 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 calculator.rmi; + +import java.io.Serializable; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.rmi.server.UnicastRemoteObject; + +/** + * + */ +public class OperationsRMIServer implements OperationsRemote, Serializable { + + private static final long serialVersionUID = 6081008315263103012L; + private transient Registry registry; + + public OperationsRMIServer() throws RemoteException { + super(); + } + + public double add(double n1, double n2) { + return n1 + n2; + } + + public double subtract(double n1, double n2) { + return n1 - n2; + } + + public double divide(double n1, double n2) { + return n1 / n2; + } + + public double multiply(double n1, double n2) { + return n1 * n2; + } + + public void start() throws RemoteException { + Thread thread = new Thread() { + public void run() { + try { + System.out.println("Starting the RMI server for calculator operations..."); + Remote stub = UnicastRemoteObject.exportObject(OperationsRMIServer.this); + registry = LocateRegistry.createRegistry(8085); + registry.bind("AddService", stub); + registry.bind("SubtractService", stub); + registry.bind("MultiplyService", stub); + registry.bind("DivideService", stub); + System.out.println("RMI server for calculator operations is now started."); + } catch (Exception e) { + e.printStackTrace(); + } + } + }; + thread.start(); + } + + public void stop() { + if (registry != null) { + try { + registry.unbind("AddService"); + registry.unbind("SubtractService"); + registry.unbind("MultiplyService"); + registry.unbind("DivideService"); + UnicastRemoteObject.unexportObject(this, false); + UnicastRemoteObject.unexportObject(registry, false); + registry = null; + } catch (Exception e) { + e.printStackTrace(); + } + } + } + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/rmi/OperationsRMIServer_Stub.java b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/rmi/OperationsRMIServer_Stub.java new file mode 100644 index 0000000000..a813dfb6f3 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/rmi/OperationsRMIServer_Stub.java @@ -0,0 +1,132 @@ +/* + * 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. + */ + +// Stub class generated by rmic, do not edit. +// Contents subject to change without notice. +package calculator.rmi; + +public final class OperationsRMIServer_Stub extends java.rmi.server.RemoteStub implements calculator.rmi.OperationsRemote { + private static final long serialVersionUID = 2; + + private static java.lang.reflect.Method $method_add_0; + private static java.lang.reflect.Method $method_divide_1; + private static java.lang.reflect.Method $method_multiply_2; + private static java.lang.reflect.Method $method_subtract_3; + + static { + try { + $method_add_0 = + calculator.rmi.OperationsRemote.class.getMethod("add", new java.lang.Class[] {double.class, double.class}); + $method_divide_1 = + calculator.rmi.OperationsRemote.class.getMethod("divide", + new java.lang.Class[] {double.class, double.class}); + $method_multiply_2 = + calculator.rmi.OperationsRemote.class.getMethod("multiply", new java.lang.Class[] {double.class, + double.class}); + $method_subtract_3 = + calculator.rmi.OperationsRemote.class.getMethod("subtract", new java.lang.Class[] {double.class, + double.class}); + } catch (java.lang.NoSuchMethodException e) { + throw new java.lang.NoSuchMethodError("stub class initialization failed"); + } + } + + // constructors + public OperationsRMIServer_Stub(java.rmi.server.RemoteRef ref) { + super(ref); + } + + // methods from remote interfaces + + // implementation of add(double, double) + public double add(double $param_double_1, double $param_double_2) throws java.rmi.RemoteException { + try { + Object $result = + ref.invoke(this, + $method_add_0, + new java.lang.Object[] {new java.lang.Double($param_double_1), + new java.lang.Double($param_double_2)}, + 864055858262779977L); + return ((java.lang.Double)$result).doubleValue(); + } catch (java.lang.RuntimeException e) { + throw e; + } catch (java.rmi.RemoteException e) { + throw e; + } catch (java.lang.Exception e) { + throw new java.rmi.UnexpectedException("undeclared checked exception", e); + } + } + + // implementation of divide(double, double) + public double divide(double $param_double_1, double $param_double_2) throws java.rmi.RemoteException { + try { + Object $result = + ref.invoke(this, + $method_divide_1, + new java.lang.Object[] {new java.lang.Double($param_double_1), + new java.lang.Double($param_double_2)}, + 8097593626497421928L); + return ((java.lang.Double)$result).doubleValue(); + } catch (java.lang.RuntimeException e) { + throw e; + } catch (java.rmi.RemoteException e) { + throw e; + } catch (java.lang.Exception e) { + throw new java.rmi.UnexpectedException("undeclared checked exception", e); + } + } + + // implementation of multiply(double, double) + public double multiply(double $param_double_1, double $param_double_2) throws java.rmi.RemoteException { + try { + Object $result = + ref.invoke(this, + $method_multiply_2, + new java.lang.Object[] {new java.lang.Double($param_double_1), + new java.lang.Double($param_double_2)}, + -346155016949350695L); + return ((java.lang.Double)$result).doubleValue(); + } catch (java.lang.RuntimeException e) { + throw e; + } catch (java.rmi.RemoteException e) { + throw e; + } catch (java.lang.Exception e) { + throw new java.rmi.UnexpectedException("undeclared checked exception", e); + } + } + + // implementation of subtract(double, double) + public double subtract(double $param_double_1, double $param_double_2) throws java.rmi.RemoteException { + try { + Object $result = + ref.invoke(this, + $method_subtract_3, + new java.lang.Object[] {new java.lang.Double($param_double_1), + new java.lang.Double($param_double_2)}, + -610707357620578750L); + return ((java.lang.Double)$result).doubleValue(); + } catch (java.lang.RuntimeException e) { + throw e; + } catch (java.rmi.RemoteException e) { + throw e; + } catch (java.lang.Exception e) { + throw new java.rmi.UnexpectedException("undeclared checked exception", e); + } + } +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/rmi/OperationsRemote.java b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/rmi/OperationsRemote.java new file mode 100644 index 0000000000..955e386ad8 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/main/java/calculator/rmi/OperationsRemote.java @@ -0,0 +1,37 @@ +/* + * 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 calculator.rmi; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + * RMI Remote interface + */ +public interface OperationsRemote extends Remote { + double add(double n1, double n2) throws RemoteException; + + double subtract(double n1, double n2) throws RemoteException; + + double multiply(double n1, double n2) throws RemoteException; + + double divide(double n1, double n2) throws RemoteException; + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/test/java/calculator/dosgi/test/CalculatorNode.java b/java/sca/samples/dosgi-dynamic-calculator/src/test/java/calculator/dosgi/test/CalculatorNode.java new file mode 100644 index 0000000000..565a314d85 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/test/java/calculator/dosgi/test/CalculatorNode.java @@ -0,0 +1,43 @@ +/* + * 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 calculator.dosgi.test; + +import org.apache.tuscany.sca.node.equinox.launcher.NodeLauncher; + +/** + * + */ +public class CalculatorNode { + + /** + * @param args + */ + public static void main(String[] args) { + if (args.length == 0) { + args = new String[] {"-bundles"}; + } + try { + NodeLauncher.main(args); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/test/java/calculator/dosgi/test/CalculatorOSGiNodeTestCase.java b/java/sca/samples/dosgi-dynamic-calculator/src/test/java/calculator/dosgi/test/CalculatorOSGiNodeTestCase.java new file mode 100644 index 0000000000..4015c7bbc2 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/test/java/calculator/dosgi/test/CalculatorOSGiNodeTestCase.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 calculator.dosgi.test; + +import static calculator.dosgi.test.OSGiTestUtils.bundleStatus; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; + +import org.apache.tuscany.sca.node.equinox.launcher.EquinoxHost; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceReference; + +import calculator.dosgi.CalculatorService; +import calculator.rmi.OperationsRMIServer; + +/** + * + */ +public class CalculatorOSGiNodeTestCase { + private static EquinoxHost host; + private static BundleContext context; + private static Bundle calculatorBundle; + private static OperationsRMIServer rmiServer; + + /** + * @throws java.lang.Exception + */ + @BeforeClass + public static void setUpBeforeClass() throws Exception { + try { + rmiServer = new OperationsRMIServer(); + rmiServer.start(); + + host = new EquinoxHost(); + context = host.start(); + + for (Bundle b : context.getBundles()) { + System.out.println(b); + if (b.getSymbolicName().equals("org.eclipse.equinox.ds") || b.getSymbolicName() + .startsWith("org.apache.tuscany.sca.")) { + try { + if (b.getHeaders().get(Constants.FRAGMENT_HOST) == null) { + // Start the non-fragment bundle + b.start(); + } + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println(bundleStatus(b, false)); + } + if ("calculator.dosgi.dynamic".equals(b.getSymbolicName())) { + calculatorBundle = b; + } + } + + if (calculatorBundle != null) { + calculatorBundle.start(); + System.out.println(bundleStatus(calculatorBundle, false)); + } + + } catch (Exception e) { + e.printStackTrace(); + throw e; + } + } + + @Test + public void testOSGi() { + ServiceReference ref = + calculatorBundle.getBundleContext().getServiceReference(CalculatorService.class.getName()); + Assert.assertNotNull(ref); + Object service = context.getService(ref); + Assert.assertNotNull(service); + CalculatorService calculator = OSGiTestUtils.cast(service, CalculatorService.class); + System.out.println("2.0 + 1.0 = " + calculator.add(2.0, 1.0)); + System.out.println("2.0 - 1.0 = " + calculator.subtract(2.0, 1.0)); + System.out.println("2.0 * 1.0 = " + calculator.multiply(2.0, 1.0)); + System.out.println("2.0 / 1.0 = " + calculator.divide(2.0, 1.0)); + } + + @Test + /** + * Test the Web service exposed by the Calculator + */ + public void testWS() throws Exception { + URL url = new URL("http://localhost:8086/CalculatorService?wsdl"); + InputStream is = url.openStream(); + Reader reader = new InputStreamReader(is); + char[] content = new char[10240]; // 10k + int len = 0; + while (true) { + int size = reader.read(content, len, content.length - len); + if (size < 0) { + break; + } + len += size; + } + Assert.assertTrue(len > 0); + String str = new String(content, 0, len); + System.out.println(str); + Assert.assertTrue(str.indexOf("<wsdl:definitions") != -1); + } + + /** + * @throws java.lang.Exception + */ + @AfterClass + public static void tearDownAfterClass() throws Exception { + if (host != null) { + host.stop(); + rmiServer.stop(); + host = null; + rmiServer = null; + context = null; + } + } + +} diff --git a/java/sca/samples/dosgi-dynamic-calculator/src/test/java/calculator/dosgi/test/OSGiTestUtils.java b/java/sca/samples/dosgi-dynamic-calculator/src/test/java/calculator/dosgi/test/OSGiTestUtils.java new file mode 100644 index 0000000000..99e0da9f97 --- /dev/null +++ b/java/sca/samples/dosgi-dynamic-calculator/src/test/java/calculator/dosgi/test/OSGiTestUtils.java @@ -0,0 +1,105 @@ +/* + * 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 calculator.dosgi.test; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import org.osgi.framework.Bundle; + +/** + * + * Utility class to create OSGi bundles + * + * @version $Rev$ $Date$ + */ +public class OSGiTestUtils { + private static class InvocationHandlerImpl implements InvocationHandler { + private Object instance; + + public InvocationHandlerImpl(Object instance) { + super(); + this.instance = instance; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Method m = instance.getClass().getMethod(method.getName(), method.getParameterTypes()); + return m.invoke(instance, args); + } + + } + + /** + * Returns a string representation of the given bundle. + * + * @param b + * @param verbose + * @return + */ + public static String bundleStatus(Bundle bundle, boolean verbose) { + StringBuffer sb = new StringBuffer(); + sb.append(bundle.getBundleId()).append(" ").append(bundle.getSymbolicName()); + int s = bundle.getState(); + if ((s & Bundle.UNINSTALLED) != 0) { + sb.append(" UNINSTALLED"); + } + if ((s & Bundle.INSTALLED) != 0) { + sb.append(" INSTALLED"); + } + if ((s & Bundle.RESOLVED) != 0) { + sb.append(" RESOLVED"); + } + if ((s & Bundle.STARTING) != 0) { + sb.append(" STARTING"); + } + if ((s & Bundle.STOPPING) != 0) { + sb.append(" STOPPING"); + } + if ((s & Bundle.ACTIVE) != 0) { + sb.append(" ACTIVE"); + } + + if (verbose) { + sb.append(" ").append(bundle.getLocation()); + sb.append(" ").append(bundle.getHeaders()); + } + return sb.toString(); + } + + /** + * A utility to cast the object to the given interface. If the class for the object + * is loaded by a different classloader, a proxy will be created. + * + * @param <T> + * @param obj + * @param cls + * @return + */ + public static <T> T cast(Object obj, Class<T> cls) { + if (cls.isInstance(obj)) { + return cls.cast(obj); + } else { + return cls.cast(Proxy.newProxyInstance(cls.getClassLoader(), + new Class<?>[] {cls}, + new InvocationHandlerImpl(obj))); + } + } +} diff --git a/java/sca/samples/pom.xml b/java/sca/samples/pom.xml index 55582f903a..88fa77c2be 100644 --- a/java/sca/samples/pom.xml +++ b/java/sca/samples/pom.xml @@ -51,6 +51,9 @@ <module>dosgi-calculator</module> <module>dosgi-calculator-operations</module> + + <module>dosgi-dynamic-calculator</module> + <module>dosgi-dynamic-calculator-operations</module> <module>helloworld-bpel</module> |