diff options
Diffstat (limited to 'sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl')
10 files changed, 1876 insertions, 0 deletions
diff --git a/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/AbstractOSGiServiceHandler.java b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/AbstractOSGiServiceHandler.java new file mode 100644 index 0000000000..a043e33c32 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/AbstractOSGiServiceHandler.java @@ -0,0 +1,90 @@ +/* + * 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.remoteserviceadmin.impl; + +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.discovery.impl.LocalDiscoveryService; +import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; + +/** + * Watching and exporting OSGi services + */ +public class AbstractOSGiServiceHandler implements LifeCycleListener { + protected ExtensionPointRegistry registry; + protected BundleContext context; + protected NodeFactoryImpl nodeFactory; + protected EndpointIntrospector introspector; + protected ServiceTracker discoveryTracker; + protected String domainRegistry; + + /** + * @param context + * @param clazz + * @param customizer + */ + protected AbstractOSGiServiceHandler(BundleContext context) { + this.context = context; + } + + 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; + } + + protected synchronized void init() { + if (nodeFactory == null) { + this.nodeFactory = (NodeFactoryImpl)NodeFactory.newInstance(); + this.nodeFactory.init(); + this.discoveryTracker = LocalDiscoveryService.getTracker(context); + discoveryTracker.open(); + this.introspector = new EndpointIntrospector(context, getExtensionPointRegistry(), discoveryTracker); + } + } + + public void start() { + init(); + } + + public void stop() { + if (nodeFactory != null) { + discoveryTracker.close(); + discoveryTracker = null; + introspector = null; + nodeFactory = null; + registry = null; + context = null; + } + } + + public void setDomainRegistry(String domainRegistry) { + this.domainRegistry = domainRegistry; + } + +} diff --git a/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointHelper.java b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointHelper.java new file mode 100644 index 0000000000..8ac949ccc5 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointHelper.java @@ -0,0 +1,82 @@ +/* + * 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.remoteserviceadmin.impl; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +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.remoteserviceadmin.EndpointDescription; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteConstants; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; + +/** + * Implementation of {@link EndpointDescription} + */ +public class EndpointHelper { + private final static String FRAMEWORK_UUID = "org.osgi.framework.uuid"; + private EndpointHelper() { + } + + public static EndpointDescription createEndpointDescription(BundleContext bundleContext, Endpoint endpoint) { + return new EndpointDescription(getProperties(bundleContext, endpoint)); + } + + private static List<String> getInterfaces(Endpoint endpoint) { + Interface intf = endpoint.getInterfaceContract().getInterface(); + JavaInterface javaInterface = (JavaInterface)intf; + return Collections.singletonList(javaInterface.getName()); + } + + private static Map<String, Object> getProperties(BundleContext bundleContext, Endpoint endpoint) { + Map<String, Object> props = new HashMap<String, Object>(); + + String uuid = getFrameworkUUID(bundleContext); + + props.put(RemoteConstants.SERVICE_REMOTE_FRAMEWORK_UUID, uuid); + props.put(RemoteConstants.SERVICE_REMOTE_URI, endpoint.getURI()); + props.put(RemoteConstants.SERVICE_REMOTE_ID, String.valueOf(System.currentTimeMillis())); + props.put(RemoteConstants.SERVICE_EXPORTED_CONFIGS, new String[] {"org.osgi.sca"}); + props.put(Endpoint.class.getName(), endpoint); + List<String> interfaces = getInterfaces(endpoint); + props.put(Constants.OBJECTCLASS, interfaces.toArray(new String[interfaces.size()])); + return props; + } + + public synchronized static String getFrameworkUUID(BundleContext bundleContext) { + String uuid = System.getProperty(FRAMEWORK_UUID); + if (uuid == null) { + uuid = UUID.randomUUID().toString(); + } + System.setProperty(FRAMEWORK_UUID, uuid); + return uuid; + } + + public static Endpoint getEndpoint(EndpointDescription endpointDescription) { + return (Endpoint)endpointDescription.getProperties().get(Endpoint.class.getName()); + } + +} diff --git a/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointIntrospector.java b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointIntrospector.java new file mode 100644 index 0000000000..c88632407c --- /dev/null +++ b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/EndpointIntrospector.java @@ -0,0 +1,494 @@ +/* + * 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.remoteserviceadmin.impl; + +import static org.apache.tuscany.sca.assembly.Base.SCA11_TUSCANY_NS; +import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SCA_BINDINGS; +import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTENTS; +import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTENTS_EXTRA; +import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTERFACES; +import static org.osgi.framework.Constants.OBJECTCLASS; +import static org.osgi.framework.Constants.SERVICE_ID; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +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.Set; +import java.util.UUID; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; + +import org.apache.tuscany.sca.assembly.AssemblyFactory; +import org.apache.tuscany.sca.assembly.Base; +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.assembly.Component; +import org.apache.tuscany.sca.assembly.ComponentReference; +import org.apache.tuscany.sca.assembly.ComponentService; +import org.apache.tuscany.sca.assembly.Composite; +import org.apache.tuscany.sca.assembly.Endpoint; +import org.apache.tuscany.sca.assembly.Reference; +import org.apache.tuscany.sca.assembly.Service; +import org.apache.tuscany.sca.contribution.Contribution; +import org.apache.tuscany.sca.contribution.ContributionFactory; +import org.apache.tuscany.sca.contribution.processor.ContributionReadException; +import org.apache.tuscany.sca.contribution.resolver.ExtensibleModelResolver; +import org.apache.tuscany.sca.contribution.resolver.ModelResolver; +import org.apache.tuscany.sca.contribution.resolver.ModelResolverExtensionPoint; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.core.FactoryExtensionPoint; +import org.apache.tuscany.sca.core.UtilityExtensionPoint; +import org.apache.tuscany.sca.deployment.Deployer; +import org.apache.tuscany.sca.implementation.osgi.OSGiImplementation; +import org.apache.tuscany.sca.implementation.osgi.OSGiImplementationFactory; +import org.apache.tuscany.sca.implementation.osgi.OSGiProperty; +import org.apache.tuscany.sca.implementation.osgi.SCAConfig; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; +import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceContract; +import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceFactory; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription; +import org.apache.tuscany.sca.osgi.service.discovery.impl.LocalDiscoveryService; +import org.apache.tuscany.sca.osgi.service.discovery.impl.LocalDiscoveryService.ExtenderConfiguration; +import org.apache.tuscany.sca.policy.Intent; +import org.apache.tuscany.sca.policy.PolicyFactory; +import org.oasisopen.sca.ServiceRuntimeException; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTracker; + +/** + * Introspect an OSGi Service to create an SCA composite that contains a single component with + * implementation.osgi + */ +public class EndpointIntrospector { + private BundleContext context; + private AssemblyFactory assemblyFactory; + private ContributionFactory contributionFactory; + private OSGiImplementationFactory implementationFactory; + private PolicyFactory policyFactory; + private ExtensionPointRegistry registry; + private FactoryExtensionPoint factories; + private ModelResolverExtensionPoint modelResolvers; + private JavaInterfaceFactory javaInterfaceFactory; + private Deployer deployer; + private ServiceTracker discoveryTracker; + + /** + * @param intentName + * @return + */ + private static QName getQName(String intentName) { + QName qname; + if (intentName.startsWith("{")) { + int i = intentName.indexOf('}'); + if (i != -1) { + qname = new QName(intentName.substring(1, i), intentName.substring(i + 1)); + } else { + throw new IllegalArgumentException("Invalid intent: " + intentName); + } + } else { + // Default to SCA namespace + qname = new QName(Base.SCA11_NS, intentName); + } + return qname; + } + + /** + * @param context TODO + * @param registry + */ + public EndpointIntrospector(BundleContext context, ExtensionPointRegistry registry, ServiceTracker discoveryTracker) { + super(); + this.context = context; + this.discoveryTracker = discoveryTracker; + this.registry = registry; + this.factories = registry.getExtensionPoint(FactoryExtensionPoint.class); + this.modelResolvers = registry.getExtensionPoint(ModelResolverExtensionPoint.class); + this.assemblyFactory = factories.getFactory(AssemblyFactory.class); + this.contributionFactory = factories.getFactory(ContributionFactory.class); + this.policyFactory = factories.getFactory(PolicyFactory.class); + this.implementationFactory = factories.getFactory(OSGiImplementationFactory.class); + this.javaInterfaceFactory = factories.getFactory(JavaInterfaceFactory.class); + this.deployer = registry.getExtensionPoint(UtilityExtensionPoint.class).getUtility(Deployer.class); + } + + private Intent getIntent(String intent) { + QName name = getQName(intent); + Intent i = policyFactory.createIntent(); + i.setName(name); + return i; + } + + private List<Intent> getIntents(String[] intents) { + if (intents == null || intents.length == 0) { + return Collections.emptyList(); + } + List<Intent> intentList = new ArrayList<Intent>(); + for (String i : intents) { + Intent intent = getIntent(i); + if (intent != null) { + intentList.add(intent); + } + } + return intentList; + } + + /** + * Any property in the map overrides the service reference properties, regardless of + * case. That is, if the map contains a key then it will override any case variant + * of this key in the Service Reference.<p> + * If the map contains the objectClass or service. id property key in any case + * variant, then these properties must not override the Service References value. This + * implies that the map can provide the service.exported. interfaces, property allowing + * the Topology Manager to export any registered service, also services not specifically + * marked to be exported. + * @param reference + * @param props + * @return + */ + private Map<String, Object> getProperties(ServiceReference reference, Map<String, Object> props) { + String[] names = reference.getPropertyKeys(); + Map<String, Object> properties = new HashMap<String, Object>(); + if (names != null) { + for (String name : names) { + properties.put(name, reference.getProperty(name)); + } + } + if (props != null) { + // Create a map of names (key = lowcase name, value = name) + Map<String, String> nameMap = new HashMap<String, String>(); + if (names != null) { + for (String name : names) { + nameMap.put(name.toLowerCase(), name); + } + } + for (Map.Entry<String, Object> p : props.entrySet()) { + if (Constants.OBJECTCLASS.equalsIgnoreCase(p.getKey())) { + throw new IllegalArgumentException(Constants.OBJECTCLASS + " property cannot be overridden."); + } else if (Constants.SERVICE_ID.equalsIgnoreCase(p.getKey())) { + throw new IllegalArgumentException(Constants.SERVICE_ID + " property cannot be overridden."); + } + String key = nameMap.get(p.getKey().toLowerCase()); + if (key != null) { + properties.put(key, p.getValue()); + } else { + properties.put(p.getKey(), p.getValue()); + } + } + } + return properties; + } + + /** + * Parse the Stringp[] to support values that are separated by comma + * @param interfaces + * @return + */ + private String[] parse(String[] interfaces) { + if (interfaces == null) { + return null; + } + List<String> names = new ArrayList<String>(); + for (String i : interfaces) { + String[] parts = i.split(","); + for (String p : parts) { + names.add(p.trim()); + } + } + return names.toArray(new String[names.size()]); + } + + /** + * Introspect a local OSGi Service represented by the ServiceReference to create + * an SCA service with the required intents and bindings + * @param reference The service reference for a local OSGi service + * @param props Addiontal properties + * @return An SCA contribution with a deployable composite for the SCA service + * @throws Exception + */ + public Contribution introspect(ServiceReference reference, Map<String, Object> props) throws Exception { + Map<String, Object> properties = getProperties(reference, props); + + OSGiProperty serviceID = implementationFactory.createOSGiProperty(); + serviceID.setName(SERVICE_ID); + // The service.id is Long + serviceID.setValue(String.valueOf(reference.getProperty(SERVICE_ID))); + + String id = "osgi.service." + UUID.randomUUID(); + Composite composite = assemblyFactory.createComposite(); + composite.setName(new QName(SCA11_TUSCANY_NS, id)); + + Component component = assemblyFactory.createComponent(); + component.setName(id); + component.setAutowire(Boolean.TRUE); + + composite.getComponents().add(component); + + Bundle bundle = reference.getBundle(); + OSGiImplementation implementation = implementationFactory.createOSGiImplementation(); + + implementation.setBundle(bundle); + component.setImplementation(implementation); + implementation.setUnresolved(false); + + String[] remoteInterfaces = getStrings(reference.getProperty(SERVICE_EXPORTED_INTERFACES)); + if (remoteInterfaces == null || remoteInterfaces.length > 0 && "*".equals(remoteInterfaces[0])) { + remoteInterfaces = getStrings(reference.getProperty(OBJECTCLASS)); + } else { + remoteInterfaces = parse(remoteInterfaces); + String[] objectClasses = getStrings(reference.getProperty(OBJECTCLASS)); + Set<String> objectClassSet = new HashSet<String>(Arrays.asList(objectClasses)); + if (!objectClassSet.containsAll(Arrays.asList(remoteInterfaces))) { + throw new IllegalArgumentException( + "The exported interfaces are not a subset of the types" + " listed in the objectClass service property from the Service Reference"); + } + } + for (String intf : remoteInterfaces) { + Service service = assemblyFactory.createService(); + JavaInterfaceContract interfaceContract = javaInterfaceFactory.createJavaInterfaceContract(); + Class<?> interfaceClass = bundle.loadClass(intf); + JavaInterface javaInterface = javaInterfaceFactory.createJavaInterface(interfaceClass); + interfaceContract.setInterface(javaInterface); + if (javaInterface.getCallbackClass() != null) { + interfaceContract.setCallbackInterface(javaInterfaceFactory.createJavaInterface(javaInterface + .getCallbackClass())); + } + + service.setName(interfaceClass.getSimpleName()); + service.setInterfaceContract(interfaceContract); + + service.getExtensions().add(serviceID); + + implementation.getServices().add(service); + + ComponentService componentService = assemblyFactory.createComponentService(); + componentService.setName(service.getName()); + component.getServices().add(componentService); + componentService.setService(service); + } + + String[] requiredIntents = getStrings(properties.get(SERVICE_EXPORTED_INTENTS)); + List<Intent> intents = getIntents(requiredIntents); + String[] requiredIntentsExtra = getStrings(properties.get(SERVICE_EXPORTED_INTENTS_EXTRA)); + List<Intent> extraIntents = getIntents(requiredIntentsExtra); + + String[] bindingNames = getStrings(properties.get(SCA_BINDINGS)); + Collection<Binding> bindings = loadBindings(bindingNames); + + for (ComponentService componentService : component.getServices()) { + componentService.getRequiredIntents().addAll(intents); + componentService.getRequiredIntents().addAll(extraIntents); + componentService.getBindings().addAll(bindings); + } + + // FIXME: Should we scan the owning bundle to create the SCA contribution? + Contribution contribution = contributionFactory.createContribution(); + contribution.setURI("urn:" + id); + contribution.setLocation(bundle.getEntry("/").toString()); + contribution.getDeployables().add(composite); + ModelResolver modelResolver = new ExtensibleModelResolver(contribution, modelResolvers, factories); + contribution.setModelResolver(modelResolver); + contribution.setUnresolved(true); + return contribution; + } + + public Contribution introspect(Bundle bundle, EndpointDescription endpoint) throws Exception { + Endpoint ep = (Endpoint)endpoint.getProperties().get(Endpoint.class.getName()); + if (ep != null) { + return introspect(bundle, ep); + } + Map<String, Object> properties = endpoint.getProperties(); + List<String> remoteInterfaces = endpoint.getInterfaces(); + + String id = "osgi.reference." + UUID.randomUUID(); + Composite composite = assemblyFactory.createComposite(); + composite.setName(new QName(Base.SCA11_TUSCANY_NS, id)); + + Component component = assemblyFactory.createComponent(); + component.setName(id); + // component.setAutowire(Boolean.TRUE); + + composite.getComponents().add(component); + + OSGiImplementation implementation = implementationFactory.createOSGiImplementation(); + + implementation.setBundle(bundle); + component.setImplementation(implementation); + implementation.setUnresolved(false); + + int count = 0; + for (String intf : remoteInterfaces) { + Reference reference = assemblyFactory.createReference(); + JavaInterfaceContract interfaceContract = javaInterfaceFactory.createJavaInterfaceContract(); + Class<?> interfaceClass = bundle.loadClass(intf); + JavaInterface javaInterface = javaInterfaceFactory.createJavaInterface(interfaceClass); + interfaceContract.setInterface(javaInterface); + if (javaInterface.getCallbackClass() != null) { + interfaceContract.setCallbackInterface(javaInterfaceFactory.createJavaInterface(javaInterface + .getCallbackClass())); + } + + reference.setName("ref" + (count++)); + reference.setInterfaceContract(interfaceContract); + + implementation.getReferences().add(reference); + + ComponentReference componentReference = assemblyFactory.createComponentReference(); + componentReference.setName(reference.getName()); + component.getReferences().add(componentReference); + componentReference.setReference(reference); + componentReference.setWiredByImpl(true); + } + + String[] requiredIntents = getStrings(properties.get(SERVICE_EXPORTED_INTENTS)); + List<Intent> intents = getIntents(requiredIntents); + + String[] bindingNames = getStrings(properties.get(SCA_BINDINGS)); + Collection<Binding> bindings = loadBindings(bindingNames); + + for (ComponentReference componentReference : component.getReferences()) { + componentReference.getRequiredIntents().addAll(intents); + componentReference.getBindings().addAll(bindings); + } + // FIXME: Should we scan the owning bundle to create the SCA contribution? + Contribution contribution = contributionFactory.createContribution(); + contribution.setURI("urn:" + id); + contribution.setLocation(bundle.getEntry("/").toString()); + contribution.getDeployables().add(composite); + ModelResolver modelResolver = new ExtensibleModelResolver(contribution, modelResolvers, factories); + contribution.setModelResolver(modelResolver); + contribution.setUnresolved(true); + return contribution; + } + + public Contribution introspect(Bundle bundle, Endpoint endpoint) throws Exception { + String id = "osgi.reference." + UUID.randomUUID(); + Composite composite = assemblyFactory.createComposite(); + composite.setName(new QName(Base.SCA11_TUSCANY_NS, id)); + + Component component = assemblyFactory.createComponent(); + component.setName(id); + // component.setAutowire(Boolean.TRUE); + + composite.getComponents().add(component); + + OSGiImplementation implementation = implementationFactory.createOSGiImplementation(); + + implementation.setBundle(bundle); + component.setImplementation(implementation); + implementation.setUnresolved(false); + + Reference reference = assemblyFactory.createReference(); + Service service = endpoint.getService().getService(); + reference.setInterfaceContract(service.getInterfaceContract()); + reference.setName("ref"); + + reference.getBindings().add(endpoint.getBinding()); + + /* + reference.getRequiredIntents().addAll(service.getRequiredIntents()); + reference.getPolicySets().addAll(service.getPolicySets()); + */ + + implementation.getReferences().add(reference); + + ComponentReference componentReference = assemblyFactory.createComponentReference(); + component.getReferences().add(componentReference); + componentReference.setReference(reference); + componentReference.setName(reference.getName()); + componentReference.setWiredByImpl(true); + + // FIXME: Should we scan the owning bundle to create the SCA contribution? + Contribution contribution = contributionFactory.createContribution(); + contribution.setURI("urn:" + id); + contribution.setLocation(bundle.getEntry("/").toString()); + contribution.getDeployables().add(composite); + ModelResolver modelResolver = new ExtensibleModelResolver(contribution, modelResolvers, factories); + contribution.setModelResolver(modelResolver); + contribution.setUnresolved(true); + return contribution; + } + + private Collection<Binding> loadBindings(String[] qnames) throws IOException, + ContributionReadException, XMLStreamException { + if (qnames == null || qnames.length == 0) { + return Collections.emptyList(); + } + QName[] bindingNames = new QName[qnames.length]; + int index = 0; + for (String name : qnames) { + bindingNames[index++] = getQName(name); + } + + LocalDiscoveryService discoveryService = (LocalDiscoveryService)discoveryTracker.getService(); + + Map<QName, Binding> bindingMap = new HashMap<QName, Binding>(); + if (discoveryService != null) { + for (ExtenderConfiguration config : discoveryService.getConfigurations()) { + for (SCAConfig sc : config.getSCAConfigs()) { + for (QName bindingName : bindingNames) { + if (sc.getTargetNamespace().equals(bindingName.getNamespaceURI())) { + for (Binding binding : sc.getBindings()) { + if (bindingName.getLocalPart().equals(binding.getName())) { + bindingMap.put(bindingName, binding); + break; + } + } + } + } + } + } + } + for (QName bindingName : bindingNames) { + if (!bindingMap.containsKey(bindingName)) { + throw new ServiceRuntimeException("Binding cannot be resolved: " + bindingName); + } + } + return bindingMap.values(); + } + + /** + * In OSGi, the value of String+ can be a single String, String[] or Collection<String> + * @param value + * @return + */ + private String[] getStrings(Object value) { + if (value == null) { + return null; + } + if (value instanceof String) { + return new String[] {(String)value}; + } else if (value instanceof Collection) { + Collection<String> collection = (Collection)value; + return collection.toArray(new String[collection.size()]); + } + return (String[])value; + + } + +} diff --git a/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportRegistrationImpl.java b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ExportRegistrationImpl.java new file mode 100644 index 0000000000..58d72f389f --- /dev/null +++ b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/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.remoteserviceadmin.impl; + +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.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.remoteserviceadmin.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/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportRegistrationImpl.java b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/ImportRegistrationImpl.java new file mode 100644 index 0000000000..d085bd282d --- /dev/null +++ b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/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.remoteserviceadmin.impl; + +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.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.remoteserviceadmin.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/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiHelper.java b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiHelper.java new file mode 100644 index 0000000000..d5a2b7aa95 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiHelper.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl; + +import java.net.URL; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; + +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; + +/** + * + */ +public class OSGiHelper { + private OSGiHelper() { + } + + /** + * 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); + } + } + + /** + * Get a collection of resources that are configured by the given header + * @param bundle The bundle + * @param header + * @param defaultValue + * @return + */ + public static Collection<URL> getConfiguration(Bundle bundle, String header, String defaultValue) { + String value = (String)bundle.getHeaders().get(header); + if (value == null) { + return Collections.emptyList(); + } + String paths[] = value.trim().split("( |\t|\n|\r|\f|,)+"); + if (paths.length == 0) { + if (defaultValue != null) { + paths = new String[] {defaultValue}; + } else { + paths = new String[0]; + } + } + Collection<URL> files = new HashSet<URL>(); + for (String path : paths) { + if (path.endsWith("/")) { + path = path + "*.xml"; + } + if (!path.startsWith("/")) { + path = "/" + path; + } + int lastIndex = path.lastIndexOf('/'); + String root = path.substring(0, lastIndex); + if ("".equals(root)) { + root = "/"; + } + String pattern = path.substring(lastIndex + 1); + Enumeration<URL> entries = bundle.findEntries(root, pattern, false); + if (entries != null) { + while (entries.hasMoreElements()) { + files.add(entries.nextElement()); + } + } + } + return files; + } + +} diff --git a/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceExporter.java b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceExporter.java new file mode 100644 index 0000000000..02603417cc --- /dev/null +++ b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceExporter.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl; + +import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.EndpointHelper.createEndpointDescription; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +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.node.configuration.NodeConfiguration; +import org.apache.tuscany.sca.node.impl.NodeImpl; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.ExportRegistration; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.util.tracker.ServiceTrackerCustomizer; + +/** + * Watching and exporting OSGi services + */ +public class OSGiServiceExporter extends AbstractOSGiServiceHandler implements ServiceTrackerCustomizer { + + /** + * @param context + * @param clazz + * @param customizer + */ + public OSGiServiceExporter(BundleContext context) { + super(context); + } + + public void start() { + init(); + } + + public Object addingService(ServiceReference reference) { + return exportService(reference, null); + } + + public List<ExportRegistration> exportService(ServiceReference reference, Map<String, Object> properties) { + // FIXME: [rfeng] We need to check if a corresponding endpoint has been exported + try { + Contribution contribution = introspector.introspect(reference, properties); + if (contribution != null) { + + NodeConfiguration configuration = nodeFactory.createNodeConfiguration(); + if (domainRegistry != null) { + configuration.setDomainRegistryURI(domainRegistry); + } + configuration.setURI(contribution.getURI()); + configuration.getExtensions().add(reference.getBundle()); + // FIXME: Configure the domain and node URI + NodeImpl node = new NodeImpl(nodeFactory, configuration, Collections.singletonList(contribution)); + 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 = createEndpointDescription(context, endpoint); + ExportRegistration exportRegistration = + new ExportRegistrationImpl(node, reference, endpointDescription); + exportedServices.add(exportRegistration); + } + return exportedServices; + } else { + return null; + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public void modifiedService(ServiceReference reference, Object service) { + removedService(reference, service); + exportService(reference, null); + } + + public void removedService(ServiceReference reference, Object service) { + List<ExportRegistration> exportedServices = (List<ExportRegistration>)service; + for(ExportRegistration exportRegistration: exportedServices) { + exportRegistration.close(); + } + } +} diff --git a/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceImporter.java b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceImporter.java new file mode 100644 index 0000000000..59a21ade06 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/OSGiServiceImporter.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.remoteserviceadmin.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.node.Node; +import org.apache.tuscany.sca.node.configuration.NodeConfiguration; +import org.apache.tuscany.sca.node.impl.NodeImpl; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.ImportRegistration; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; + +/** + * Watching and exporting OSGi services + */ +public class OSGiServiceImporter extends AbstractOSGiServiceHandler { + + /** + * @param context + * @param clazz + * @param customizer + */ + public OSGiServiceImporter(BundleContext context) { + super(context); + } + + public void start() { + // Defer init() to importService() + } + + public ImportRegistration importService(Bundle bundle, EndpointDescription endpointDescription) { + init(); + try { + Contribution contribution = introspector.introspect(bundle, endpointDescription); + if (contribution != null) { + + NodeConfiguration configuration = nodeFactory.createNodeConfiguration(); + if (domainRegistry != null) { + configuration.setDomainRegistryURI(domainRegistry); + } + configuration.setURI(contribution.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(); + } + +} diff --git a/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/RemoteServiceAdminImpl.java b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/RemoteServiceAdminImpl.java new file mode 100644 index 0000000000..bc2e3ce012 --- /dev/null +++ b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/RemoteServiceAdminImpl.java @@ -0,0 +1,284 @@ +/* + * 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.remoteserviceadmin.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.ExportRegistration; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.ImportRegistration; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdmin; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdminEvent; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdminListener; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.cm.ConfigurationException; +import org.osgi.service.cm.ManagedService; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventAdmin; +import org.osgi.util.tracker.ServiceTracker; + +/** + * SCA Implementation of {@link RemoteServiceAdmin} + */ +public class RemoteServiceAdminImpl implements RemoteServiceAdmin, ManagedService { + private BundleContext context; + private ServiceRegistration registration; + private ServiceRegistration managedService; + private ServiceTracker listeners; + + private OSGiServiceExporter exporter; + private OSGiServiceImporter importer; + + private Collection<ImportRegistration> importedEndpoints = new ArrayList<ImportRegistration>(); + private Collection<ExportRegistration> exportedServices = new ArrayList<ExportRegistration>(); + + public RemoteServiceAdminImpl(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(RemoteServiceAdmin.class.getName(), this, null); + Hashtable<String, Object> props = new Hashtable<String, Object>(); + props.put(Constants.SERVICE_PID, RemoteServiceAdminImpl.class.getName()); + managedService = context.registerService(ManagedService.class.getName(), this, props); + listeners = new ServiceTracker(this.context, RemoteServiceAdminListener.class.getName(), null); + listeners.open(); + } + + public void stop() { + if (registration != null) { + try { + registration.unregister(); + } catch (IllegalStateException e) { + // The service has been unregistered, ignore it + } + registration = null; + } + if (managedService != null) { + try { + managedService.unregister(); + } catch (IllegalStateException e) { + // The service has been unregistered, ignore it + } + managedService = 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.remoteserviceadmin.RemoteServiceAdmin#exportService(org.osgi.framework.ServiceReference, + * java.util.Map) + */ + public List<ExportRegistration> exportService(ServiceReference ref, Map properties) { + List<ExportRegistration> exportRegistrations = exporter.exportService(ref, properties); + if (exportRegistrations != null) { + exportedServices.addAll(exportRegistrations); + fireExportEvents(ref.getBundle(), exportRegistrations); + } + return exportRegistrations; + } + + private void fireExportEvents(Bundle source, List<ExportRegistration> exportRegistrations) { + for (ExportRegistration registration : exportRegistrations) { + RemoteServiceAdminEvent rsaEvent = + new RemoteServiceAdminEvent(RemoteServiceAdminEvent.EXPORT_REGISTRATION, source, registration, + registration.getException()); + EventAdmin eventAdmin = getEventAdmin(); + if (eventAdmin != null) { + eventAdmin.postEvent(wrap(rsaEvent)); + } + for (Object listener : listeners.getServices()) { + RemoteServiceAdminListener rsaListener = (RemoteServiceAdminListener)listener; + rsaListener.remoteAdminEvent(rsaEvent); + } + } + } + + private EventAdmin getEventAdmin() { + ServiceReference reference = context.getServiceReference(EventAdmin.class.getName()); + if (reference == null) { + return null; + } else { + return (EventAdmin)context.getService(reference); + } + } + + private Event wrap(RemoteServiceAdminEvent rsaEvent) { + int type = rsaEvent.getType(); + String eventType = null; + switch (type) { + case RemoteServiceAdminEvent.EXPORT_ERROR: + eventType = "EXPORT_ERROR"; + break; + case RemoteServiceAdminEvent.EXPORT_REGISTRATION: + eventType = "EXPORT_REGISTRATION"; + break; + case RemoteServiceAdminEvent.EXPORT_UNREGISTRATION: + eventType = "EXPORT_UNREGISTRATION"; + break; + case RemoteServiceAdminEvent.EXPORT_WARNING: + eventType = "EXPORT_WARNING"; + break; + case RemoteServiceAdminEvent.IMPORT_ERROR: + eventType = "IMPORT_ERROR"; + break; + case RemoteServiceAdminEvent.IMPORT_REGISTRATION: + eventType = "IMPORT_REGISTRATION"; + break; + case RemoteServiceAdminEvent.IMPORT_UNREGISTRATION: + eventType = "EXPORT_ERROR"; + break; + case RemoteServiceAdminEvent.IMPORT_WARNING: + eventType = "IMPORT_UNREGISTRATION"; + break; + } + String topic = "org/osgi/service/remoteserviceadmin/" + eventType; + Map<String, Object> props = new HashMap<String, Object>(); + /* + * <ul> + <li>bundle (Bundle) The Remote Service Admin bundle + <li>bundle-id (Long) The id of the Blueprint bundle. + <li>bundle-symbolicname (String) The Bundle Symbolic Name of the + Remote Service Admin bundle. + <li>bundle-version - (Version) The version of the Blueprint bundle. + <li>cause The exception, if present. + <li>import.registration An imported endpoint, if present + <li>export.registration An exported endpoint, if present + <li>service.remote.id Remote service UUID, if present + <li>service.remote.uuid Remote service UUID, if present + <li>service.remote.uri (String) The URI of the endpoint, if present + <li>objectClass (String[]) The interface names, if present + <li>service.imported.configs (String+) The configuration types of the + imported services, if present + <li>timestamp (Long) The time when the event occurred + <li>event (RemoteServiceAdminEvent) The RemoteServiceAdminEvent + object that caused this event. + </ul> + */ + Bundle rsaBundle = context.getBundle(); + props.put("bundle", rsaBundle); + props.put("bundle-id", rsaBundle.getBundleId()); + props.put("bundle-symbolicname", rsaBundle.getSymbolicName()); + props.put("bundle-version", rsaBundle.getHeaders().get(Constants.BUNDLE_VERSION)); + props.put("cause", rsaEvent.getException()); + props.put("import.registration", rsaEvent.getImportRegistration()); + props.put("export.registration", rsaEvent.getExportRegistration()); + EndpointDescription ep = null; + if (rsaEvent.getImportRegistration() != null) { + ep = rsaEvent.getImportRegistration().getImportedEndpointDescription(); + } else { + ep = rsaEvent.getExportRegistration().getEndpointDescription(); + } + props.put("service.remote.id", ep.getRemoteServiceID()); + props.put("service.remote.uuid", ep.getRemoteFrameworkUUID()); + props.put("service.remote.uri", ep.getRemoteURI()); + props.put("objectClass", ep.getInterfaces()); + props.put("service.imported.configs", ep.getConfigurationTypes()); + props.put("timestamp", new Long(System.currentTimeMillis())); + props.put("event", rsaEvent); + return new Event(topic, props); + } + + private void fireImportEvents(Bundle source, ImportRegistration registration) { + RemoteServiceAdminEvent rsaEvent = + new RemoteServiceAdminEvent(RemoteServiceAdminEvent.IMPORT_REGISTRATION, source, registration, registration + .getException()); + EventAdmin eventAdmin = getEventAdmin(); + if (eventAdmin != null) { + eventAdmin.postEvent(wrap(rsaEvent)); + } + for (Object listener : listeners.getServices()) { + RemoteServiceAdminListener rsaListener = (RemoteServiceAdminListener)listener; + rsaListener.remoteAdminEvent(rsaEvent); + } + } + + /** + * @see org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdmin#getExportedServices() + */ + public Collection<ExportRegistration> getExportedServices() { + return exportedServices; + } + + /** + * @see org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdmin#getImportedEndpoints() + */ + public Collection<ImportRegistration> getImportedEndpoints() { + return importedEndpoints; + } + + /** + * @see org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdmin#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) { + fireImportEvents(bundle, importReg); + importedEndpoints.add(importReg); + } + return importReg; + } + + public synchronized void updated(Dictionary props) throws ConfigurationException { + if (props == null) { + // It can be null in Apache Felix + return; + } + String domainRegistry = (String)props.get("org.osgi.sca.domain.registry"); + if (domainRegistry != null) { + exporter.setDomainRegistry(domainRegistry); + importer.setDomainRegistry(domainRegistry); + } + } +} diff --git a/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/TopologyManagerImpl.java b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/TopologyManagerImpl.java new file mode 100644 index 0000000000..1967fa6e4a --- /dev/null +++ b/sca-java-2.x/tags/2.0-M4-RC1/modules/node-impl-osgi/src/main/java/org/apache/tuscany/sca/osgi/remoteserviceadmin/impl/TopologyManagerImpl.java @@ -0,0 +1,410 @@ +/* + * 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.remoteserviceadmin.impl; + +import static org.apache.tuscany.sca.implementation.osgi.OSGiProperty.SERVICE_EXPORTED_INTERFACES; +import static org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteConstants.SERVICE_EXPORTED_CONFIGS; +import static org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteConstants.SERVICE_IMPORTED; + +import java.util.Collection; +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.Level; +import java.util.logging.Logger; + +import org.apache.tuscany.sca.common.java.collection.CollectionMap; +import org.apache.tuscany.sca.core.LifeCycleListener; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointListener; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.ExportRegistration; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.ImportRegistration; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdmin; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdminEvent; +import org.apache.tuscany.sca.osgi.remoteserviceadmin.RemoteServiceAdminListener; +import org.osgi.framework.Bundle; +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.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 TopologyManagerImpl implements ListenerHook, RemoteServiceAdminListener, EndpointListener, + ServiceTrackerCustomizer, LifeCycleListener /*, EventHook */{ + private final static Logger logger = Logger.getLogger(TopologyManagerImpl.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 CollectionMap<String, ListenerInfo> serviceListeners = new CollectionMap<String, ListenerInfo>(); + + private CollectionMap<ServiceReference, ExportRegistration> exportedServices = + new CollectionMap<ServiceReference, ExportRegistration>(); + private CollectionMap<EndpointDescription, ImportRegistration> importedServices = + new CollectionMap<EndpointDescription, ImportRegistration>(); + + private Filter remotableServiceFilter; + + public TopologyManagerImpl(BundleContext context) { + this.context = context; + } + + public void start() { + String filter = + "(& (!(" + SERVICE_IMPORTED + + "=*)) (" + + SERVICE_EXPORTED_INTERFACES + + "=*) (" + + SERVICE_EXPORTED_CONFIGS + + "=org.osgi.sca) )"; + try { + remotableServiceFilter = context.createFilter(filter); + } catch (InvalidSyntaxException e) { + // Ignore + } + + endpointListener = context.registerService(EndpointListener.class.getName(), this, null); + remoteAdmins = new ServiceTracker(this.context, RemoteServiceAdmin.class.getName(), null); + remoteAdmins.open(); + + // DO NOT register EventHook.class.getName() as it cannot report existing services + String interfaceNames[] = + new String[] {ListenerHook.class.getName(), RemoteServiceAdminListener.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) { + RemoteServiceAdmin remoteAdmin = (RemoteServiceAdmin)ra; + List<ExportRegistration> exportRegistrations = remoteAdmin.exportService(reference, null); + if (exportRegistrations != null && !exportRegistrations.isEmpty()) { + exportedServices.putValues(reference, exportRegistrations); + } + } + } + } + + /** + * @see org.osgi.framework.hooks.service.ListenerHook#added(java.util.Collection) + */ + public void added(Collection listeners) { + synchronized (serviceListeners) { + try { + 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(); + } + } catch (Throwable e) { + logger.log(Level.SEVERE, e.getMessage(), e); + if (e instanceof Error) { + throw (Error)e; + } else if (e instanceof RuntimeException) { + throw (RuntimeException)e; + } else { + // Should not happen + throw new RuntimeException(e); + } + } + } + } + + 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 CollectionMap<Class<?>, ListenerInfo> findServiceListeners(EndpointDescription endpointDescription, + String matchedFilter) { + synchronized (serviceListeners) { + + // 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(); + CollectionMap<Class<?>, ListenerInfo> interfaceToListeners = new CollectionMap<Class<?>, ListenerInfo>(); + for (String i : interfaceNames) { + for (Iterator<ListenerInfo> it = listeners.iterator(); it.hasNext();) { + try { + ListenerInfo listener = it.next(); + if (listener.isRemoved()) { + it.remove(); + continue; + } + try { + Class<?> interfaceClass = listener.getBundleContext().getBundle().loadClass(i); + interfaceToListeners.putValue(interfaceClass, listener); + } catch (IllegalStateException e) { + logger.log(Level.WARNING, e.getMessage(), e); + // Ignore the exception + } + } 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) { + synchronized (serviceListeners) { + try { + 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(); + } + } catch (Throwable e) { + logger.log(Level.SEVERE, e.getMessage(), e); + if (e instanceof Error) { + throw (Error)e; + } else if (e instanceof RuntimeException) { + throw (RuntimeException)e; + } else { + // Should not happen + throw new RuntimeException(e); + } + } + } + } + + /** + * @see org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminListener#remoteAdminEvent(org.apache.tuscany.sca.osgi.service.remoteadmin.RemoteAdminEvent) + */ + public void remoteAdminEvent(RemoteServiceAdminEvent event) { + switch (event.getType()) { + case RemoteServiceAdminEvent.EXPORT_ERROR: + case RemoteServiceAdminEvent.EXPORT_REGISTRATION: + case RemoteServiceAdminEvent.EXPORT_UNREGISTRATION: + case RemoteServiceAdminEvent.EXPORT_WARNING: + break; + case RemoteServiceAdminEvent.IMPORT_ERROR: + case RemoteServiceAdminEvent.IMPORT_REGISTRATION: + case RemoteServiceAdminEvent.IMPORT_UNREGISTRATION: + case RemoteServiceAdminEvent.IMPORT_WARNING: + break; + } + } + + /** + * @see org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointListener#addEndpoint(org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription, + * java.lang.String) + */ + public void endpointAdded(EndpointDescription endpoint, String matchedFilter) { + importService(endpoint, matchedFilter); + } + + /** + * @see org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointListener#removeEndpoint(org.apache.tuscany.sca.osgi.remoteserviceadmin.EndpointDescription) + */ + public void endpointRemoved(EndpointDescription endpoint, String matchedFilter) { + unimportService(endpoint); + } + + private void importService(EndpointDescription endpoint, String matchedFilter) { + Object[] admins = remoteAdmins.getServices(); + if (admins == null) { + logger.warning("No RemoteAdmin services are available."); + return; + } + + CollectionMap<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); + props.put(Constants.OBJECTCLASS, new String[] {interfaceClass.getName()}); + EndpointDescription description = new EndpointDescription(props); + + if (admins != null) { + for (Object ra : admins) { + RemoteServiceAdmin remoteAdmin = (RemoteServiceAdmin)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) { + try { + registration.unregister(); + } catch (IllegalStateException e) { + // The service has been unregistered, ignore it + } + registration = null; + } + if (remoteAdmins != null) { + remoteAdmins.close(); + remoteAdmins = null; + } + synchronized (serviceListeners) { + serviceListeners.clear(); + } + } + +} |