blob: 694ece2d6c040316343ee0944f40fa981d3697ec [file] [log] [blame]
/*
* 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.Interface;
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.JavaInterface;
import org.apache.tuscany.sca.interfacedef.java.JavaInterfaceContract;
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
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, monitor);
// bindings
calculateBindings(component, componentService, componentTypeService, monitor);
// add callback reference model objects
createCallbackReference(component, componentService);
}
}
/**
* 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, monitor);
// bindings
calculateBindings(componentReference, componentTypeReference);
// add callback service model objects
createCallbackService(component, componentReference);
// 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 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);
} 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 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 the types specified in the component type and component property match
if ( componentProperty.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 &&
!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<XMLType> sourceDataType = new DataTypeImpl<XMLType>(DOMDataBinding.NAME,
Node.class,
new XMLType(null, componentProperty.getXSDType()));
DataType<XMLType> targetDataType = new DataTypeImpl<XMLType>(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 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> " +
"<schema xmlns=\"http://www.w3.org/2001/XMLSchema\" "+
"xmlns:sca=\"http://docs.oasis-open.org/ns/opencsa/sca/200912\" "+
"xmlns:__tmp=\"" + componentProperty.getXSDType().getNamespaceURI() + "\" "+
"targetNamespace=\"http://docs.oasis-open.org/ns/opencsa/sca/200912\" " +
"elementFormDefault=\"qualified\">" +
"<include schemaLocation=\"" + xsdDefinition.getLocation() + "\"/>" +
// "<element name=\"value\" type=\"" + "__tmp:" + componentProperty.getXSDType().getLocalPart() + "\"/>" +
"</schema>";
// 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 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> " +
"<schema xmlns=\"http://www.w3.org/2001/XMLSchema\" "+
"xmlns:sca=\"http://docs.oasis-open.org/ns/opencsa/sca/200912\" "+
"xmlns:__tmp=\"" + componentProperty.getXSDType().getNamespaceURI() + "\" "+
"targetNamespace=\"http://docs.oasis-open.org/ns/opencsa/sca/200912\" " +
"elementFormDefault=\"qualified\">" +
"<import namespace=\"" + componentProperty.getXSDType().getNamespaceURI() + "\"/>" +
"<element name=\"value\" type=\"" + "__tmp:" + componentProperty.getXSDType().getLocalPart() + "\"/>" +
"</schema>";
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) {
// $<name>/...
int index = source.indexOf('/');
if (index == -1) {
// Tolerating $prop
source = source + "/";
index = source.length() - 1;
}
if (source.charAt(0) == '$') {
String name = source.substring(1, index);
Property sourceProp = null;
if (outerComponent != null) {
sourceProp = outerComponent.getProperty(name);
} else {
sourceProp = parentComposite.getProperty(name);
}
if (sourceProp == null) {
Monitor.error(monitor,
this,
Messages.ASSEMBLY_VALIDATION,
"PropertySourceNotFound",
source,
componentProperty.getName(),
component.getName());
}
Document sourcePropValue = (Document)sourceProp.getValue();
try {
// FIXME: How to deal with namespaces?
Document node =
evaluateXPath(sourcePropValue,
componentProperty.getSourceXPathExpression(),
documentBuilderFactory);
if (node != null) {
componentProperty.setValue(node);
} else {
Monitor.warning(monitor,
this,
Messages.ASSEMBLY_VALIDATION,
"PropertyXpathExpressionReturnedNull",
component.getName(),
componentProperty.getName(),
componentProperty.getSource());
}
} catch (Exception ex) {
Monitor.error(monitor,
this,
Messages.ASSEMBLY_VALIDATION,
"PropertySourceXpathInvalid",
source,
componentProperty.getName(),
component.getName(),
ex);
}
} else {
Monitor.error(monitor,
this,
Messages.ASSEMBLY_VALIDATION,
"PropertySourceValueInvalid",
source,
componentProperty.getName(),
component.getName());
}
}
}
/**
* If the property has a file attribute use this to retrieve the value from a
* local file
*
* @param parentCompoent the composite that contains the component
* @param component
*/
private void processPropertyFileAttribute(Component component, ComponentProperty componentProperty, Monitor monitor) {
String file = componentProperty.getFile();
if (file != null) {
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();
// 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();
}
}
} catch (Exception ex) {
Monitor.error(monitor,
this,
Messages.ASSEMBLY_VALIDATION,
"PropertyFileValueInvalid",
file,
componentProperty.getName(),
component.getName(),
ex);
}
}
}
/**
* 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 <sca:property/> element
Node property = node.getDocumentElement();
// The first child of the <property/> element is a <value/> 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 <value/> element, use it directly in the result
newProperty.appendChild(document.importNode(result, true));
} else {
// If the result is not a <value/> element, create a <value/> 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;
}
}
/**
* Create a callback reference for a component service
*
* @param component
* @param service
*/
private void createCallbackReference(Component component, ComponentService service) {
// 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);
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);
implCompReference.getPromotedReferences().add(promotedReference);
}
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);
implReference.setInterfaceContract(implContract);
} catch (CloneNotSupportedException e) {
// will not happen
}
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<Binding> 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) {
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);
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);
implCompService.setPromotedService(promotedService);
}
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);
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, Monitor monitor) {
// 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) {
// Check that the top and bottom interface contracts are compatible
boolean isCompatible = true;
String incompatibilityReason = "";
try{
isCompatible = checkSubsetCompatibility(topInterfaceContract,
bottomInterfaceContract);
} catch (IncompatibleInterfaceContractException ex){
isCompatible = false;
incompatibilityReason = ex.getMessage();
}
if (!isCompatible) {
Monitor.error(monitor,
this,
Messages.ASSEMBLY_VALIDATION,
"ServiceIncompatibleComponentInterface",
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());
}
}
}
/**
* 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, Monitor monitor) {
// 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) {
// Check that the top and bottom interface contracts are compatible
boolean isCompatible = true;
String incompatibilityReason = "";
try{
isCompatible = checkSubsetCompatibility(bottomInterfaceContract,
topInterfaceContract);
} catch (IncompatibleInterfaceContractException ex){
isCompatible = false;
incompatibilityReason = ex.getMessage();
}
if (!isCompatible) {
Monitor.error(monitor,
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, Monitor monitor) {
// forward bindings
if (componentService.getBindings().isEmpty()) {
componentService.getBindings().addAll(componentTypeService.getBindings());
}
if (componentService.getBindings().isEmpty()) {
createSCABinding(componentService, null);
}
// 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 interace 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)
throws IncompatibleInterfaceContractException {
if (contractA.getClass() != contractB.getClass()) {
if (contractA instanceof JavaInterfaceContract){
contractBuilder.build(contractA, null);
contractA = ((JavaInterfaceContract)contractA).getNormalizedWSDLContract();
}
if (contractB instanceof JavaInterfaceContract){
contractBuilder.build(contractB, null);
contractB = ((JavaInterfaceContract)contractB).getNormalizedWSDLContract();
}
}
return interfaceContractMapper.checkCompatibility(contractA,
contractB,
Compatibility.SUBSET,
false,
false);
}
}