blob: f87854cac41774a836c68678462c642b02c26414 [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.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
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.ComponentReference;
import org.apache.tuscany.sca.assembly.ComponentService;
import org.apache.tuscany.sca.assembly.ComponentType;
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.Multiplicity;
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.Messages;
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.definitions.Definitions;
import org.apache.tuscany.sca.interfacedef.Compatibility;
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.java.JavaInterfaceContract;
import org.apache.tuscany.sca.monitor.Monitor;
import org.apache.tuscany.sca.policy.ExtensionType;
import org.apache.tuscany.sca.policy.PolicySubject;
/**
* @version $Rev$ $Date$
*/
// TODO - really implementation.composite component type builder - CompositeComponentTypeBuilder?
public class CompositeComponentTypeBuilderImpl {
private static final Logger logger = Logger.getLogger(CompositeComponentTypeBuilderImpl.class.getName());
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 ComponentBuilderImpl componentBuilder;
private AssemblyFactory assemblyFactory;
private SCABindingFactory scaBindingFactory;
private InterfaceContractMapper interfaceContractMapper;
private BuilderExtensionPoint builders;
private ContractBuilder contractBuilder;
public CompositeComponentTypeBuilderImpl(ExtensionPointRegistry registry) {
UtilityExtensionPoint utilities = registry.getExtensionPoint(UtilityExtensionPoint.class);
FactoryExtensionPoint modelFactories = registry.getExtensionPoint(FactoryExtensionPoint.class);
assemblyFactory = modelFactories.getFactory(AssemblyFactory.class);
scaBindingFactory = modelFactories.getFactory(SCABindingFactory.class);
interfaceContractMapper = utilities.getUtility(InterfaceContractMapper.class);
builders = registry.getExtensionPoint(BuilderExtensionPoint.class);
contractBuilder = builders.getContractBuilder();
}
public void setComponentBuilder(ComponentBuilderImpl componentBuilder) {
this.componentBuilder = componentBuilder;
}
/**
* Calculate the component type for the provided implementation
*
* @param implementation
* @return component type
*/
public void createComponentType(Component outerComponent, Composite composite, BuilderContext context) {
Monitor monitor = context.getMonitor();
monitor.pushContext("Composite: " + composite.getName().toString());
try {
// first make sure that each child component has been properly configured based
// on its own component type
for (Component component : composite.getComponents()) {
// Check for duplicate component names
if (component != composite.getComponent(component.getName())) {
Monitor.error(monitor,
this,
Messages.ASSEMBLY_VALIDATION,
"DuplicateComponentName",
composite.getName().toString(),
component.getName());
}
// do any work we need to do before we configure the component
// Anything that needs to be pushed down the promotion
// hierarchy must be done before we configure the component
// Push down the autowire flag from the composite to components
if (component.getAutowire() == null) {
component.setAutowire(composite.getAutowire());
}
// configure the component from its component type
componentBuilder.configureComponentFromComponentType(outerComponent, composite, component, context);
}
// create the composite component type based on the promoted artifacts
// from the components that it contains
// index all the components, services and references in the
// component type so that they are easy to find
Map<String, Component> components = new HashMap<String, Component>();
Map<String, ComponentService> componentServices = new HashMap<String, ComponentService>();
Map<String, ComponentReference> componentReferences = new HashMap<String, ComponentReference>();
indexComponentsServicesAndReferences(composite, components, componentServices, componentReferences);
// services
calculateServices(composite, components, componentServices, context);
// references
calculateReferences(composite, components, componentReferences, context);
// properties
// Properties on the composite component are unaffected by properties
// on child components. Instead child component properties might take their
// values from composite properties. Hence there is nothing to do here.
//calculateProperties(composite, components);
} finally {
monitor.popContext();
}
}
/**
* Index components, services and references inside a composite.
*
* @param composite
* @param components
* @param componentServices
* @param componentReferences
*/
private void indexComponentsServicesAndReferences(Composite composite,
Map<String, Component> components,
Map<String, ComponentService> componentServices,
Map<String, ComponentReference> componentReferences) {
for (Component component : composite.getComponents()) {
// Index components by name
components.put(component.getName(), component);
ComponentService nonCallbackService = null;
int nonCallbackServices = 0;
for (ComponentService componentService : component.getServices()) {
// Index component services by component name / service name
String uri = component.getName() + '/' + componentService.getName();
componentServices.put(uri, componentService);
// count how many non-callback services there are
// if there is only one the component name also acts as the service name
if (!componentService.isForCallback()) {
// Check how many non callback non-promoted services we have
if (nonCallbackServices == 0) {
nonCallbackService = componentService;
}
nonCallbackServices++;
}
}
if (nonCallbackServices == 1) {
// If we have a single non callback service, index it by
// component name as well
componentServices.put(component.getName(), nonCallbackService);
}
// Index references by component name / reference name
for (ComponentReference componentReference : component.getReferences()) {
String uri = component.getName() + '/' + componentReference.getName();
componentReferences.put(uri, componentReference);
}
}
}
/**
* Connect the services in the component type to the component services that
* they promote
*
* @param componentType
* @param component
*/
private void calculateServices(ComponentType componentType,
Map<String, Component> components,
Map<String, ComponentService> componentServices,
BuilderContext context) {
Monitor monitor = context.getMonitor();
// Connect this component type's services to the
// services from child components which it promotes
connectPromotedServices(componentType, components, componentServices, monitor);
// look at each component type service in turn and
// calculate its configuration based on OASIS rules
for (Service service : componentType.getServices()) {
CompositeService compositeService = (CompositeService)service;
ComponentService promotedComponentService = compositeService.getPromotedService();
// promote interface contracts
calculatePromotedServiceInterfaceContract(compositeService, promotedComponentService, monitor);
// promote bindings
calculatePromotedBindings(compositeService, promotedComponentService);
componentBuilder.policyBuilder.configure(compositeService, context);
}
}
/**
* Connect the references in the component type to the component references that
* they promote
*
* @param componentType
* @param component
*/
private void calculateReferences(ComponentType componentType,
Map<String, Component> components,
Map<String, ComponentReference> componentReferences,
BuilderContext context) {
Monitor monitor = context.getMonitor();
// Connect this component type's references to the
// references from child components which it promotes
connectPromotedReferences(componentType, components, componentReferences, monitor);
// look at each component type reference in turn and
// calculate its configuration based on OASIS rules
for (Reference reference : componentType.getReferences()) {
CompositeReference compositeReference = (CompositeReference)reference;
List<ComponentReference> promotedReferences = compositeReference.getPromotedReferences();
for (ComponentReference promotedComponentReference : promotedReferences) {
// promote multiplicity
reconcileReferenceMultiplicity(componentType, compositeReference, promotedComponentReference, monitor);
// check nonOverridable
validateNonOverridable(componentType, compositeReference, promotedComponentReference, monitor);
// promote interface contracts
calculatePromotedReferenceInterfaceContract(compositeReference, promotedComponentReference, monitor);
// promote bindings
// Don't need to promote reference bindings as any lower level binding will
// already be targeting the correct service without need for promotion
//calculatePromotedBindings(compositeReference, promotedComponentReference);
}
componentBuilder.policyBuilder.configure(compositeReference, context);
}
}
/**
* Connect the services in the component type to the component services that
* they promote
*
* @param componentType
* @param component
*/
private void connectPromotedServices(ComponentType componentType,
Map<String, Component> components,
Map<String, ComponentService> componentServices,
Monitor monitor) {
for (Service service : componentType.getServices()) {
// Connect composite (component type) services to the component services
// that they promote
CompositeService compositeService = (CompositeService)service;
ComponentService componentService = compositeService.getPromotedService();
if (componentService != null && componentService.isUnresolved()) {
// get the name of the promoted component/service
String promotedComponentName = compositeService.getPromotedComponent().getName();
String promotedServiceName;
if (componentService.getName() != null) {
if (compositeService.isForCallback()) {
// For callbacks the name already has the form "componentName/servicename"
promotedServiceName = componentService.getName();
} else {
promotedServiceName = promotedComponentName + '/' + componentService.getName();
}
} else {
promotedServiceName = promotedComponentName;
}
// find the promoted service
ComponentService promotedService = componentServices.get(promotedServiceName);
if (promotedService != null) {
// Point to the resolved component
Component promotedComponent = components.get(promotedComponentName);
compositeService.setPromotedComponent(promotedComponent);
// Point to the resolved component service
compositeService.setPromotedService(promotedService);
} else {
Monitor.error(monitor,
this,
Messages.ASSEMBLY_VALIDATION,
"PromotedServiceNotFound",
((Composite)componentType).getName().toString(),
promotedServiceName);
}
}
}
}
/**
* Connect the references in the component type to the component references that
* they promote
*
* @param componentType
* @param component
*/
private void connectPromotedReferences(ComponentType componentType,
Map<String, Component> components,
Map<String, ComponentReference> componentReferences,
Monitor monitor) {
// Connect composite (component type) references to the component references that they promote
for (Reference reference : componentType.getReferences()) {
CompositeReference compositeReference = (CompositeReference)reference;
List<ComponentReference> promotedReferences = compositeReference.getPromotedReferences();
for (int i = 0, n = promotedReferences.size(); i < n; i++) {
ComponentReference componentReference = promotedReferences.get(i);
if (componentReference.isUnresolved()) {
String componentReferenceName = componentReference.getName();
componentReference = componentReferences.get(componentReferenceName);
if (componentReference != null) {
// Set the promoted component
Component promotedComponent = compositeReference.getPromotedComponents().get(i);
promotedComponent = components.get(promotedComponent.getName());
compositeReference.getPromotedComponents().set(i, promotedComponent);
componentReference.setPromoted(true);
// Point to the resolved component reference
promotedReferences.set(i, componentReference);
} else {
Monitor.error(monitor,
this,
Messages.ASSEMBLY_VALIDATION,
"PromotedReferenceNotFound",
((Composite)componentType).getName().toString(),
componentReferenceName);
}
}
}
}
}
/**
* 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);
}
/**
* The following methods implement rules that the OASIS specification defined explicitly
* to control how configuration from a component type is inherited by a component
*/
/**
* 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 calculatePromotedServiceInterfaceContract(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,
"ServiceInterfaceNotSubSet",
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 calculatePromotedReferenceInterfaceContract(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,
"ReferenceInterfaceNotSubSet",
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 implementation hierarchy take precedence
*
* @param compositeService
* @param promotedComponentService
*/
private void calculatePromotedBindings(CompositeService compositeService, ComponentService promotedComponentService) {
// forward bindings
if (compositeService.getBindings().isEmpty()) {
for (Binding binding : promotedComponentService.getBindings()) {
try {
compositeService.getBindings().add((Binding)binding.clone());
} catch (CloneNotSupportedException ex) {
// this binding can't be used in the promoted service
}
}
}
if (compositeService.getBindings().isEmpty()) {
createSCABinding(compositeService, null);
}
// callback bindings
if (promotedComponentService.getCallback() != null) {
if (compositeService.getCallback() != null) {
compositeService.getCallback().getBindings().clear();
} else {
compositeService.setCallback(assemblyFactory.createCallback());
}
for (Binding binding : promotedComponentService.getCallback().getBindings()) {
try {
compositeService.getCallback().getBindings().add((Binding)binding.clone());
} catch (CloneNotSupportedException ex) {
// this binding can't be used in the promoted service
}
}
}
}
private void reconcileReferenceMultiplicity(ComponentType componentType,
Reference compositeReference,
Reference promotedComponentReference,
Monitor monitor) {
if (compositeReference.getMultiplicity() != null) {
if (!isValidMultiplicityOverride(promotedComponentReference.getTargets().size() > 0,
promotedComponentReference.getMultiplicity(),
compositeReference.getMultiplicity())) {
Monitor.error(monitor,
this,
Messages.ASSEMBLY_VALIDATION,
"CompositeReferenceIncompatibleMultiplicity",
componentType.getURI(),
compositeReference.getName(),
promotedComponentReference.getName());
}
} else {
compositeReference.setMultiplicity(promotedComponentReference.getMultiplicity());
}
}
private boolean isValidMultiplicityOverride(boolean componentRefHasTarget,
Multiplicity componentRefMul,
Multiplicity compositeRefMul) {
if ((componentRefMul != null) &&
(compositeRefMul != null) &&
componentRefMul != compositeRefMul) {
if (componentRefHasTarget){
switch (componentRefMul) {
case ZERO_ONE:
return compositeRefMul == Multiplicity.ZERO_ONE ||
compositeRefMul == Multiplicity.ONE_ONE;
case ONE_ONE:
return compositeRefMul == Multiplicity.ZERO_ONE ||
compositeRefMul == Multiplicity.ONE_ONE;
case ZERO_N:
return true;
case ONE_N:
return true;
default:
return false;
}
} else {
switch (componentRefMul) {
case ZERO_ONE:
return compositeRefMul == Multiplicity.ONE_ONE;
case ONE_ONE:
return compositeRefMul == Multiplicity.ONE_ONE;
case ZERO_N:
return true;
case ONE_N:
return compositeRefMul == Multiplicity.ONE_ONE ||
compositeRefMul == Multiplicity.ONE_N;
default:
return false;
}
}
} else {
return true;
}
}
/**
* ASM50042 - Checks that if a component reference with multiplicity="1..1" is marked
* as nonOveridable then there are no composite references that promote it
*
* @param componentType
* @param compositeReference
* @param promotedComponentReference
* @param monitor
*/
private void validateNonOverridable(ComponentType componentType,
Reference compositeReference,
Reference promotedComponentReference,
Monitor monitor){
if ((promotedComponentReference.getMultiplicity() == Multiplicity.ONE_ONE) &&
(((ComponentReference)promotedComponentReference)).isNonOverridable() == true) {
Monitor.error(monitor,
this,
Messages.ASSEMBLY_VALIDATION,
"CompositeReferencePromotesNonOverridableReference",
componentType.getURI(),
compositeReference.getName(),
promotedComponentReference.getName());
}
}
/**
* 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)
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);
}
} //end class