Package org.apache.tuscany.sca.osgi.remoteserviceadmin.impl

Source Code of org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.EndpointIntrospector

/*
* 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.osgi.remoteserviceadmin.impl.OSGiHelper.createOSGiProperty;
import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper.getStringArray;
import static org.osgi.framework.Constants.OBJECTCLASS;
import static org.osgi.framework.Constants.SERVICE_ID;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
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 javax.xml.stream.XMLStreamWriter;

import org.apache.tuscany.sca.assembly.AssemblyFactory;
import org.apache.tuscany.sca.assembly.Base;
import org.apache.tuscany.sca.assembly.Binding;
import org.apache.tuscany.sca.assembly.Component;
import org.apache.tuscany.sca.assembly.ComponentReference;
import org.apache.tuscany.sca.assembly.ComponentService;
import org.apache.tuscany.sca.assembly.Composite;
import org.apache.tuscany.sca.assembly.Endpoint;
import org.apache.tuscany.sca.assembly.Reference;
import org.apache.tuscany.sca.assembly.Service;
import org.apache.tuscany.sca.contribution.Contribution;
import org.apache.tuscany.sca.contribution.ContributionFactory;
import org.apache.tuscany.sca.contribution.processor.ContributionReadException;
import org.apache.tuscany.sca.contribution.processor.ContributionResolveException;
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.implementation.osgi.ServiceDescriptionsFactory;
import org.apache.tuscany.sca.interfacedef.InvalidInterfaceException;
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.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.apache.tuscany.sca.policy.PolicySet;
import org.oasisopen.sca.ServiceRuntimeException;
import org.oasisopen.sca.annotation.PolicySets;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.remoteserviceadmin.EndpointDescription;
import org.osgi.service.remoteserviceadmin.RemoteConstants;
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 StAXArtifactProcessor<Composite> compositeProcessor;
    private JavaInterfaceFactory javaInterfaceFactory;
    private Deployer deployer;
    private ServiceTracker discoveryTracker;

    /**
     * @param name
     * @return
     */
    private static QName getQName(String name) {
        QName qname;
        if (name.startsWith("{")) {
            int i = name.indexOf('}');
            if (i != -1) {
                qname = new QName(name.substring(1, i), name.substring(i + 1));
            } else {
                throw new IllegalArgumentException("Invalid qname: " + name);
            }
        } else {
            // Default to SCA namespace
            qname = new QName("", name);
        }
        return qname;
    }

    /**
     * @param context TODO
     * @param registry
     */
    public EndpointIntrospector(BundleContext context, ExtensionPointRegistry registry, ServiceTracker discoveryTracker) {
        super();
        this.registry = registry;
        // this.context = context;
        this.discoveryTracker = discoveryTracker;
        this.factories = registry.getExtensionPoint(FactoryExtensionPoint.class);
        this.modelResolvers = registry.getExtensionPoint(ModelResolverExtensionPoint.class);
//        this.compositeProcessor =
//            registry.getExtensionPoint(StAXArtifactProcessorExtensionPoint.class).getProcessor(Composite.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 Reference�s 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 (OBJECTCLASS.equalsIgnoreCase(p.getKey())) {
                    throw new IllegalArgumentException(OBJECTCLASS + " property cannot be overridden.");
                } else if (SERVICE_ID.equalsIgnoreCase(p.getKey())) {
                    throw new IllegalArgumentException(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 {
        Bundle bundle = reference.getBundle();
        Map<String, Object> properties = getProperties(reference, props);
        Collection<OSGiProperty> osgiProps = implementationFactory.createOSGiProperties(reference);
        Long sid = (Long)reference.getProperty(SERVICE_ID);

        String[] requiredIntents = getStringArray(properties.get(RemoteConstants.SERVICE_EXPORTED_INTENTS));
        List<Intent> intents = getIntents(requiredIntents);
        String[] requiredIntentsExtra = getStringArray(properties.get(RemoteConstants.SERVICE_EXPORTED_INTENTS_EXTRA));
        List<Intent> extraIntents = getIntents(requiredIntentsExtra);
        Set<Intent> allIntents = new HashSet<Intent>(intents);
        allIntents.addAll(extraIntents);

        String[] bindingNames = getStringArray(properties.get(SCA_BINDINGS));
        Collection<Binding> bindings = loadBindings(bindingNames);

        String[] remoteInterfaces = getStringArray(reference.getProperty(RemoteConstants.SERVICE_EXPORTED_INTERFACES));
        if (remoteInterfaces == null || remoteInterfaces.length > 0 && "*".equals(remoteInterfaces[0])) {
            remoteInterfaces = getStringArray(reference.getProperty(OBJECTCLASS));
        } else {
            remoteInterfaces = parse(remoteInterfaces);
            String[] objectClasses = getStringArray(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");
            }
        }

        Contribution contribution = generateContribution(bundle, sid, remoteInterfaces, bindings, allIntents, osgiProps);
        return contribution;
    }
   
    public String instrospectSCAConfig(ServiceReference reference, Map<String, Object> props, ComponentService service){
       
        ServiceDescriptionsFactory serviceDescriptionFactory = registry.getExtensionPoint(ServiceDescriptionsFactory.class);
        SCAConfig scaConfig = serviceDescriptionFactory.createSCAConfig();
       
        // add the binding configurations
        List<Binding> bindings = scaConfig.getBindings();
        bindings.addAll(service.getBindings());
       
        // add the intent configurations
        List<Intent> intents = scaConfig.getIntents();
        intents.addAll(service.getRequiredIntents());
       
        // add the policy set configurations
        List<PolicySet> policySets = scaConfig.getPolicySets();
        policySets.addAll(service.getPolicySets());
       
        // set up the target namespace
        // TODO - there is a bug in the spec which only allow bindings from one
        //        namsepace to be included in sca-config element. Here we just
        //        the first bindings namespace
        Map<String, Object> properties = getProperties(reference, props);
        String[] bindingNames = getStringArray(properties.get(SCA_BINDINGS));
        if (bindingNames.length > 0){
            QName firstBindingQName = getQName(bindingNames[0]);
            scaConfig.setTargetNamespace(firstBindingQName.getNamespaceURI());
        }       
       
        // write the sca config out to XML
        String scaConfigXMLString = "";
       
        try {
            Writer writer = new StringWriter();
            deployer.saveXMLDocument(scaConfig, writer, deployer.createMonitor());
            scaConfigXMLString = writer.toString();
        } catch (Exception ex){
            throw new ServiceRuntimeException(ex);
        }
       
        return scaConfigXMLString;
    }   
   
    /*
    public Contribution loadContribution(Bundle bundle, Composite composite) {
        try {
            URL root = bundle.getEntry("/");
            Contribution contribution = deployer.loadContribution(root.toURI(), root, deployer.createMonitor());
            deployer.attachDeploymentComposite(contribution, composite, false);
            return contribution;
        } catch (Exception e) {
            throw new ServiceRuntimeException(e);
        }
    }
    */

    /**
     * Generate a contribution that contains the composite for the exported service
     * @param bundle The OSGi bundle
     * @param sid The service id
     * @param remoteInterfaces
     * @param bindings
     * @param allIntents
     * @return
     * @throws ClassNotFoundException
     * @throws InvalidInterfaceException
     */
    private Contribution generateContribution(Bundle bundle,
                                              Long sid,
                                              String[] remoteInterfaces,
                                              Collection<Binding> bindings,
                                              Set<Intent> allIntents,
                                              Collection<OSGiProperty> osgiProps) throws ClassNotFoundException,
        InvalidInterfaceException {
        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);

        composite.getComponents().add(component);

        OSGiImplementation implementation = implementationFactory.createOSGiImplementation();

        implementation.setBundle(bundle);
        component.setImplementation(implementation);
        implementation.setUnresolved(false);

        OSGiProperty serviceID = implementationFactory.createOSGiProperty();
        serviceID.setName(SERVICE_ID);
        // The service.id is Long
        serviceID.setValue(String.valueOf(sid));

        for (String intf : remoteInterfaces) {
            Service service = assemblyFactory.createService();
            JavaInterfaceContract interfaceContract = createJavaInterfaceContract(bundle, intf);
            String name = intf.substring(intf.lastIndexOf('.') + 1);
            service.setName(name);
            service.setInterfaceContract(interfaceContract);

            implementation.getServices().add(service);

            ComponentService componentService = assemblyFactory.createComponentService();
            componentService.setName(service.getName());
            componentService.getExtensions().add(serviceID);
            componentService.getExtensions().addAll(osgiProps);

            component.getServices().add(componentService);
            componentService.setService(service);
        }

        for (ComponentService componentService : component.getServices()) {
            componentService.getRequiredIntents().addAll(allIntents);
            componentService.getBindings().addAll(bindings);
        }

        // FIXME: Should we scan the owning bundle to create the SCA contribution?
        Contribution contribution = loadContribution(bundle, id, composite);
        return contribution;
    }

    private Contribution loadContribution(Bundle bundle, String id, Composite composite) {
        Contribution contribution = contributionFactory.createContribution();
        contribution.setClassLoader(OSGiHelper.createBundleClassLoader(bundle));
        contribution.setURI(id);
        contribution.setLocation(bundle.getEntry("/").toString());
        deployer.attachDeploymentComposite(contribution, composite, false);
        ModelResolver modelResolver = new ExtensibleModelResolver(contribution, modelResolvers, factories);
        contribution.setModelResolver(modelResolver);
        // compositeProcessor.resolve(composite, modelResolver, new ProcessorContext(registry));
        contribution.setUnresolved(true);
        return contribution;
    }

    /**
     * @param bundle
     * @param endpoint
     * @return
     * @throws Exception
     */
    public Contribution introspect(Bundle bundle, EndpointDescription endpoint) throws Exception {
        Collection<Binding> bindings = Collections.emptyList();
        Collection<String> interfaces = Collections.emptyList();
        Collection<Intent> intents = Collections.emptyList();
        Endpoint ep = (Endpoint)endpoint.getProperties().get(Endpoint.class.getName());
        Collection<OSGiProperty> osgiProps = implementationFactory.createOSGiProperties(endpoint.getProperties());
        if (ep != null) {
            bindings = Collections.singletonList(ep.getBinding());
            interfaces = Collections.singletonList(((JavaInterface)ep.getComponentServiceInterfaceContract().getInterface()).getName());
            // FIXME: [rfeng] We need to build the in-memory composite so that intents are calculated at the ep level
            intents = ep.getService().getRequiredIntents();
        } else {
            Map<String, Object> properties = endpoint.getProperties();
            interfaces = endpoint.getInterfaces();
            String[] requiredIntents = getStringArray(properties.get(RemoteConstants.SERVICE_INTENTS));
            intents = getIntents(requiredIntents);

            String[] bindingNames = getStringArray(properties.get(SCA_BINDINGS));
            bindings = loadBindings(bindingNames);
        }

        Contribution contribution = generateContribution(bundle, interfaces, bindings, intents, osgiProps);
        return contribution;
    }

    private Contribution generateContribution(Bundle bundle,
                                              Collection<String> remoteInterfaces,
                                              Collection<Binding> bindings,
                                              Collection<Intent> intents,
                                              Collection<OSGiProperty> osgiProps) throws ClassNotFoundException,
        InvalidInterfaceException, ContributionResolveException {
        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 = createJavaInterfaceContract(bundle, intf);

            reference.setName("ref" + (count++));
            reference.setInterfaceContract(interfaceContract);

            implementation.getReferences().add(reference);

            ComponentReference componentReference = assemblyFactory.createComponentReference();
            componentReference.setName(reference.getName());
            componentReference.getExtensions().addAll(osgiProps);
            component.getReferences().add(componentReference);
            componentReference.setReference(reference);
            componentReference.setWiredByImpl(true);
        }

        for (ComponentReference componentReference : component.getReferences()) {
            componentReference.getRequiredIntents().addAll(intents);
            componentReference.getBindings().addAll(bindings);
        }

        Contribution contribution = loadContribution(bundle, id, composite);
        return contribution;
    }

    private JavaInterfaceContract createJavaInterfaceContract(Bundle bundle, String intf)
        throws ClassNotFoundException, InvalidInterfaceException {
        JavaInterfaceContract interfaceContract = javaInterfaceFactory.createJavaInterfaceContract();
        Class<?> interfaceClass = bundle.loadClass(intf);
        JavaInterface javaInterface = javaInterfaceFactory.createJavaInterface();
        // [rfeng] For OSGi, the interfaces should be marked as remotable
        javaInterface.setRemotable(true);
        // [rfeng] We need to mark the interface to be remotable before the createJavaInterface() is called
        javaInterfaceFactory.createJavaInterface(javaInterface, interfaceClass);
        interfaceContract.setInterface(javaInterface);
        if (javaInterface.getCallbackClass() != null) {
            JavaInterface callbackInterface = javaInterfaceFactory.createJavaInterface(javaInterface.getCallbackClass());
            callbackInterface.setRemotable(true);
            interfaceContract.setCallbackInterface(callbackInterface);
        }
        return interfaceContract;
    }

    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 ("".equals(bindingName.getNamespaceURI()) ||
                            sc.getTargetNamespace().equals(bindingName.getNamespaceURI())) {
                            for (Binding binding : sc.getBindings()) {
                                if (bindingName.getLocalPart().equals(binding.getName())) {
                                    // We need to check duplications
                                    if (bindingMap.put(bindingName, binding) != null) {
                                        throw new ServiceRuntimeException("Duplicate binding found: " + bindingName);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        for (QName bindingName : bindingNames) {
            if (!bindingMap.containsKey(bindingName)) {
                throw new ServiceRuntimeException("Binding cannot be resolved: " + bindingName);
            }
        }
        return bindingMap.values();
    }

}
TOP

Related Classes of org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.EndpointIntrospector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.