Package org.apache.tuscany.sca.implementation.spring.introspect

Source Code of org.apache.tuscany.sca.implementation.spring.introspect.SpringXMLComponentTypeLoader

/*
* 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.implementation.spring.introspect;

import static javax.xml.stream.XMLStreamConstants.END_ELEMENT;
import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.apache.tuscany.sca.assembly.AssemblyFactory;
import org.apache.tuscany.sca.assembly.ComponentType;
import org.apache.tuscany.sca.assembly.Multiplicity;
import org.apache.tuscany.sca.assembly.Property;
import org.apache.tuscany.sca.assembly.Reference;
import org.apache.tuscany.sca.assembly.Service;
import org.apache.tuscany.sca.assembly.xml.PolicySubjectProcessor;
import org.apache.tuscany.sca.contribution.Artifact;
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.processor.ProcessorContext;
import org.apache.tuscany.sca.contribution.resolver.ClassReference;
import org.apache.tuscany.sca.contribution.resolver.ModelResolver;
import org.apache.tuscany.sca.core.ExtensionPointRegistry;
import org.apache.tuscany.sca.core.FactoryExtensionPoint;
import org.apache.tuscany.sca.implementation.java.JavaConstructorImpl;
import org.apache.tuscany.sca.implementation.java.JavaElementImpl;
import org.apache.tuscany.sca.implementation.java.JavaImplementation;
import org.apache.tuscany.sca.implementation.java.JavaParameterImpl;
import org.apache.tuscany.sca.implementation.spring.SpringBeanElement;
import org.apache.tuscany.sca.implementation.spring.SpringConstructorArgElement;
import org.apache.tuscany.sca.implementation.spring.SpringImplementation;
import org.apache.tuscany.sca.implementation.spring.SpringImplementationConstants;
import org.apache.tuscany.sca.implementation.spring.SpringPropertyElement;
import org.apache.tuscany.sca.implementation.spring.SpringSCAPropertyElement;
import org.apache.tuscany.sca.implementation.spring.SpringSCAReferenceElement;
import org.apache.tuscany.sca.implementation.spring.SpringSCAServiceElement;
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.interfacedef.util.JavaXMLMapper;
import org.apache.tuscany.sca.monitor.Monitor;
import org.apache.tuscany.sca.monitor.Problem;
import org.apache.tuscany.sca.monitor.Problem.Severity;
import org.apache.tuscany.sca.policy.PolicyFactory;

/**
* Introspects a Spring XML application-context configuration file to create <implementation-spring../>
* component type information.
*
* @version $Rev: 826907 $ $Date: 2009-10-19 17:17:14 -0700 (Mon, 19 Oct 2009) $
*/
public class SpringXMLComponentTypeLoader {
    private ExtensionPointRegistry registry;
    private XMLInputFactory xmlInputFactory;
    private ContributionFactory contributionFactory;
    private AssemblyFactory assemblyFactory;
    private JavaInterfaceFactory javaFactory;
    private PolicyFactory policyFactory;
    private PolicySubjectProcessor policyProcessor;
    private Monitor monitor;
    private SpringBeanIntrospector beanIntrospector;

    public SpringXMLComponentTypeLoader(ExtensionPointRegistry registry,
                                        Monitor monitor) {
        super();
        this.registry = registry;
        FactoryExtensionPoint factories = registry.getExtensionPoint(FactoryExtensionPoint.class);
        this.assemblyFactory = factories.getFactory(AssemblyFactory.class);
        this.javaFactory = factories.getFactory(JavaInterfaceFactory.class);
        this.policyFactory = factories.getFactory(PolicyFactory.class);
        this.policyProcessor = new PolicySubjectProcessor(policyFactory);
        this.contributionFactory = factories.getFactory(ContributionFactory.class);
        this.xmlInputFactory = factories.getFactory(XMLInputFactory.class);
        this.monitor = monitor;
    }
   
    /**
     * Report a exception.
     *
     * @param problems
     * @param message
     * @param model
     */
    private void error(String message, Object model, Exception ex) {
       if (monitor != null) {
          Problem problem = monitor.createProblem(this.getClass().getName(), "impl-spring-validation-messages", Severity.ERROR, model, message, ex);
          monitor.problem(problem);
       }
    }

    /**
     * Report a error.
     *
     * @param problems
     * @param message
     * @param model
     */
    private void error(String message, Object model, Object... messageParameters) {
       if (monitor != null) {
          Problem problem = monitor.createProblem(this.getClass().getName(), "impl-spring-validation-messages", Severity.ERROR, model, message, (Object[])messageParameters);
          monitor.problem(problem);
       }
    }

    protected Class<SpringImplementation> getImplementationClass() {
        return SpringImplementation.class;
    }

    /**
     * Base method which loads the component type from the application-context attached to the
     * Spring implementation
     *
     */
    public void load(SpringImplementation implementation, ModelResolver resolver, ProcessorContext context) throws ContributionReadException {
        //System.out.println("Spring TypeLoader - load method start");
      ComponentType componentType = implementation.getComponentType();
        /* Check that there is a component type object already set  */
        if (componentType == null) {
            throw new ContributionReadException("SpringXMLLoader load: implementation has no ComponentType object");
        }
        if (componentType.isUnresolved()) {
            /* Fetch the location of the application-context file from the implementation */
            loadFromXML(implementation, resolver, context);
            if (!componentType.isUnresolved())
                implementation.setUnresolved(false);
        } // end if
        //System.out.println("Spring TypeLoader - load method complete");
    } // end method load

    private Class<?> resolveClass(ModelResolver resolver, String className, ProcessorContext context) throws ClassNotFoundException {
        ClassReference classReference = new ClassReference(className);
        classReference = resolver.resolveModel(ClassReference.class, classReference, context);
        if (classReference.isUnresolved()) {
            throw new ClassNotFoundException(className);
        }
        Class<?> javaClass = classReference.getJavaClass();
        return javaClass;
    }

    /**
     * Method which fills out the component type for a Spring implementation by reading the
     * Spring application-context.xml file.
     *
     * @param implementation SpringImplementation into which to load the component type information
     * @throws ContributionReadException Failed to read the contribution
     */
    private void loadFromXML(SpringImplementation implementation, ModelResolver resolver, ProcessorContext context) throws ContributionReadException {
        XMLStreamReader reader;
        List<SpringBeanElement> beans = new ArrayList<SpringBeanElement>();
        List<SpringSCAServiceElement> services = new ArrayList<SpringSCAServiceElement>();
        List<SpringSCAReferenceElement> references = new ArrayList<SpringSCAReferenceElement>();
        List<SpringSCAPropertyElement> scaproperties = new ArrayList<SpringSCAPropertyElement>();

        URL resource;
        List<URL> contextResources = new ArrayList<URL>();
        String contextPath = implementation.getLocation();

        try {         
            resource = resolveLocation(resolver, contextPath, context);
            contextResources = getApplicationContextResource(resource);
           
            implementation.setClassLoader(new ContextClassLoader(resolver, context));
            implementation.setResource(contextResources);
            // The URI is used to uniquely identify the Implementation
            implementation.setURI(resource.toString());
           
            for (URL contextResource : contextResources) {             
              List<SpringBeanElement> appCxtBeans = new ArrayList<SpringBeanElement>();
                List<SpringSCAServiceElement> appCxtServices = new ArrayList<SpringSCAServiceElement>();
                List<SpringSCAReferenceElement> appCxtReferences = new ArrayList<SpringSCAReferenceElement>();
                List<SpringSCAPropertyElement> appCxtProperties = new ArrayList<SpringSCAPropertyElement>();
              reader = xmlInputFactory.createXMLStreamReader(contextResource.openStream());
              // Read the beans, services, references and properties for individual application context
              readContextDefinition(resolver, reader, contextPath, appCxtBeans, appCxtServices, appCxtReferences, appCxtProperties, context);
              // Validate the beans from individual application context for uniqueness
              validateBeans(appCxtBeans, appCxtServices, appCxtReferences, appCxtProperties);
              // Add all the validated beans to the generic list
              beans.addAll(appCxtBeans);
              services.addAll(appCxtServices);
              references.addAll(appCxtReferences);
              scaproperties.addAll(appCxtProperties);
              reader.close();
            }
        } catch (IOException e) {
            throw new ContributionReadException(e);
        } catch (XMLStreamException e) {
            throw new ContributionReadException(e);
        }

        /* At this point, the complete application-context.xml file has been read and its contents  */
        /* stored in the lists of beans, services, references.  These are now used to generate      */
        /* the implied componentType for the application context                    */
        generateComponentType(implementation, resolver, beans, services, references, scaproperties, context);

        return;
    } // end method loadFromXML

    private URL resolveLocation(ModelResolver resolver, String contextPath, ProcessorContext context) throws MalformedURLException,
        ContributionReadException {
        URL resource = null;
        URI uri = URI.create(contextPath);
        if (!uri.isAbsolute()) {
            Artifact artifact = contributionFactory.createArtifact();
            artifact.setUnresolved(true);
            artifact.setURI(contextPath);
            artifact = resolver.resolveModel(Artifact.class, artifact, context);
            if (!artifact.isUnresolved()) {
                resource = new URL(artifact.getLocation());
            } else {
                throw new ContributionReadException("Location cannot be resloved: " + contextPath);
            }
        } else {
            resource = new URL(contextPath);
        }
        return resource;
    }

    /**
     * Method which returns the XMLStreamReader for the Spring application-context.xml file
     * specified in the location attribute
     */
    private XMLStreamReader getApplicationContextReader(ModelResolver resolver, String location, ProcessorContext context) throws ContributionReadException {

        try {
            URL resource = getApplicationContextResource(resolveLocation(resolver, location, context)).get(0);
            XMLStreamReader reader =
              xmlInputFactory.createXMLStreamReader(resource.openStream());
            return reader;
        } catch (IOException e) {
            throw new ContributionReadException(e);
        } catch (XMLStreamException e) {
            throw new ContributionReadException(e);
        }
    }

    /**
     * Method which reads the spring context definitions from Spring application-context.xml
     * file and identifies the defined beans, properties, services and references
     * @param context
     */
    private void readContextDefinition(ModelResolver resolver,
                                       XMLStreamReader reader,
                                       String contextPath,
                                       List<SpringBeanElement> beans,
                                       List<SpringSCAServiceElement> services,
                                       List<SpringSCAReferenceElement> references,
                                       List<SpringSCAPropertyElement> scaproperties, ProcessorContext context) throws ContributionReadException {

        SpringBeanElement bean = null;

        try {
            boolean completed = false;
            while (!completed) {
                switch (reader.next()) {
                    case START_ELEMENT:
                        QName qname = reader.getName();
                        //System.out.println("Spring TypeLoader - found element with name: " + qname.toString());
                        if (SpringImplementationConstants.IMPORT_ELEMENT.equals(qname)) {
                          //FIXME - put the sequence of code below which gets the ireader into a subsidiary method
                            String location = reader.getAttributeValue(null, "resource");
                            if (location != null) {
                              // FIXME - need to find a right way of generating this path
                                String resourcePath = contextPath.substring(0, contextPath.lastIndexOf("/")+1) + location;
                                XMLStreamReader ireader = getApplicationContextReader(resolver, resourcePath, context);
                                // Read the context definition for the identified imported resource
                                readContextDefinition(resolver, ireader, contextPath, beans, services, references, scaproperties, context);
                            }
                        } else if (SpringImplementationConstants.SCA_SERVICE_ELEMENT.equals(qname)) {
                          // The value of the @name attribute of an <sca:service/> subelement of a <beans/>
                          // element MUST be unique amongst the <sca:service/> subelements of the <beans/> element.
                          if (!services.isEmpty() && (services.contains(reader.getAttributeValue(null, "name"))))
                                error("ScaServiceNameNotUnique", resolver);
                         
                          SpringSCAServiceElement service =
                                new SpringSCAServiceElement(reader.getAttributeValue(null, "name"),
                                              reader.getAttributeValue(null, "target"));
                            if (reader.getAttributeValue(null, "type") != null)
                              service.setType(reader.getAttributeValue(null, "type"));
                            policyProcessor.readPolicies(service, reader);
                            services.add(service);
                        } else if (SpringImplementationConstants.SCA_REFERENCE_ELEMENT.equals(qname)) {
                          // The value of the @name attribute of an <sca:reference/> subelement of a <beans/>
                          // element MUST be unique amongst the @name attributes of the <sca:reference/> subelements,
                          // of the <beans/> element.
                          if (!references.isEmpty() && (references.contains(reader.getAttributeValue(null, "name"))))
                                error("ScaReferenceNameNotUnique", resolver);
                         
                          SpringSCAReferenceElement reference =
                                new SpringSCAReferenceElement(reader.getAttributeValue(null, "name"),
                                                reader.getAttributeValue(null, "type"));
                            if (reader.getAttributeValue(null, "default") != null)
                              reference.setDefaultBean(reader.getAttributeValue(null, "default"));
                            policyProcessor.readPolicies(reference, reader);
                            references.add(reference);                           
                        } else if (SpringImplementationConstants.SCA_PROPERTY_ELEMENT.equals(qname)) {
                          // The value of the @name attribute of an <sca:property/> subelement of a <beans/>
                          // element MUST be unique amongst the @name attributes of the <sca:property/> subelements,
                          // of the <beans/> element.
                          if (!scaproperties.isEmpty() && (scaproperties.contains(reader.getAttributeValue(null, "name"))))
                                error("ScaPropertyNameNotUnique", resolver);
                         
                          SpringSCAPropertyElement scaproperty =
                                new SpringSCAPropertyElement(reader.getAttributeValue(null, "name"), reader
                                    .getAttributeValue(null, "type"));
                            scaproperties.add(scaproperty);
                        } else if (SpringImplementationConstants.BEAN_ELEMENT.equals(qname)) {
                            bean = new SpringBeanElement(reader.getAttributeValue(null, "id"),
                                           reader.getAttributeValue(null, "class"));
                            if (reader.getAttributeValue(null, "abstract") != null)
                              if (reader.getAttributeValue(null, "abstract").equals("true"))
                                bean.setAbstractBean(true);
                            if (reader.getAttributeValue(null, "parent") != null)
                              if (!reader.getAttributeValue(null, "parent").equals(""))
                                bean.setParentAttribute(true);                            
                            if (reader.getAttributeValue(null, "factory-bean") != null)
                              if (!reader.getAttributeValue(null, "factory-bean").equals(""))
                                bean.setFactoryBeanAttribute(true);                             
                            if (reader.getAttributeValue(null, "factory-method") != null)
                              if (!reader.getAttributeValue(null, "factory-method").equals(""))
                                bean.setFactoryMethodAttribute(true);                            
                            // Set the first name as bean name, when the @id attribute is absent.
                            if (reader.getAttributeValue(null, "id") == null) {
                              if (reader.getAttributeValue(null, "name") != null) {
                                String[] names = (reader.getAttributeValue(null, "name")).split(",");
                                bean.setId(names[0]);
                              }
                            }
                            beans.add(bean);
                            // Read the <bean> element and its child elements
                            readBeanDefinition(reader, bean, beans);
                        } // end if
                        break;
                    case END_ELEMENT:
                        if (SpringImplementationConstants.BEANS_ELEMENT.equals(reader.getName())) {
                            //System.out.println("Spring TypeLoader - finished read of context file");
                            completed = true;
                            break;
                        } // end if
                } // end switch
            } // end while
        } catch (XMLStreamException e) {
            throw new ContributionReadException(e);
        }
    }


    /**
     * Method which reads the bean definitions from Spring application-context.xml file and identifies
     * the defined beans, properties, services and references
     */
    private void readBeanDefinition(XMLStreamReader reader,
                                    SpringBeanElement bean,
                                    List<SpringBeanElement> beans) throws ContributionReadException {

      SpringBeanElement innerbean = null;
        SpringPropertyElement property = null;
        SpringConstructorArgElement constructorArg = null;
       
        try {
            boolean completed = false;           
            while (!completed) {
                switch (reader.next()) {
                    case START_ELEMENT:
                        QName qname = reader.getName();
                        if (SpringImplementationConstants.BEAN_ELEMENT.equals(qname)) {
                          innerbean = new SpringBeanElement(reader.getAttributeValue(null, "id"), reader
                                    .getAttributeValue(null, "class"));
                          // Set the first name as bean name, when the @id attribute is absent.
                            if (reader.getAttributeValue(null, "id") == null) {
                              if (reader.getAttributeValue(null, "name") != null) {
                                String[] names = (reader.getAttributeValue(null, "name")).split(",");
                                innerbean.setId(names[0]);
                              }
                            }
                            innerbean.setInnerBean(true);
                            beans.add(innerbean);
                            readBeanDefinition(reader, innerbean, beans);
                        } else if (SpringImplementationConstants.PROPERTY_ELEMENT.equals(qname)) {
                            property = new SpringPropertyElement(reader.getAttributeValue(null, "name"));
                            if (reader.getAttributeValue(null, "ref") != null)
                              property.addRef(reader.getAttributeValue(null, "ref"));
                            bean.addProperty(property);
                        } else if (SpringImplementationConstants.CONSTRUCTORARG_ELEMENT.equals(qname)) {
                            constructorArg = new SpringConstructorArgElement(reader.getAttributeValue(null, "type"));
                            if (reader.getAttributeValue(null, "ref") != null)
                              constructorArg.addRef(reader.getAttributeValue(null, "ref"));
                            if (reader.getAttributeValue(null, "index") != null)
                              constructorArg.setIndex((new Integer(reader.getAttributeValue(null, "index"))).intValue());
                            if (reader.getAttributeValue(null, "value") != null)
                              constructorArg.addValue(reader.getAttributeValue(null, "value"));
                            bean.addCustructorArgs(constructorArg);
                        } else if (SpringImplementationConstants.REF_ELEMENT.equals(qname)) {
                          String ref = reader.getAttributeValue(null, "bean");                           
                            // Check if the parent element is a property
                            if (property != null) property.addRef(ref);
                            // Check if the parent element is a constructor-arg
                            if (constructorArg != null) constructorArg.addRef(ref);
                        } else if (SpringImplementationConstants.VALUE_ELEMENT.equals(qname)) {
                            String value = reader.getElementText();
                            // Check if the parent element is a constructor-arg
                            if (constructorArg != null) constructorArg.addValue(value);
                        } else if (SpringImplementationConstants.LIST_ELEMENT.equals(qname) ||
                               SpringImplementationConstants.SET_ELEMENT.equals(qname||
                               SpringImplementationConstants.MAP_ELEMENT.equals(qname)) {                         
                          if (property != null)
                            readCollections(reader, bean, beans, property, null);
                            if (constructorArg != null)
                              readCollections(reader, bean, beans, null, constructorArg);
                        } // end if
                        break;
                    case END_ELEMENT:
                        if (SpringImplementationConstants.BEAN_ELEMENT.equals(reader.getName())) {
                            completed = true;
                            break;
                        } else if (SpringImplementationConstants.PROPERTY_ELEMENT.equals(reader.getName())) {
                            property = null;
                        } else if (SpringImplementationConstants.CONSTRUCTORARG_ELEMENT.equals(reader.getName())) {
                            constructorArg = null;
                        } // end if
                } // end switch
            } // end while
        } catch (XMLStreamException e) {
            throw new ContributionReadException(e);
        }
    }
   
   
    /**
     * Method which reads the collection elements from Spring application-context.xml file and identifies
     * the defined beans, list, maps and sets
     */
    private void readCollections(XMLStreamReader reader,
                                 SpringBeanElement bean,
                                 List<SpringBeanElement> beans,
                                 SpringPropertyElement property,
                                 SpringConstructorArgElement constructorArg) throws ContributionReadException {
     
      SpringBeanElement innerbean = null;
       
        try {
            boolean completed = false;           
            while (!completed) {
                switch (reader.next()) {
                    case START_ELEMENT:
                        QName qname = reader.getName();
                        if (SpringImplementationConstants.BEAN_ELEMENT.equals(qname)) {
                          innerbean = new SpringBeanElement(reader.getAttributeValue(null, "id"), reader
                                    .getAttributeValue(null, "class"));
                          // Set the first name as bean name, when the @id attribute is absent.
                            if (reader.getAttributeValue(null, "id") == null)
                              if (reader.getAttributeValue(null, "name") != null) {
                                String[] names = (reader.getAttributeValue(null, "name")).split(",");
                                innerbean.setId(names[0]);
                              }
                            innerbean.setInnerBean(true);
                            beans.add(innerbean);
                            readBeanDefinition(reader, innerbean, beans);
                        } else if (SpringImplementationConstants.REF_ELEMENT.equals(qname)) {
                          String ref = reader.getAttributeValue(null, "bean");
                            if (property != null) property.addRef(ref);
                            if (constructorArg != null) constructorArg.addRef(ref);                           
                        } else if (SpringImplementationConstants.LIST_ELEMENT.equals(qname) ||
                               SpringImplementationConstants.SET_ELEMENT.equals(qname) ||
                               SpringImplementationConstants.MAP_ELEMENT.equals(qname)) {
                          if (property != null)
                            readCollections(reader, innerbean, beans, property, null);
                          if (constructorArg != null)
                              readCollections(reader, innerbean, beans, null, constructorArg);                         
                        } else if (SpringImplementationConstants.ENTRY_ELEMENT.equals(qname)) {
                            String keyRef = reader.getAttributeValue(null, "key-ref");
                            String valueRef = reader.getAttributeValue(null, "value-ref");
                            if (property != null) {property.addRef(keyRef); property.addRef(valueRef);}
                            if (constructorArg != null) {constructorArg.addRef(keyRef); constructorArg.addRef(valueRef);}
                        } // end if
                        break;
                    case END_ELEMENT:
                        if (SpringImplementationConstants.LIST_ELEMENT.equals(reader.getName())) {
                          completed = true;
                            break;
                        } else if (SpringImplementationConstants.SET_ELEMENT.equals(reader.getName())) {
                          completed = true;
                            break;
                        } else if (SpringImplementationConstants.MAP_ELEMENT.equals(reader.getName())) {
                          completed = true;
                            break;
                        } // end if
                } // end switch
            } // end while
        } catch (XMLStreamException e) {
            throw new ContributionReadException(e);
        }
    }

    /**
     * Generates the Spring implementation component type from the configuration contained in the
     * lists of beans, services, references and scaproperties derived from the application context
     */
    private void generateComponentType(SpringImplementation implementation,
                                       ModelResolver resolver,
                                       List<SpringBeanElement> beans,
                                       List<SpringSCAServiceElement> services,
                                       List<SpringSCAReferenceElement> references,
                                       List<SpringSCAPropertyElement> scaproperties,
                                       ProcessorContext context) throws ContributionReadException {
        /*
         * 1. Each sca:service becomes a service in the component type
         * 2. Each sca:reference becomes a reference in the component type
         * 3. Each sca:property becomes a property in the component type
         * 4. IF there are no explicit service elements, each bean becomes a service
         * 5. Each bean property which is a reference not pointing at another bean in the
         *    application context becomes a reference unless it is pointing at one of the references
         * 6. Each bean property which is not a reference and which is not pointing
         *    at another bean in the application context becomes a property in the component type
         */

      JavaImplementation javaImplementation = null;
        ComponentType componentType = implementation.getComponentType();

        try {
            // Deal with the services first....
            Iterator<SpringSCAServiceElement> its = services.iterator();
            while (its.hasNext()) {
                SpringSCAServiceElement serviceElement = its.next();               
                Class<?> interfaze = resolveClass(resolver, serviceElement.getType(), context);
                Service theService = createService(interfaze, serviceElement.getName());
                // Spring allows duplication of bean definitions in multiple context scenario,
                // in such cases, the latest bean definition overrides the older ones, hence
                // we will remove any older definition and use the latest.
                Service duplicate = null;
                for (Service service : componentType.getServices()) {
                  if (service.getName().equals(theService.getName()))
                    duplicate = service;
                }
                if (duplicate != null)
                  componentType.getServices().remove(duplicate);
               
                componentType.getServices().add(theService);
                // Add this service to the Service / Bean map
                String beanName = serviceElement.getTarget();
                for (SpringBeanElement beanElement : beans) {
                    if (beanName.equals(beanElement.getId())) {
                      if (isvalidBeanForService(beanElement)) {
                        // add the required intents and policySets for the service
                            theService.getRequiredIntents().addAll(serviceElement.getRequiredIntents());
                            theService.getPolicySets().addAll(serviceElement.getPolicySets());
                            implementation.setBeanForService(theService, beanElement);
                      }
                    }
                } // end for
            } // end while

            // Next handle the references
            Iterator<SpringSCAReferenceElement> itr = references.iterator();
            while (itr.hasNext()) {
                SpringSCAReferenceElement referenceElement = itr.next();
                Class<?> interfaze = resolveClass(resolver, referenceElement.getType(), context);
                Reference theReference = createReference(interfaze, referenceElement.getName());
                // Override the older bean definition with the latest ones
                // for the duplicate definitions found.
                Reference duplicate = null;
                for (Reference reference : componentType.getReferences()) {
                  if (reference.getName().equals(theReference.getName()))
                    duplicate = reference;
                }
                if (duplicate != null)
                  componentType.getReferences().remove(duplicate);
               
                // add the required intents and policySets for this reference
                theReference.getRequiredIntents().addAll(referenceElement.getRequiredIntents());
                theReference.getPolicySets().addAll(referenceElement.getPolicySets());
                componentType.getReferences().add(theReference);
            } // end while

            // Next handle the properties
            Iterator<SpringSCAPropertyElement> itsp = scaproperties.iterator();
            while (itsp.hasNext()) {
                SpringSCAPropertyElement scaproperty = itsp.next();
                // Create a component type property if the SCA property element has a name
                // and a type declared...
                if (scaproperty.getType() != null && scaproperty.getName() != null) {
                    Property theProperty = assemblyFactory.createProperty();
                    theProperty.setName(scaproperty.getName());
                    // Get the Java class and then an XSD element type for the property
                    Class<?> propType = Class.forName(scaproperty.getType());
                    theProperty.setXSDType(JavaXMLMapper.getXMLType(propType));
                    // Override the older bean definition with the latest ones
                    // for the duplicate definitions found.
                    Property duplicate = null;
                    for (Property property : componentType.getProperties()) {
                      if (property.getName().equals(theProperty.getName()))
                        duplicate = property;
                    }
                    if (duplicate != null)
                      componentType.getProperties().remove(duplicate);
                   
                    componentType.getProperties().add(theProperty);
                    // Remember the Java Class (ie the type) for this property
                    implementation.setPropertyClass(theProperty.getName(), propType);
                } // end if
            } // end while

            // Finally deal with the beans
            Iterator<SpringBeanElement> itb;
            // If there are no explicit service elements, then expose all the beans
            if (services.isEmpty()) {
                itb = beans.iterator();
                // Loop through all the beans found
                while (itb.hasNext()) {
                    SpringBeanElement beanElement = itb.next();
                    // If its not a valid bean for service, ignore it
                    if (!isvalidBeanForService(beanElement)) continue;
                    // Load the Spring bean class
                    Class<?> beanClass = resolveClass(resolver, beanElement.getClassName(), context);
                    // Introspect the bean
                    beanIntrospector =
                        new SpringBeanIntrospector(registry, beanElement.getCustructorArgs());
                    ComponentType beanComponentType = assemblyFactory.createComponentType();
                    javaImplementation = beanIntrospector.introspectBean(beanClass, beanComponentType);
                    // Set the service name as bean name
                    for (Service componentService : beanComponentType.getServices())                     
                      componentService.setName(beanElement.getId());
                    // Get the service interface defined by this Spring Bean and add to
                    // the component type of the Spring Assembly
                    List<Service> beanServices = beanComponentType.getServices();
                    componentType.getServices().addAll(beanServices);
                    // Add these services to the Service / Bean map
                    for (Service beanService : beanServices) {
                        implementation.setBeanForService(beanService, beanElement);
                    }
                } // end while
            } // end if

            itb = beans.iterator();
            while (itb.hasNext()) {
                SpringBeanElement beanElement = itb.next();
                // Ignore if the bean has no properties and constructor arguments
                if (beanElement.getProperties().isEmpty() && beanElement.getCustructorArgs().isEmpty())
                  continue;

                Class<?> beanClass = resolveClass(resolver, beanElement.getClassName(), context);
                // Introspect the bean
                beanIntrospector =
                    new SpringBeanIntrospector(registry, beanElement.getCustructorArgs());
                ComponentType beanComponentType = assemblyFactory.createComponentType();
                javaImplementation = beanIntrospector.introspectBean(beanClass, beanComponentType);
                Map<String, JavaElementImpl> propertyMap = javaImplementation.getPropertyMembers();
                JavaConstructorImpl constructor = javaImplementation.getConstructor();
                // Get the references by this Spring Bean and add the unresolved ones to
                // the component type of the Spring Assembly
                List<Reference> beanReferences = beanComponentType.getReferences();
                List<Property> beanProperties = beanComponentType.getProperties();

                Iterator<SpringPropertyElement> itp = beanElement.getProperties().iterator();
                while (itp.hasNext()) {
                    SpringPropertyElement propertyElement = itp.next();
                    for (String propertyRef : propertyElement.getRefs()) {
                      if (propertyRefUnresolved(propertyRef, beans, references, scaproperties)) {
                          // This means an unresolved reference from the spring bean...
                        for (Reference reference : beanReferences) {
                              if (propertyElement.getName().equals(reference.getName())) {
                                // The name of the reference in this case is the string in
                                  // the @ref attribute of the Spring property element, NOT the
                                  // name of the field in the Spring bean....
                                  reference.setName(propertyRef);
                                  componentType.getReferences().add(reference);
                              } // end if
                          } // end for
 
                        // Store the unresolved references as unresolvedBeanRef in the Spring Implementation type
                        for (Property scaproperty : beanProperties) {
                              if (propertyElement.getName().equals(scaproperty.getName())) {
                                // The name of the reference in this case is the string in
                                  // the @ref attribute of the Spring property element, NOT the
                                  // name of the field in the Spring bean....
                                Class<?> interfaze = resolveClass(resolver, (propertyMap.get(propertyElement.getName()).getType()).getName(), context);
                                  Reference theReference = createReference(interfaze, propertyRef);
                                  implementation.setUnresolvedBeanRef(propertyRef, theReference);
                              } // end if
                        } // end for
                      } // end if
                    } // end for
                } // end while

                Iterator<SpringConstructorArgElement> itcr = beanElement.getCustructorArgs().iterator();
                while (itcr.hasNext()) {
                  SpringConstructorArgElement conArgElement = itcr.next();
                  for (String constructorArgRef : conArgElement.getRefs()) {
                    if (propertyRefUnresolved(constructorArgRef, beans, references, scaproperties)) {
                        for (JavaParameterImpl parameter : constructor.getParameters()) {
                          String paramType = parameter.getType().getName();
                          Class<?> interfaze = resolveClass(resolver, paramType, context);
                          // Create a component type reference/property if the constructor-arg element has a
                              // type attribute OR index attribute declared...
                          if ((conArgElement.getType() != null && paramType.equals(conArgElement.getType())) ||
                              (conArgElement.getIndex() != -1 && (conArgElement.getIndex() == parameter.getIndex())))
                          {
                            if (parameter.getClassifer().getName().equals("org.osoa.sca.annotations.Reference")) {
                              Reference theReference = createReference(interfaze, constructorArgRef);
                              componentType.getReferences().add(theReference);
                            }
                            if (parameter.getClassifer().getName().equals("org.osoa.sca.annotations.Property")) {
                              // Store the unresolved references as unresolvedBeanRef in the Spring Implementation type
                                    // we might need to verify with the component definition later.
                              Reference theReference = createReference(interfaze, constructorArgRef);
                                implementation.setUnresolvedBeanRef(constructorArgRef, theReference);
                            }
                          }
                        } // end for
                      } // end if
                  } // end for
                } // end while

            } // end while

        } catch (ClassNotFoundException e) {
            // Means that either an interface class, property class or a bean was not found
            throw new ContributionReadException(e);
        } catch (InvalidInterfaceException e) {
            throw new ContributionReadException(e);
        } catch (ContributionResolveException e) {

        } // end try

        // If we get here, the Spring assembly component type is resolved
        componentType.setUnresolved(false);
        implementation.setComponentType(componentType);
        return;
    } // end method generateComponentType

    /*
     * Determines whether a reference attribute of a Spring property element is resolved either
     * by a bean in the application context or by an SCA reference element or by an SCA property
     * element
     * @param ref - a String containing the name of the reference - may be null
     * @param beans - a List of SpringBean elements
     * @param references - a List of SCA reference elements
     * @return true if the property is not resolved, false if it is resolved
     */
    private boolean propertyRefUnresolved(String ref,
                                          List<SpringBeanElement> beans,
                                          List<SpringSCAReferenceElement> references,
                                          List<SpringSCAPropertyElement> scaproperties) {
        boolean unresolved = true;

        if (ref != null) {
            // Scan over the beans looking for a match
            Iterator<SpringBeanElement> itb = beans.iterator();
            while (itb.hasNext()) {
                SpringBeanElement beanElement = itb.next();
                // Does the bean name match the ref?
                if (ref.equals(beanElement.getId())) {
                    unresolved = false;
                    break;
                } // end if
            } // end while
            // Scan over the SCA reference elements looking for a match
            if (unresolved) {
                Iterator<SpringSCAReferenceElement> itr = references.iterator();
                while (itr.hasNext()) {
                    SpringSCAReferenceElement referenceElement = itr.next();
                    if (ref.equals(referenceElement.getName())) {
                        unresolved = false;
                        break;
                    } // end if
                } // end while
            } // end if
            // Scan over the SCA property elements looking for a match
            if (unresolved) {
                Iterator<SpringSCAPropertyElement> itsp = scaproperties.iterator();
                while (itsp.hasNext()) {
                    SpringSCAPropertyElement propertyElement = itsp.next();
                    if (ref.equals(propertyElement.getName())) {
                        unresolved = false;
                        break;
                    } // end if
                } // end while
            } // end if
        } else {
            // In the case where ref = null, the property is not going to be a reference of any
            // kind and can be ignored
            unresolved = false;
        } // end if

        return unresolved;

    } // end method propertyRefUnresolved
   
    /**
     * Validates whether the <sca:service>, <sca:reference> and <sca:property> elements
     * has unique names within the application context.
     */
    private void validateBeans(List<SpringBeanElement> beans,
                       List<SpringSCAServiceElement> services,
                       List<SpringSCAReferenceElement> references,
                       List<SpringSCAPropertyElement> scaproperties) throws ContributionReadException {
             
      // The @target attribute of a <service/> subelement of a <beans/> element
      // MUST have the value of the @name attribute of one of the <bean/>
      // subelements of the <beans/> element.     
      Iterator<SpringSCAServiceElement> its = services.iterator();
        while (its.hasNext()) {
          SpringSCAServiceElement serviceElement = its.next();
          boolean targetBeanExists = false;
          Iterator<SpringBeanElement> itb = beans.iterator();
          while (itb.hasNext()) {
            SpringBeanElement beanElement = itb.next();
            if (serviceElement.getTarget().equals(beanElement.getId()))
              targetBeanExists = true;
          }
          if (!targetBeanExists)
            error("TargetBeanDoesNotExist", beans);
        } // end while
       
      // The value of the @name attribute of an <sca:reference/> subelement of a <beans/>
      // element MUST be unique amongst the @name attributes of the <sca:property/>
        // subelements and the <bean/> subelements of the <beans/> element.
        //                   AND
        // The @default attribute of a <sca:reference/> subelement of a <beans/> 
        // element MUST have the value of the @name attribute of one of the <bean/>
        // subelements of the <beans/> element.
        Iterator<SpringSCAReferenceElement> itr = references.iterator();
        while (itr.hasNext()) {
          SpringSCAReferenceElement referenceElement = itr.next();
          boolean defaultBeanExists = true;
          boolean isUniqueReferenceName = true;
          Iterator<SpringBeanElement> itb = beans.iterator();
          while (itb.hasNext()) {
            SpringBeanElement beanElement = itb.next();
            if (referenceElement.getDefaultBean() != null)
              if (referenceElement.getDefaultBean().equals(beanElement.getId()))
                defaultBeanExists = false;
            if (referenceElement.getName().equals(beanElement.getId()))
              isUniqueReferenceName = false;
          }
          Iterator<SpringSCAPropertyElement> itp = scaproperties.iterator();
          while (itp.hasNext()) {
            SpringSCAPropertyElement propertyElement = itp.next();
            if (referenceElement.getName().equals(propertyElement.getName()))
              isUniqueReferenceName = false;
          }
          if (!defaultBeanExists)
            error("DefaultBeanDoesNotExist", beans);
          if (!isUniqueReferenceName)
            error("ScaReferenceNameNotUnique", beans);
        } // end while
             
      // The value of the @name attribute of an <sca:property/> subelement of a <beans/>
      // element MUST be unique amongst the @name attributes of the <sca:reference/>
        // subelements and the <bean/> subelements of the <beans/> element.     
        Iterator<SpringSCAPropertyElement> itp = scaproperties.iterator();
        while (itp.hasNext()) {
          SpringSCAPropertyElement propertyElement = itp.next();
          boolean isUniquePropertyName = true;
          Iterator<SpringBeanElement> itb = beans.iterator();
          while (itb.hasNext()) {
            SpringBeanElement beanElement = itb.next();
            if (propertyElement.getName().equals(beanElement.getId()))
              isUniquePropertyName = false
          }
          Iterator<SpringSCAReferenceElement> itrp = references.iterator();
            while (itrp.hasNext()) {
              SpringSCAReferenceElement referenceElement = itrp.next();
              if (propertyElement.getName().equals(referenceElement.getName()))
              isUniquePropertyName = false;
            }
          if (!isUniquePropertyName)
            error("ScaPropertyNameNotUnique", beans);
        } // end while
    }
   
    /**
     * Validates whether a bean definition is valid for exposing as service.
     */
    private boolean isvalidBeanForService(SpringBeanElement beanElement) {
     
      if (beanElement.isInnerBean())
        return false;
      if (beanElement.hasParentAttribute())
        return false;
      if (beanElement.hasFactoryMethodAttribute())
        return false;
      if (beanElement.hasFactoryBeanAttribute())
        return false;
      if (beanElement.getClassName() == null)
        return false;
      if (beanElement.getClassName().startsWith("org.springframework"))
        return false;
        // return true otherwise 
      return true;
    }
   
   
    /**
     * Gets hold of the application-context.xml file as a Spring resource
     * @param locationAttr - the location attribute from the <implementation.spring../> element
     * @param cl - the ClassLoader for the Spring implementation
     */
    protected List<URL> getApplicationContextResource(URL url)
        throws ContributionReadException {
        File manifestFile = null;
        File appXmlFile;
        File appXmlFolder;
        File locationFile = null;
        List<URL> appCtxResources = new ArrayList<URL>();
       
        if (url != null) {
            String path = url.getPath();
            locationFile = new File(path);
        } else {
            throw new ContributionReadException("SpringXMLComponentTypeLoader getApplicationContextResource: "
                              + "unable to find resource file " + url);
        }

        if (locationFile.isDirectory()) {
            try {
                manifestFile = new File(locationFile, "META-INF"+ File.separator +"MANIFEST.MF");
                if (manifestFile.exists()) {
                    Manifest mf = new Manifest(new FileInputStream(manifestFile));
                    Attributes mainAttrs = mf.getMainAttributes();
                    String appCtxPath = mainAttrs.getValue("Spring-Context");
                    if (appCtxPath != null) {
                      String[] cxtPaths = appCtxPath.split(";");
                  for (String path : cxtPaths) {
                    appXmlFile = new File(locationFile, path.trim());
                    if (appXmlFile.exists()) {
                      appCtxResources.add(appXmlFile.toURI().toURL());
                    }
                  }
                  return appCtxResources;
                    }
                }               
                // No MANIFEST.MF file OR no manifest-specified Spring context , then read all the
                // xml files available in the META-INF/spring folder.
                appXmlFolder = new File(locationFile, "META-INF" + File.separator + "spring");
                if (appXmlFolder.exists()) {
                  File[] files = appXmlFolder.listFiles();
                  for (File appFile: files) {
                    if (appFile.getName().endsWith(".xml")) {
                      appCtxResources.add(appFile.toURI().toURL());
                    }
                  }
                  return appCtxResources;
                }
            } catch (IOException e) {
                throw new ContributionReadException("Error reading manifest " + manifestFile);
            }
        } else {
          if (locationFile.isFile() && locationFile.getName().endsWith(".jar")) {
                try {
                    JarFile jf = new JarFile(locationFile);                   
                    JarEntry je;
                    Manifest mf = jf.getManifest();
                    if (mf != null) {
                        Attributes mainAttrs = mf.getMainAttributes();
                        String appCtxPath = mainAttrs.getValue("Spring-Context");
                        if (appCtxPath != null) {
                          String[] cxtPaths = appCtxPath.split(";");
                      for (String path : cxtPaths) {
                        je = jf.getJarEntry(path.trim());
                        if (je != null)
                          appCtxResources.add(new URL("jar:" + locationFile.toURI().toURL() + "!/" + appCtxPath));
                      }
                      return appCtxResources;
                        }
                    }
                    // No MANIFEST.MF file OR no manifest-specified Spring context , then read all the
                    // .xml files available in the META-INF/spring folder.
                    Enumeration<JarEntry> entries = jf.entries();
                    while (entries.hasMoreElements()) {
                      je = entries.nextElement();
                      if (je.getName().startsWith("META-INF/spring/") && je.getName().endsWith(".xml")) {
                        appCtxResources.add(new URL("jar:" + locationFile.toURI().toURL() + "!/" + je.getName()));
                      }
                    }
                    return appCtxResources;
                } catch (IOException e) {
                    // TODO: create a more appropriate exception type
                    throw new ContributionReadException("SpringXMLComponentTypeLoader getApplicationContextResource: "
                                            + " IO exception reading context file.", e);
                }
          }
          else {
            if (locationFile.getName().endsWith(".xml")) {
              appCtxResources.add(url);
                  return appCtxResources;
            }
            else {
              // Deal with the directory inside a jar file, in case the contribution itself is a JAR file.
              try {
                if (locationFile.getPath().indexOf(".jar") > 0) {
                  String jarPath = url.getPath().substring(5, url.getPath().indexOf("!"));
                  JarFile jf = new JarFile(jarPath);
                  JarEntry je = jf.getJarEntry(url.getPath().substring(url.getPath().indexOf("!/")+2)
                                  + "/" + "META-INF" + "/" + "MANIFEST.MF");
                    if (je != null) {
                    Manifest mf = new Manifest(jf.getInputStream(je));
                    Attributes mainAttrs = mf.getMainAttributes();
                              String appCtxPath = mainAttrs.getValue("Spring-Context");
                              if (appCtxPath != null) {
                                String[] cxtPaths = appCtxPath.split(";");
                            for (String path : cxtPaths) {
                                    je = jf.getJarEntry(url.getPath().substring(url.getPath().indexOf("!/")+2) + "/" + path.trim());
                                    if (je != null) {
                                      appCtxResources.add(new URL("jar:" + url.getPath() + "/" + path.trim()));                                   
                                    }
                            }
                            return appCtxResources;
                              }
                  }                   
                    // No MANIFEST.MF file OR no manifest-specified Spring context , then read all the
                          // .xml files available in the META-INF/spring folder.
                    Enumeration<JarEntry> entries = jf.entries();
                          while (entries.hasMoreElements()) {
                            je = entries.nextElement();
                            if (je.getName().startsWith("META-INF/spring/") && je.getName().endsWith(".xml")) {
                              appCtxResources.add(new URL("jar:" + url.getPath() + "/" + je.getName()));
                            }
                          }
                            return appCtxResources;
                }
                } catch (IOException e) {
                        throw new ContributionReadException("Error reading manifest " + manifestFile);
                    }
            }
          }
        }

        throw new ContributionReadException("SpringXMLComponentTypeLoader getApplicationContextResource: "
                                        + "unable to read resource file " + url);
    } // end method getApplicationContextResource

    /**
     * Creates a Service for the component type based on its name and Java interface
     */
    public Service createService(Class<?> interfaze, String name) throws InvalidInterfaceException {
        Service service = assemblyFactory.createService();
        JavaInterfaceContract interfaceContract = javaFactory.createJavaInterfaceContract();
        service.setInterfaceContract(interfaceContract);

        // Set the name for the service
        service.setName(name);

        // Set the call interface and, if present, the callback interface
        JavaInterface callInterface = javaFactory.createJavaInterface(interfaze);
        service.getInterfaceContract().setInterface(callInterface);
        if (callInterface.getCallbackClass() != null) {
            JavaInterface callbackInterface = javaFactory.createJavaInterface(callInterface.getCallbackClass());
            service.getInterfaceContract().setCallbackInterface(callbackInterface);
        }
        return service;
    } // end method createService

    /**
     * Creates a Reference for the component type based on its name and Java interface
     */
    private org.apache.tuscany.sca.assembly.Reference createReference(Class<?> interfaze, String name)
        throws InvalidInterfaceException {
        org.apache.tuscany.sca.assembly.Reference reference = assemblyFactory.createReference();
        JavaInterfaceContract interfaceContract = javaFactory.createJavaInterfaceContract();
        reference.setInterfaceContract(interfaceContract);

        // Set the name of the reference to the supplied name and the multiplicity of the reference
        // to 1..1 - for Spring implementations, this is the only multiplicity supported
        reference.setName(name);
        reference.setMultiplicity(Multiplicity.ONE_ONE);

        // Set the call interface and, if present, the callback interface
        JavaInterface callInterface = javaFactory.createJavaInterface(interfaze);
        reference.getInterfaceContract().setInterface(callInterface);
        if (callInterface.getCallbackClass() != null) {
            JavaInterface callbackInterface = javaFactory.createJavaInterface(callInterface.getCallbackClass());
            reference.getInterfaceContract().setCallbackInterface(callbackInterface);
        }

        return reference;
    }
   
    private class ContextClassLoader extends ClassLoader {
      public ContextClassLoader(ModelResolver resolver, ProcessorContext context) {
        super();
        this.resolver = resolver;
        this.context = context;
      }

    private ModelResolver resolver;
    private ProcessorContext context;
   
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
      return SpringXMLComponentTypeLoader.this.resolveClass(resolver, name, context);
    }
   
    @Override
    protected URL findResource(String name) {
      try {
        return resolveLocation(resolver, name, context);
      } catch (Exception e) {
        return null;
      }
    }
   
    @Override
    protected Enumeration<URL> findResources(String name) throws IOException {
      URL url = findResource(name);
      if (url != null) {
        return Collections.enumeration(Arrays.asList(url));
      } else {
        Collection<URL> urls = Collections.emptyList();
        return Collections.enumeration(urls);
      }
    }
    }
} // end class SpringXMLComponentTypeLoader
TOP

Related Classes of org.apache.tuscany.sca.implementation.spring.introspect.SpringXMLComponentTypeLoader

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.