From 132aa8a77685ec92bc90c03f987650d275a7b639 Mon Sep 17 00:00:00 2001 From: lresende Date: Mon, 30 Sep 2013 06:59:11 +0000 Subject: 2.0.1 RC1 release tag git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1527464 13f79535-47bb-0310-9956-ffa450edef68 --- .../sca/builder/impl/ComponentBuilderImpl.java | 1735 ++++++++++++++++++++ 1 file changed, 1735 insertions(+) create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/builder/src/main/java/org/apache/tuscany/sca/builder/impl/ComponentBuilderImpl.java (limited to 'sca-java-2.x/tags/2.0.1-RC1/modules/builder/src/main/java/org/apache/tuscany/sca/builder/impl/ComponentBuilderImpl.java') diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/builder/src/main/java/org/apache/tuscany/sca/builder/impl/ComponentBuilderImpl.java b/sca-java-2.x/tags/2.0.1-RC1/modules/builder/src/main/java/org/apache/tuscany/sca/builder/impl/ComponentBuilderImpl.java new file mode 100644 index 0000000000..d67cb65bb0 --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/builder/src/main/java/org/apache/tuscany/sca/builder/impl/ComponentBuilderImpl.java @@ -0,0 +1,1735 @@ +/* + * 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.builder.impl; + +import java.io.InputStream; +import java.io.StringReader; +import java.net.URI; +import java.net.URL; +import java.net.URLConnection; +import java.util.List; + +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Source; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.Validator; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; + +import org.apache.tuscany.sca.assembly.AssemblyFactory; +import org.apache.tuscany.sca.assembly.Binding; +import org.apache.tuscany.sca.assembly.Component; +import org.apache.tuscany.sca.assembly.ComponentProperty; +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.CompositeReference; +import org.apache.tuscany.sca.assembly.CompositeService; +import org.apache.tuscany.sca.assembly.Contract; +import org.apache.tuscany.sca.assembly.Implementation; +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.SCABinding; +import org.apache.tuscany.sca.assembly.SCABindingFactory; +import org.apache.tuscany.sca.assembly.Service; +import org.apache.tuscany.sca.assembly.builder.BuilderContext; +import org.apache.tuscany.sca.assembly.builder.BuilderExtensionPoint; +import org.apache.tuscany.sca.assembly.builder.ContractBuilder; +import org.apache.tuscany.sca.assembly.builder.ImplementationBuilder; +import org.apache.tuscany.sca.assembly.builder.Messages; +import org.apache.tuscany.sca.assembly.xsd.Constants; +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.databinding.Mediator; +import org.apache.tuscany.sca.databinding.impl.MediatorImpl; +import org.apache.tuscany.sca.databinding.jaxb.JAXBDataBinding; +import org.apache.tuscany.sca.databinding.xml.DOMDataBinding; +import org.apache.tuscany.sca.definitions.Definitions; +import org.apache.tuscany.sca.interfacedef.Compatibility; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.IncompatibleInterfaceContractException; +import org.apache.tuscany.sca.interfacedef.InterfaceContract; +import org.apache.tuscany.sca.interfacedef.InterfaceContractMapper; +import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; +import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceContract; +import org.apache.tuscany.sca.interfacedef.util.JavaXMLMapper; +import org.apache.tuscany.sca.interfacedef.util.XMLType; +import org.apache.tuscany.sca.monitor.Monitor; +import org.apache.tuscany.sca.policy.ExtensionType; +import org.apache.tuscany.sca.policy.PolicySubject; +import org.apache.tuscany.sca.xsd.XSDefinition; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +/** + * @version $Rev$ $Date$ + */ +public class ComponentBuilderImpl { + protected static final String SCA11_NS = "http://docs.oasis-open.org/ns/opencsa/sca/200912"; + protected static final String BINDING_SCA = "binding.sca"; + protected static final QName BINDING_SCA_QNAME = new QName(SCA11_NS, BINDING_SCA); + + private CompositeComponentTypeBuilderImpl componentTypeBuilder; + protected ComponentPolicyBuilderImpl policyBuilder; + private AssemblyFactory assemblyFactory; + private SCABindingFactory scaBindingFactory; + private DocumentBuilderFactory documentBuilderFactory; + protected TransformerFactory transformerFactory; + private InterfaceContractMapper interfaceContractMapper; + private BuilderExtensionPoint builders; + private Mediator mediator; + private ContractBuilder contractBuilder; + + public ComponentBuilderImpl(ExtensionPointRegistry registry) { + UtilityExtensionPoint utilities = registry.getExtensionPoint(UtilityExtensionPoint.class); + + FactoryExtensionPoint modelFactories = registry.getExtensionPoint(FactoryExtensionPoint.class); + assemblyFactory = modelFactories.getFactory(AssemblyFactory.class); + scaBindingFactory = modelFactories.getFactory(SCABindingFactory.class); + documentBuilderFactory = modelFactories.getFactory(DocumentBuilderFactory.class); + transformerFactory = modelFactories.getFactory(TransformerFactory.class); + + interfaceContractMapper = utilities.getUtility(InterfaceContractMapper.class); + policyBuilder = new ComponentPolicyBuilderImpl(registry); + builders = registry.getExtensionPoint(BuilderExtensionPoint.class); + mediator = new MediatorImpl(registry); + contractBuilder = builders.getContractBuilder(); + } + + public void setComponentTypeBuilder(CompositeComponentTypeBuilderImpl componentTypeBuilder) { + this.componentTypeBuilder = componentTypeBuilder; + } + + /** + * Configure the component based on its component type using OASIS rules + * + * @Param outerCompoment the component that uses the parentComposite as its implementation + * @Param parentComposite the composite that contains the component being configured. Required for property processing + * @param component the component to be configured + */ + public void configureComponentFromComponentType(Component outerComponent, Composite parentComposite, Component component, BuilderContext context) { + + Monitor monitor = context.getMonitor(); + monitor.pushContext("Component: " + component.getName().toString()); + + try { + // do any work we need to do before we calculate the component type + // for this component. Anything that needs to be pushed down the promotion + // hierarchy must be done before we calculate the component type + + // check that the implementation is present + if (!isComponentImplementationPresent(component, monitor)){ + return; + } + + // carry out any implementation specific builder processing + Implementation impl = component.getImplementation(); + if (impl != null) { + ImplementationBuilder builder = builders.getImplementationBuilder(impl.getType()); + if (builder != null) { + builder.build(component, impl, context); + } + } + + // Properties on the composite component type are not affected by the components + // that the composite contains. Instead the child components might source + // composite level property values. Hence we have to calculate whether the component + // type property value should be overridden by this component's property value + // before we go ahead and calculate the component type + configureProperties(outerComponent, parentComposite, component, monitor); + + // create the component type for this component + // taking any nested composites into account + createComponentType(component, context); + + // configure services based on the calculated component type + configureServices(component, context); + + // configure services based on the calculated component type + configureReferences(component, context); + + // NOTE: configureServices/configureReferences may add callback references and services + + // inherit the intents and policy sets from the component type + policyBuilder.configure(component, context); + + } finally { + monitor.popContext(); + } + } + + /** + * Checks that a component implementation is present and resolved + * before doing anything else + * + * @param component + * @return true if the implementation is present and resolved + */ + private boolean isComponentImplementationPresent(Component component, Monitor monitor){ + Implementation implementation = component.getImplementation(); + if (implementation == null) { + // A component must have an implementation + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "NoComponentImplementation", + component.getName()); + return false; + } else if (implementation.isUnresolved()) { + // The implementation must be fully resolved + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "UnresolvedComponentImplementation", + component, + component.getName(), + implementation.getURI()); + return false; + } + + return true; + } + + /** + * Use the component type builder to build the component type for + * this component. + * + * @param component + */ + private void createComponentType(Component component, BuilderContext context) { + Implementation implementation = component.getImplementation(); + if (implementation instanceof Composite) { + componentTypeBuilder.createComponentType(component, (Composite)implementation, context); + } + } + + /** + * Configure this component's services based on the services in its + * component type and the configuration from the composite file + * + * @param component + */ + private void configureServices(Component component, BuilderContext context) { + Monitor monitor = context.getMonitor(); + + // If the component type has services that are not described in this + // component then create services for this component + addServicesFromComponentType(component, monitor); + + // Connect this component's services to the + // services from its component type + connectServicesToComponentType(component, monitor); + + // look at each component service in turn and calculate its + // configuration based on OASIS rules + for (ComponentService componentService : component.getServices()) { + + Service componentTypeService = componentService.getService(); + + if (componentTypeService == null) { + // raise error? + // can be null in some of the assembly-xml unit tests + continue; + } + + // interface contracts + calculateServiceInterfaceContract(component, componentService, componentTypeService, context); + + // bindings + calculateBindings(component, componentService, componentTypeService, context); + + // add callback reference model objects + createCallbackReference(component, componentService, monitor); + } + } + + /** + * Configure this component's references based on the references in its + * component type and the configuration from the composite file + * + * @param component + */ + private void configureReferences(Component component, BuilderContext context) { + Monitor monitor = context.getMonitor(); + + // If the component type has references that are not described in this + // component then create references for this component + addReferencesFromComponentType(component, monitor); + + // Connect this component's references to the + // references from its component type + connectReferencesToComponentType(component, monitor); + + // look at each component reference in turn and calculate its + // configuration based on OASIS rules + for (ComponentReference componentReference : component.getReferences()) { + Reference componentTypeReference = componentReference.getReference(); + + if (componentTypeReference == null) { + // raise error? + // can be null in some of the assembly-xml unit tests + continue; + } + + // reference multiplicity + reconcileReferenceMultiplicity(component, componentReference, componentTypeReference, monitor); + + // interface contracts + calculateReferenceInterfaceContract(component, componentReference, componentTypeReference, context); + + // bindings + calculateBindings(componentReference, componentTypeReference); + + // add callback service model objects + createCallbackService(component, componentReference, monitor); + + // Propagate autowire setting from the component down the structural + // hierarchy + if (componentReference.getAutowire() == null) { + componentReference.setAutowire(component.getAutowire()); + } + } + } + + /** + * Configure this component's properties based on the properties in its + * component type and the configuration from the composite file + * + * @param component + */ + private void configureProperties(Component outerComponent, Composite parentComposite, Component component, Monitor monitor) { + // If the component type has properties that are not described in this + // component then create properties for this component + addPropertiesFromComponentType(component, monitor); + + // Connect this component's properties to the + // properties from its component type + connectPropertiesToComponentType(component, monitor); + + // Reconcile component properties and their component type properties + for (ComponentProperty componentProperty : component.getProperties()) { + reconcileComponentPropertyWithComponentType(component, componentProperty, monitor); + + // configure the property value based on the @source attribute + // At the moment this is done in the parent composite component + // type calculation + processPropertySourceAttribute(outerComponent, parentComposite, component, componentProperty, monitor); + + // configure the property value based on the @file attribute + processPropertyFileAttribute(component, componentProperty, monitor); + + // Check that a type or element are specified + if (componentProperty.getXSDElement() == null && componentProperty.getXSDType() == null) { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "NoTypeForComponentProperty", + component.getName(), + componentProperty.getName()); + } + + // Check that a value is supplied + if (componentProperty.isMustSupply() && !isPropertyValueSet(componentProperty)) { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertyMustSupplyNull", + component.getName(), + componentProperty.getName()); + } + + // check that not too many values are supplied + if (!componentProperty.isMany() && isPropertyManyValued(componentProperty)){ + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertyHasManyValues", + component.getName(), + componentProperty.getName()); + } + + // check the property type + checkComponentPropertyType(component, componentProperty, monitor); + + } + } + + private void addServicesFromComponentType(Component component, Monitor monitor) { + + // Create a component service for each service + if (component.getImplementation() != null) { + for (Service service : component.getImplementation().getServices()) { + // check for duplicate service names in implementation + if (service != component.getImplementation().getService(service.getName())){ + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "DuplicateImplementationServiceName", + component.getName(), + service.getName()); + } + + ComponentService componentService = (ComponentService)component.getService(service.getName()); + + // if the component doesn't have a service with the same name as the + // component type service then create one + if (componentService == null) { + componentService = assemblyFactory.createComponentService(); + componentService.setForCallback(service.isForCallback()); + String name = service.getName(); + componentService.setName(name); + component.getServices().add(componentService); + } + } + } + } + + private void addReferencesFromComponentType(Component component, Monitor monitor) { + + // Create a component reference for each reference + if (component.getImplementation() != null) { + for (Reference reference : component.getImplementation().getReferences()) { + // check for duplicate reference names in implementation + if (reference != component.getImplementation().getReference(reference.getName())){ + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "DuplicateImplementationReferenceName", + component.getName(), + reference.getName()); + } + + ComponentReference componentReference = (ComponentReference)component.getReference(reference.getName()); + + // if the component doesn't have a reference with the same name as the + // component type reference then create one + if (componentReference == null) { + componentReference = assemblyFactory.createComponentReference(); + componentReference.setForCallback(reference.isForCallback()); + componentReference.setName(reference.getName()); + componentReference.setReference(reference); + component.getReferences().add(componentReference); + } + } + } + } + + private void addPropertiesFromComponentType(Component component, Monitor monitor) { + + // Create component property for each property + if (component.getImplementation() != null) { + for (Property property : component.getImplementation().getProperties()) { + // check for duplicate property names in implementation + if (property != component.getImplementation().getProperty(property.getName())){ + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "DuplicateImplementationPropertyName", + component.getName(), + property.getName()); + } + ComponentProperty componentProperty = (ComponentProperty)component.getProperty(property.getName()); + + // if the component doesn't have a property with the same name as + // the component type property then create one + if (componentProperty == null) { + componentProperty = assemblyFactory.createComponentProperty(); + componentProperty.setName(property.getName()); + componentProperty.setValue(property.getValue()); + componentProperty.setMany(property.isMany()); + componentProperty.setMustSupply(property.isMustSupply()); + componentProperty.setXSDElement(property.getXSDElement()); + componentProperty.setXSDType(property.getXSDType()); + componentProperty.setProperty(property); + component.getProperties().add(componentProperty); + } + } + } + } + + private void connectServicesToComponentType(Component component, Monitor monitor) { + + // Connect each component service to the corresponding component type service + for (ComponentService componentService : component.getServices()) { + // check for duplicate service names in component + if (componentService != component.getService(componentService.getName())){ + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "DuplicateComponentServiceName", + component.getName(), + componentService.getName()); + } + + if (componentService.getService() != null || componentService.isForCallback()) { + continue; + } + + if (component.getImplementation() == null) { + // is null in some of our basic unit tests + continue; + } + + Service service = component.getImplementation().getService(componentService.getName()); + + if (service != null) { + componentService.setService(service); + } else { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "ServiceNotFoundForComponentService", + component.getName(), + componentService.getName()); + } + } + } + + private void connectReferencesToComponentType(Component component, Monitor monitor) { + + // Connect each component reference to the corresponding component type reference + for (ComponentReference componentReference : component.getReferences()) { + // check for duplicate reference names in component + if (componentReference != component.getReference(componentReference.getName())){ + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "DuplicateComponentReferenceName", + component.getName(), + componentReference.getName()); + } + + if (componentReference.getReference() != null || componentReference.isForCallback()) { + continue; + } + + if (component.getImplementation() == null) { + // is null in some of our basic unit tests + continue; + } + + Reference reference = component.getImplementation().getReference(componentReference.getName()); + + if (reference != null) { + componentReference.setReference(reference); + } else { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "ReferenceNotFoundForComponentReference", + component.getName(), + componentReference.getName()); + } + } + } + + private void connectPropertiesToComponentType(Component component, Monitor monitor) { + // Connect each component property to the corresponding component type property + for (ComponentProperty componentProperty : component.getProperties()) { + // check for duplicate property names in component + if (componentProperty != component.getProperty(componentProperty.getName())){ + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "DuplicateComponentPropertyName", + component.getName(), + componentProperty.getName()); + } + + Property property = component.getImplementation().getProperty(componentProperty.getName()); + + if (property != null) { + componentProperty.setProperty(property); + // copy the types up if not specified at the component level + if (componentProperty.getXSDElement() == null){ + componentProperty.setXSDElement(property.getXSDElement()); + } + if (componentProperty.getXSDType() == null){ + componentProperty.setXSDType(property.getXSDType()); + } + } else { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertyNotFound", + component.getName(), + componentProperty.getName()); + } + } + } + + private void reconcileReferenceMultiplicity(Component component, + Reference componentReference, + Reference componentTypeReference, + Monitor monitor) { + if (componentReference.getMultiplicity() != null) { + if (!isValidMultiplicityOverride(componentTypeReference.getMultiplicity(), componentReference + .getMultiplicity())) { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "ReferenceIncompatibleMultiplicity", + component.getName(), + componentReference.getName()); + } + } else { + componentReference.setMultiplicity(componentTypeReference.getMultiplicity()); + } + } + + private void reconcileComponentPropertyWithComponentType(Component component, ComponentProperty componentProperty, Monitor monitor) { + Property componentTypeProperty = componentProperty.getProperty(); + if (componentTypeProperty != null) { + + // Check that a component property does not override the + // mustSupply attribute + if (!componentTypeProperty.isMustSupply() && componentProperty.isMustSupply()) { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertyMustSupplyIncompatible", + component.getName(), + componentProperty.getName()); + } + + // Default to the mustSupply attribute specified on the property + if (!componentProperty.isMustSupply()) + componentProperty.setMustSupply(componentTypeProperty.isMustSupply()); + + // Default to the value specified on the component type property + if (!isPropertyValueSet(componentProperty)) { + componentProperty.setValue(componentTypeProperty.getValue()); + } + + // Override the property value for the composite + if (component.getImplementation() instanceof Composite) { + componentTypeProperty.setValue(componentProperty.getValue()); + } + + // Check that a component property does not override the + // many attribute + if (!componentTypeProperty.isMany() && componentProperty.isMany()) { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertyOverrideManyAttribute", + component.getName(), + componentProperty.getName()); + } + + // Default to the many attribute defined on the property + componentProperty.setMany(componentTypeProperty.isMany()); + + // Default to the type and element defined on the property + if (componentProperty.getXSDType() == null) { + componentProperty.setXSDType(componentTypeProperty.getXSDType()); + } + if (componentProperty.getXSDElement() == null) { + componentProperty.setXSDElement(componentTypeProperty.getXSDElement()); + } + + // check that the types specified in the component type and component property match + if ( componentProperty.getXSDElement() != null && + componentTypeProperty.getXSDElement() != null && + !componentProperty.getXSDElement().equals(componentTypeProperty.getXSDElement())){ + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertXSDElementsDontMatch", + component.getName(), + componentProperty.getName(), + componentProperty.getXSDElement(), + componentTypeProperty.getXSDElement()); + } + + if ( componentProperty.getXSDType() != null && + componentTypeProperty.getXSDType() != null && + !componentProperty.getXSDType().equals(componentTypeProperty.getXSDType())){ + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertXSDTypesDontMatch", + component.getName(), + componentProperty.getName(), + componentProperty.getXSDType(), + componentTypeProperty.getXSDType()); + } + } + } + + /** + * checks that the component property value is correctly typed when compared with + * the type specified in the composite file property + * + * TODO - Don't yet handle multiplicity + * Need to check composite properties also + * + * @param component + * @param componentProperty + * @param monitor + */ + private void checkComponentPropertyType(Component component, ComponentProperty componentProperty, Monitor monitor) { + + QName propertyXSDType = componentProperty.getXSDType(); + QName propertyElementType = componentProperty.getXSDElement(); + + if (propertyXSDType != null){ + if (propertyXSDType.getNamespaceURI().equals("http://www.w3.org/2001/XMLSchema")) { + // The property has a simple schema type so we can use the + // data binding framework to see if the XML value can be transformed + // into a simple Java value + Document doc = (Document)componentProperty.getValue(); + Node source = (doc == null) ? null : doc.getDocumentElement().getFirstChild(); + DataType sourceDataType = new DataTypeImpl(DOMDataBinding.NAME, + Node.class, + new XMLType(null, componentProperty.getXSDType())); + DataType targetDataType = new DataTypeImpl(JAXBDataBinding.NAME, + Object.class, + new XMLType(null, componentProperty.getXSDType())); + try { + mediator.mediate(source, sourceDataType, targetDataType, null); + } catch (Exception ex){ + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertyValueDoesNotMatchSimpleType", + componentProperty.getName(), + component.getName(), + componentProperty.getXSDType().toString()); + } + } else { + // The property has a complex schema type so we fluff up a schema + // and use that to validate the property value + XSDefinition xsdDefinition = (XSDefinition)componentProperty.getXSDDefinition(); + + if (xsdDefinition != null) { + try { + // create schema factory for XML schema + SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + + Document schemaDom = xsdDefinition.getSchema().getSchemaDocument(); + + String valueSchema = null; + Schema schema = null; + + if (componentProperty.getXSDType().getNamespaceURI().equals(Constants.SCA11_NS)){ + // include the referenced schema as it's already in the OASIS namespace + valueSchema = " " + + "" + + "" + +// "" + + ""; +// Source sources[] = {new StreamSource(new StringReader(valueSchema))}; + Source sources[] = {new DOMSource(schemaDom)}; + schema = factory.newSchema(sources); + + // The SCA schema already contains a "value" element so I can't create this schema + // the SCA value element is an any so return assuming that it validates. + return; + } else { + // import the referenced schema + valueSchema = " " + + "" + + "" + + "" + + ""; + Source sources[] = {new DOMSource(schemaDom), new StreamSource(new StringReader(valueSchema))}; + schema = factory.newSchema(sources); + } + + // get the value child of the property element + Document property = (Document)componentProperty.getValue(); + Element value = (Element)property.getDocumentElement().getFirstChild(); + + // validate the element property/value from the DOM + Validator validator = schema.newValidator(); + validator.validate(new DOMSource(value)); + + } catch (Exception e) { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertyValueDoesNotMatchComplexType", + componentProperty.getName(), + component.getName(), + componentProperty.getXSDType().toString(), + e.getMessage()); + } + } + } + } else if (propertyElementType != null) { + // TODO - TUSCANY-3530 - still need to add validation for element type + + } + } + + /** + * If the property has a source attribute use this to retrieve the value from a + * property in the parent composite + * + * @param parentCompoent the composite that contains the component + * @param component + * @param componentProperty + */ + private void processPropertySourceAttribute(Component outerComponent, + Composite parentComposite, + Component component, + ComponentProperty componentProperty, + Monitor monitor) { + String source = componentProperty.getSource(); + + if (source == null) return; + + try { + String sourceName = extractSourcePropertyName( source ); + + Property sourceProp = null; + if (outerComponent != null) { + sourceProp = outerComponent.getProperty(sourceName); + } else { + sourceProp = parentComposite.getProperty(sourceName); + } + if (sourceProp == null) { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertySourceNotFound", + source, + componentProperty.getName(), + component.getName()); + } else { + + Document sourcePropValue = (Document)sourceProp.getValue(); + + try { + // FIXME: How to deal with namespaces? + Document node = + evaluateXPath2(sourcePropValue, + componentProperty.getSourceXPathExpression(), + documentBuilderFactory); + + if (node != null) { + componentProperty.setValue(node); + if(componentProperty.getXSDElement() == null){ + componentProperty.setXSDElement(sourceProp.getXSDElement()); + } + if(componentProperty.getXSDType() == null){ + componentProperty.setXSDType(sourceProp.getXSDType()); + } + } else { + Monitor.warning(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertyXpathExpressionReturnedNull", + component.getName(), + componentProperty.getName(), + componentProperty.getSource()); + } // end if + + } catch (Exception ex) { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertySourceXpathInvalid", + source, + componentProperty.getName(), + component.getName(), + ex); + } // end try + } // end if + } catch (IllegalArgumentException e ) { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertySourceValueInvalid", + source, + componentProperty.getName(), + component.getName()); + } // end try + } // end method + + /** + * Extracts the name of the source property from the value of an @source attribute string + * @param source - the value of the @source attribute + * @return - the source property name as a String + */ + private String extractSourcePropertyName( String source ) throws IllegalArgumentException { + String propertyName = null; + + // Possible values for the source string: + // a) $name + // b) $name/expression + // c) $name[xx] + // d) $name[xx]/expression + // ...and note that the expression MAY contain '/' and '[' characters + if( source.charAt(0) != '$' ) throw new IllegalArgumentException("Source value does not start with '$'"); + + int index = source.indexOf('/'); + int bracket = source.indexOf('['); + + if( index == -1 && bracket == -1 ) { + // Format a) - simply remove the '$' + propertyName = source.substring(1); + } else if ( bracket == -1 ) { + // Format b) - remove the '$' and the '/' and everything following it + propertyName = source.substring(1, index); + } else if ( index == -1 ) { + // Format c) - remove the '$' and the '[' and everything following it + propertyName = source.substring(1, bracket); + } else { + // Format d) - but need to ensure that the '[' is BEFORE the '/' + if( bracket < index ) { + // Format d) - remove the '$' and the '[' and everything following it + propertyName = source.substring(1, bracket); + } else { + // Format b) variant where there is a '[' in the expression... + propertyName = source.substring(1, index); + } // end if + } // end if + + return propertyName; + } // end method extractSourcePropertyName( source, monitor ) + + /** + * If the property has a file attribute use this to retrieve the property value from a local file + * Format of the property value file is defined in the SCA Assembly specification in ASM50046 + * + * @param component the component holding the property + * @param componentProperty - the property + * @param monitor - a Monitor object for reporting problems + */ + /** + * Property file format: + * MUST contain a element + * - either contains one or more subelements (mandatory for property with a simple XML type) + * - or contains one or more global elements of the type of the property + * + * eg. + * + * + * MyValue + * + * + * + * + * + * AValue + * InterestingURI + * + * + */ + private void processPropertyFileAttribute(Component component, ComponentProperty componentProperty, Monitor monitor) { + String file = componentProperty.getFile(); + if (file == null) return; + try { +/* + URI uri = URI.create(file); + // URI resolution for relative URIs is done when the composite is resolved. + URL url = uri.toURL(); + URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + InputStream is = null; + try { + + is = connection.getInputStream(); + + Source streamSource = new SAXSource(new InputSource(is)); + DOMResult result = new DOMResult(); + javax.xml.transform.Transformer transformer = transformerFactory.newTransformer(); + transformer.transform(streamSource, result); + + Document document = (Document)result.getNode(); +*/ + Document document = readPropertyFileData( file ); + + Element docElement = document.getDocumentElement(); + if( docElement == null ) throw new Exception("Property File has no XML document element"); + + if( !"values".equals( docElement.getLocalName() ) ) { + throw new Exception("Property File does not start with element"); + } // end if + + // The property value is the subelement(s) of the element + NodeList values = docElement.getChildNodes(); + + Document newdoc = documentBuilderFactory.newDocumentBuilder().newDocument(); + Element newProperty = newdoc.createElementNS(SCA11_NS, "property"); + newdoc.appendChild( newProperty ); + + int count = 0; + + // Copy the property values under the new element + for( int i = 0 ; i < values.getLength() ; i++ ) { + Node valueNode = values.item(i); + // Only elements or global elements are valid values... + if( valueNode.getNodeType() == Node.ELEMENT_NODE ) { + newProperty.appendChild(newdoc.importNode(values.item(i), true)); + count++; + } // end if + } // end for + + if( count == 0 ) { + throw new Exception("Property File has no property values"); + } // end if + + componentProperty.setValue(newdoc); + +/* + // TUSCANY-2377, Add a fake value element so it's consistent with + // the DOM tree loaded from inside SCDL + if (!document.getDocumentElement().getLocalName().equals("value")){ + Element root = document.createElementNS(null, "value"); + root.appendChild(document.getDocumentElement()); + + // remove all the child nodes as they will be replaced by the "value" node + NodeList children = document.getChildNodes(); + for (int i=0; i < children.getLength(); i++){ + document.removeChild(children.item(i)); + } + + // add the value node back in + document.appendChild(root); + } + componentProperty.setValue(document); + } finally { + if (is != null) { + is.close(); + } + } // end try +*/ + } catch (Exception ex) { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PropertyFileValueInvalid", + file, + componentProperty.getName(), + component.getName(), + ex); + } // end try + } // end method processPropertyFileAttribute + + private Document readPropertyFileData( String file ) throws Exception { + Document doc = null; + + URI uri = URI.create(file); + // URI resolution for relative URIs is done when the composite is resolved. + URL url = uri.toURL(); + URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + InputStream is = null; + try { + is = connection.getInputStream(); + + Source streamSource = new SAXSource(new InputSource(is)); + DOMResult result = new DOMResult(); + javax.xml.transform.Transformer transformer = transformerFactory.newTransformer(); + transformer.transform(streamSource, result); + + doc = (Document)result.getNode(); + } finally { + if (is != null) { + is.close(); + } + } // end try + + return doc; + } // end method readPropertyFileData + + /** + * Evaluate an XPath expression against a Property value, returning the result as a Property value + * @param node - the document root element of a Property value + * @param expression - the XPath expression + * @param documentBuilderFactory - a DOM document builder factory + * @return - a DOM Document representing the result of the evaluation as a Property value + * @throws XPathExpressionException + * @throws ParserConfigurationException + */ + private Document evaluateXPath(Document node, + XPathExpression expression, + DocumentBuilderFactory documentBuilderFactory) throws XPathExpressionException, + ParserConfigurationException { + + // The document element is a element + Node property = node.getDocumentElement(); + // The first child of the element is a element + Node value = property.getFirstChild(); + + Node result = (Node)expression.evaluate(value, XPathConstants.NODE); + if (result == null) { + return null; + } + + if (result instanceof Document) { + return (Document)result; + } else { + Document document = documentBuilderFactory.newDocumentBuilder().newDocument(); + Element newProperty = document.createElementNS(SCA11_NS, "property"); + + if (result.getNodeType() == Node.ELEMENT_NODE) { + // If the result is a element, use it directly in the result + newProperty.appendChild(document.importNode(result, true)); + } else { + // If the result is not a element, create a element to contain the result + Element newValue = document.createElementNS(SCA11_NS, "value"); + newValue.appendChild(document.importNode(result, true)); + newProperty.appendChild(newValue); + } // end if + document.appendChild(newProperty); + + return document; + } + } + + /** + * Evaluate an XPath expression against a Property value, returning the result as a Property value + * - deals with multi-valued input property values and with multi-valued output property values + * @param node - the document root element of a Property value + * @param expression - the XPath expression + * @param documentBuilderFactory - a DOM document builder factory + * @return - a DOM Document representing the result of the evaluation as a Property value + * @throws XPathExpressionException + * @throws ParserConfigurationException + */ + private Document evaluateXPath2(Document node, + XPathExpression expression, + DocumentBuilderFactory documentBuilderFactory) throws XPathExpressionException, + ParserConfigurationException { + + // The document element is a element + Element property = node.getDocumentElement(); + + NodeList result = (NodeList)expression.evaluate(property, XPathConstants.NODESET); + if (result == null || result.getLength() == 0) return null; + + if (result instanceof Document) { + return (Document)result; + } else { + Document document = documentBuilderFactory.newDocumentBuilder().newDocument(); + Element newProperty = document.createElementNS(SCA11_NS, "property"); + + for( int i = 0 ; i < result.getLength() ; i++ ) { + if (result.item(i).getNodeType() == Node.ELEMENT_NODE) { + // If the result is an element, use it directly in the result + newProperty.appendChild(document.importNode(result.item(i), true)); + } else { + // If the result is not an element, create a element to contain the result + Element newValue = document.createElementNS(SCA11_NS, "value"); + newValue.appendChild(document.importNode(result.item(i), true)); + newProperty.appendChild(newValue); + } // end if + } // end for + + document.appendChild(newProperty); + + return document; + } // end if + } // end method + + /** + * Create a callback reference for a component service + * + * @param component + * @param service + */ + private void createCallbackReference(Component component, ComponentService service, Monitor monitor) { + + // if the service has a callback interface create a reference + // to represent the callback + if (service.getInterfaceContract() != null && // can be null in unit tests + service.getInterfaceContract().getCallbackInterface() != null) { + + ComponentReference callbackReference = assemblyFactory.createComponentReference(); + callbackReference.setForCallback(true); + callbackReference.setName(service.getName()); + // MJE: multiplicity = 0..n for these callback references + callbackReference.setMultiplicity(Multiplicity.ZERO_N); + try { + InterfaceContract contract = (InterfaceContract)service.getInterfaceContract().clone(); + contract.setInterface(contract.getCallbackInterface()); + contract.setCallbackInterface(null); + contract.setNormalizedWSDLContract(null); + callbackReference.setInterfaceContract(contract); + } catch (CloneNotSupportedException e) { + // will not happen + } + Service implService = service.getService(); + if (implService != null) { + + // If the implementation service is a CompositeService, ensure that the Reference that is + // created is a CompositeReference, otherwise create a Reference + Reference implReference; + if (implService instanceof CompositeService) { + CompositeReference implCompReference = assemblyFactory.createCompositeReference(); + // Set the promoted component from the promoted component of the composite service + implCompReference.getPromotedComponents().add(((CompositeService)implService) + .getPromotedComponent()); + + // Get the promoted component reference corresponding to the service with the callback + // fist checking that the promoted service is resolved lest we get a NPE trying to + // retrieve the promoted component. It could be unresolved if the user gets the + // promotes string wrong + // TODO - is there any danger that the callback reference name will clash with other + // reference names. Old code used to qualify it with promoted component name + if (((CompositeService)implService).getPromotedService().isUnresolved() == false){ + String referenceName = ((CompositeService)implService).getPromotedService().getName(); + ComponentReference promotedReference = ((CompositeService)implService).getPromotedComponent().getReference(referenceName); + + if (promotedReference != null){ + implCompReference.getPromotedReferences().add(promotedReference); + } else { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PromotedCallbackReferenceNotFound", + component.getName(), + service.getName(), + ((CompositeService)implService).getPromotedComponent().getName(), + referenceName); + } + } + implReference = implCompReference; + + // Add the composite reference to the composite implementation artifact + Implementation implementation = component.getImplementation(); + if (implementation != null && implementation instanceof Composite) { + ((Composite)implementation).getReferences().add(implCompReference); + } + } else { + implReference = assemblyFactory.createReference(); + } + + implReference.setName(implService.getName()); + // MJE: Fixup multiplicity as 0..n for callback references in the component type + implReference.setMultiplicity(Multiplicity.ZERO_N); + try { + InterfaceContract implContract = (InterfaceContract)implService.getInterfaceContract().clone(); + implContract.setInterface(implContract.getCallbackInterface()); + implContract.setCallbackInterface(null); + implContract.setNormalizedWSDLContract(null); + implReference.setInterfaceContract(implContract); + } catch (CloneNotSupportedException e) { + // will not happen + } + // FIXME: We need to set the allowsPassByReference flag based on the annotations on the implementation and callback + // implReference.setAllowsPassByReference(allowsPassByReference); + callbackReference.setReference(implReference); + } + component.getReferences().add(callbackReference); + + // Set the bindings of the callback reference + if (callbackReference.getBindings().isEmpty()) { + // If there are specific callback bindings set in the SCDL service + // callback element then use them + // at runtime a callback binding will be selected based on the forward call + if (service.getCallback() != null && service.getCallback().getBindings().size() > 0) { + callbackReference.getBindings().addAll(service.getCallback().getBindings()); + } else { + // otherwise take a copy of all the bindings on the forward service + // at runtime a callback binding will be selected based on the forward call + List serviceBindings = service.getBindings(); + for ( Binding serviceBinding: serviceBindings ) { + try { + Binding referenceBinding = (Binding)serviceBinding.clone(); + referenceBinding.setURI(null); + callbackReference.getBindings().add(referenceBinding); + } catch (CloneNotSupportedException e) { + // will not happen + } // end try + } // end for + + // if there are still no bindings for the callback create a default binding which + // will cause the EPR for this reference to be marked as EndpointReference.NOT_CONFIGURED + if( serviceBindings.size() == 0 ) { + createSCABinding(callbackReference, null); + } // end if + } + } + service.setCallbackReference(callbackReference); + } + } + + /** + * Create a callback service for a component reference + * + * @param component + * @param service + */ + private void createCallbackService(Component component, ComponentReference reference, Monitor monitor) { + if (reference.getInterfaceContract() != null && // can be null in unit tests + reference.getInterfaceContract().getCallbackInterface() != null) { + ComponentService callbackService = assemblyFactory.createComponentService(); + callbackService.setForCallback(true); + callbackService.setName(reference.getName()); + try { + InterfaceContract contract = (InterfaceContract)reference.getInterfaceContract().clone(); + contract.setInterface(contract.getCallbackInterface()); + contract.setCallbackInterface(null); + contract.setNormalizedWSDLContract(null); + callbackService.setInterfaceContract(contract); + } catch (CloneNotSupportedException e) { + // will not happen + } + Reference implReference = reference.getReference(); + if (implReference != null) { + // If the implementation reference is a CompositeReference, ensure that the Service that is + // created is a CompositeService, otherwise create a Service + Service implService; + if (implReference instanceof CompositeReference) { + CompositeService implCompService = assemblyFactory.createCompositeService(); + // TODO The reality here is that the composite reference which has the callback COULD promote more than + // one component reference - and there must be a separate composite callback service for each of these component + // references + + // Set the promoted component from the promoted component of the composite reference + implCompService.setPromotedComponent(((CompositeReference)implReference).getPromotedComponents().get(0)); + implCompService.setForCallback(true); + + // Get the promoted component service corresponding to the reference with the callback + // fist checking that the promoted reference is resolved lest we get a NPE trying to + // retrieve the promoted component. It could be unresolved if the user gets the + // promotes string wrong + if (((CompositeReference)implReference).getPromotedReferences().get(0).isUnresolved() == false){ + String serviceName = ((CompositeReference)implReference).getPromotedReferences().get(0).getName(); + ComponentService promotedService = ((CompositeReference)implReference).getPromotedComponents().get(0).getService(serviceName); + + if (promotedService != null){ + implCompService.setPromotedService(promotedService); + } else { + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "PromotedCallbackServiceNotFound", + component.getName(), + reference.getName(), + ((CompositeReference)implReference).getPromotedComponents().get(0).getName(), + serviceName); + } + } + + implService = implCompService; + // Add the composite service to the composite implementation artifact + Implementation implementation = component.getImplementation(); + if (implementation != null && implementation instanceof Composite) { + ((Composite)implementation).getServices().add(implCompService); + } // end if + // + } else { + implService = assemblyFactory.createService(); + } // end if + // + implService.setName(implReference.getName()); + try { + InterfaceContract implContract = (InterfaceContract)implReference.getInterfaceContract().clone(); + implContract.setInterface(implContract.getCallbackInterface()); + implContract.setCallbackInterface(null); + implContract.setNormalizedWSDLContract(null); + implService.setInterfaceContract(implContract); + } catch (CloneNotSupportedException e) { + // will not happen + } + callbackService.setService(implService); + } + component.getServices().add(callbackService); + + // configure bindings for the callback service + if (callbackService.getBindings().isEmpty()) { + if (reference.getCallback() != null && reference.getCallback().getBindings().size() > 0) { + // set bindings of the callback service based on the information provided in + // SCDL reference callback element + callbackService.getBindings().addAll(reference.getCallback().getBindings()); + } else if (reference.getBindings().size() > 0) { + // use any bindings explicitly declared on the forward reference + for (Binding binding : reference.getBindings()) { + try { + Binding clonedBinding = (Binding)binding.clone(); + // binding uri will be calculated during runtime build + clonedBinding.setURI(null); + callbackService.getBindings().add(clonedBinding); + } catch (CloneNotSupportedException ex) { + + } + } + } else { + // create a default binding which will have the correct policy + // and URI added. We check later to see if a new binding is required + // based on the forward binding but can then copy policy and URI + // details from here. + // TODO - there is a hole here. If the user explicitly specified an + // SCA callback binding that is different from the forward + // binding type then we're in trouble + createSCABinding(callbackService, null); + } + } + + reference.setCallbackService(callbackService); + } + } + + /** + * Create a default SCA binding in the case that no binding + * is specified by the user + * + * @param contract + * @param definitions + */ + protected void createSCABinding(Contract contract, Definitions definitions) { + + SCABinding scaBinding = scaBindingFactory.createSCABinding(); + scaBinding.setName(contract.getName()); + + if (definitions != null) { + for (ExtensionType attachPointType : definitions.getBindingTypes()) { + if (attachPointType.getType().equals(BINDING_SCA_QNAME)) { + ((PolicySubject)scaBinding).setExtensionType(attachPointType); + } + } + } + + contract.getBindings().add(scaBinding); + contract.setOverridingBindings(false); + } + + /** + * Look to see if any value elements have been set into the property + * A bit involved as the value is stored as a DOM Document + * + * @param property the property to be tested + * @return true is values are present + */ + private boolean isPropertyValueSet(Property property) { + Document value = (Document)property.getValue(); + + if (value == null) { + return false; + } + + if (value.getDocumentElement() == null) { + return false; + } + + if (value.getDocumentElement().getChildNodes().getLength() == 0) { + return false; + } + + return true; + } + + /** + * Look to see is a property has more than one value + * + * @param property + * @return true is the property has more than one value + */ + private boolean isPropertyManyValued(Property property) { + + if (isPropertyValueSet(property)){ + Document value = (Document)property.getValue(); + if (value.getDocumentElement().getChildNodes().getLength() > 1){ + return true; + } + } + return false; + } + + private boolean isValidMultiplicityOverride(Multiplicity definedMul, Multiplicity overridenMul) { + if (definedMul != overridenMul) { + switch (definedMul) { + case ZERO_N: + return overridenMul == Multiplicity.ZERO_ONE || overridenMul == Multiplicity.ONE_ONE + || overridenMul == Multiplicity.ONE_N; + case ONE_N: + return overridenMul == Multiplicity.ONE_ONE; + case ZERO_ONE: + return overridenMul == Multiplicity.ONE_ONE; + default: + return false; + } + } else { + return true; + } + } + + + /** + * Interface contract from higher in the implementation hierarchy takes precedence + * When it comes to checking compatibility the top level service interface is a + * subset of the promoted service interface so treat the top level interface as + * the source + * + * @param topContract the top contract + * @param bottomContract the bottom contract + */ + private void calculateServiceInterfaceContract(Component component, Service topContract, Service bottomContract, BuilderContext context) { + + // Use the interface contract from the bottom level contract if + // none is specified on the top level contract + InterfaceContract topInterfaceContract = topContract.getInterfaceContract(); + InterfaceContract bottomInterfaceContract = bottomContract.getInterfaceContract(); + + if (topInterfaceContract == null) { + topContract.setInterfaceContract(bottomInterfaceContract); + } else if (bottomInterfaceContract != null) { + // apply remotable override if it has been specified in the SCDL for the + // higher level contract + if (topInterfaceContract.getInterface().isRemotableSetFromSCDL() == true){ + if (bottomInterfaceContract.getInterface().isRemotable() == false && + topInterfaceContract.getInterface().isRemotable() == true){ + bottomInterfaceContract.getInterface().setRemotable(true); + } + } + + // Check that the top and bottom interface contracts are compatible + boolean isCompatible = true; + String incompatibilityReason = ""; + try{ + isCompatible = checkSubsetCompatibility(topInterfaceContract, + bottomInterfaceContract, + context); + } catch (IncompatibleInterfaceContractException ex){ + isCompatible = false; + incompatibilityReason = ex.getMessage(); + } + if (!isCompatible) { + Monitor.error(context.getMonitor(), + this, + Messages.ASSEMBLY_VALIDATION, + "ServiceIncompatibleComponentInterface", + component.getName(), + topContract.getName(), + incompatibilityReason); + } + + // TODO - there is an issue with the following code if the + // contracts are of different types. Need to use the + // normalized form + + // fix up the forward interface based on the promoted component + // Someone might have manually specified a callback interface but + // left out the forward interface + if (topInterfaceContract.getInterface() == null){ + topInterfaceContract.setInterface(bottomInterfaceContract.getInterface()); + } + + // fix up the callback interface based on the promoted component + // Someone might have manually specified a forward interface but + // left out the callback interface + if (topInterfaceContract.getCallbackInterface() == null){ + topInterfaceContract.setCallbackInterface(bottomInterfaceContract.getCallbackInterface()); + } + } + } + + /** + * Interface contract from higher in the implementation hierarchy takes precedence + * When it comes to checking compatibility the top level reference interface is a + * superset of the promoted reference interface so treat the promoted + * (bottom) interface as the source + * + * @param topContract the top contract + * @param bottomContract the bottom contract + */ + private void calculateReferenceInterfaceContract(Component component, Reference topContract, Reference bottomContract, BuilderContext context) { + + // Use the interface contract from the bottom level contract if + // none is specified on the top level contract + InterfaceContract topInterfaceContract = topContract.getInterfaceContract(); + InterfaceContract bottomInterfaceContract = bottomContract.getInterfaceContract(); + + if (topInterfaceContract == null) { + topContract.setInterfaceContract(bottomInterfaceContract); + } else if (bottomInterfaceContract != null) { + // apply remotable override if it has been specified in the SCDL for the + // higher level contract + if (topInterfaceContract.getInterface().isRemotableSetFromSCDL() == true){ + if (bottomInterfaceContract.getInterface().isRemotable() == false && + topInterfaceContract.getInterface().isRemotable() == true){ + bottomInterfaceContract.getInterface().setRemotable(true); + } + } + // Check that the top and bottom interface contracts are compatible + boolean isCompatible = true; + String incompatibilityReason = ""; + try{ + isCompatible = checkSubsetCompatibility(bottomInterfaceContract, + topInterfaceContract, + context); + } catch (IncompatibleInterfaceContractException ex){ + isCompatible = false; + incompatibilityReason = ex.getMessage(); + } + if (!isCompatible) { + Monitor.error(context.getMonitor(), + this, + Messages.ASSEMBLY_VALIDATION, + "ReferenceIncompatibleComponentInterface", + component.getName(), + topContract.getName(), + incompatibilityReason); + } + + // TODO - there is an issue with the following code if the + // contracts of of different types. Need to use the + // normalized form + + // fix up the forward interface based on the promoted component + // Someone might have manually specified a callback interface but + // left out the forward interface + if (topInterfaceContract.getInterface() == null){ + topInterfaceContract.setInterface(bottomInterfaceContract.getInterface()); + } + + // fix up the callback interface based on the promoted component + // Someone might have manually specified a forward interface but + // left out the callback interface + if (topInterfaceContract.getCallbackInterface() == null){ + topInterfaceContract.setCallbackInterface(bottomInterfaceContract.getCallbackInterface()); + } + } + } + + /** + * Bindings from higher in the hierarchy take precedence + * + * @param componentService the top service + * @param componentTypeService the bottom service + */ + private void calculateBindings(Component component, Service componentService, Service componentTypeService, BuilderContext context) { + Monitor monitor = context.getMonitor(); + + // forward bindings + if (componentService.getBindings().isEmpty()) { + componentService.getBindings().addAll(componentTypeService.getBindings()); + } + + if (componentService.getBindings().isEmpty()) { + createSCABinding(componentService, context.getDefinitions()); + } + + // callback bindings + if (componentService.getCallback() == null) { + componentService.setCallback(componentTypeService.getCallback()); + if (componentService.getCallback() == null) { + // Create an empty callback to avoid null check + componentService.setCallback(assemblyFactory.createCallback()); + } + } else if (componentService.getCallback().getBindings().isEmpty() && componentTypeService.getCallback() != null) { + componentService.getCallback().getBindings().addAll(componentTypeService.getCallback().getBindings()); + } + + // [ASM90005] validate that binding.sca has no uri set + for (Binding binding : componentService.getBindings()){ + if (binding instanceof SCABinding){ + if ((binding.getURI() != null) && + (binding.getURI().length() > 0)){ + Monitor.error(monitor, + this, + Messages.ASSEMBLY_VALIDATION, + "URIFoundOnServiceSCABinding", + binding.getName(), + component.getName(), + componentService.getName(), + binding.getURI()); + } + } + } + + } + + /** + * Bindings from higher in the hierarchy take precedence + * + * @param componentReference the top service + * @param componentTypeReference the bottom service + */ + private void calculateBindings(Reference componentReference, Reference componentTypeReference) { + // forward bindings + if (componentReference.getBindings().isEmpty()) { + componentReference.getBindings().addAll(componentTypeReference.getBindings()); + } + + // callback bindings + if (componentReference.getCallback() == null) { + componentReference.setCallback(componentTypeReference.getCallback()); + } else if (componentReference.getCallback().getBindings().isEmpty() && componentTypeReference.getCallback() != null) { + componentReference.getCallback().getBindings().addAll(componentTypeReference.getCallback().getBindings()); + } + } + + /** + * A local wrapper for the interface contract mapper as we need to normalize the + * interface contracts if appropriate and the mapper doesn't have the right + * dependencies to be able to do it. + * + * Sometimes the two interfaces can be presented using different IDLs, for example + * Java and WSDL. In this case interfaces are converted so that they are both WSDL1.1 interfaces + * and they are then compared. The generated WSDL is cached on the interface object for + * any subsequent matching + * + * @param contractA + * @param contractB + * @return true if the interface contracts match + */ + private boolean checkSubsetCompatibility(InterfaceContract contractA, InterfaceContract contractB, BuilderContext context) + throws IncompatibleInterfaceContractException { + + if (contractA.getClass() != contractB.getClass()) { + + if (contractA instanceof JavaInterfaceContract){ + contractBuilder.build(contractA, context); + contractA = ((JavaInterfaceContract)contractA).getNormalizedWSDLContract(); + } + + if (contractB instanceof JavaInterfaceContract){ + contractBuilder.build(contractB, context); + contractB = ((JavaInterfaceContract)contractB).getNormalizedWSDLContract(); + } + } + + return interfaceContractMapper.checkCompatibility(contractA, + contractB, + Compatibility.SUBSET, + false, + false); + } + +} -- cgit v1.2.3