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 --- .../impl/InterfaceContractMapperImpl.java | 594 +++++++++++++++++++++ 1 file changed, 594 insertions(+) create mode 100644 sca-java-2.x/tags/2.0.1-RC1/modules/assembly/src/main/java/org/apache/tuscany/sca/interfacedef/impl/InterfaceContractMapperImpl.java (limited to 'sca-java-2.x/tags/2.0.1-RC1/modules/assembly/src/main/java/org/apache/tuscany/sca/interfacedef/impl/InterfaceContractMapperImpl.java') diff --git a/sca-java-2.x/tags/2.0.1-RC1/modules/assembly/src/main/java/org/apache/tuscany/sca/interfacedef/impl/InterfaceContractMapperImpl.java b/sca-java-2.x/tags/2.0.1-RC1/modules/assembly/src/main/java/org/apache/tuscany/sca/interfacedef/impl/InterfaceContractMapperImpl.java new file mode 100644 index 0000000000..d04c8b4d97 --- /dev/null +++ b/sca-java-2.x/tags/2.0.1-RC1/modules/assembly/src/main/java/org/apache/tuscany/sca/interfacedef/impl/InterfaceContractMapperImpl.java @@ -0,0 +1,594 @@ +/* + * 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.interfacedef.impl; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.namespace.QName; + +import org.apache.tuscany.sca.assembly.builder.BuilderExtensionPoint; +import org.apache.tuscany.sca.assembly.builder.ContractBuilder; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +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.Operation; +import org.apache.tuscany.sca.interfacedef.util.Audit; +import org.apache.tuscany.sca.interfacedef.util.XMLType; +import org.apache.tuscany.sca.policy.ExtensionType; + +/** + * @version $Rev$ $Date$ + */ +public class InterfaceContractMapperImpl implements InterfaceContractMapper { + + protected ExtensionPointRegistry registry; + protected BuilderExtensionPoint builders; + protected ContractBuilder contractBuilder; + + public InterfaceContractMapperImpl(ExtensionPointRegistry registry){ + this.registry = registry; + this.builders = registry.getExtensionPoint(BuilderExtensionPoint.class); + } + + public boolean isCompatible(DataType source, DataType target, boolean passByValue) { + return isCompatible(source, target, passByValue, null); + } + + public boolean isCompatible(DataType source, DataType target, boolean passByValue, Audit audit) { + if (source == target) { + return true; + } + if (!passByValue) { + if (source == null || target == null) { + if (audit != null){ + audit.append("One of either the source or target data types is null for"); + } + return false; + } + // For local case + return target.getPhysical().isAssignableFrom(source.getPhysical()); + } else { + // For remote interfaces where the target is represented with WSDL + // the source will have been converted to WSDL so we rely on JAXB mappings + // being the same in both cases and just compare the type names directly. + // TODO - is this right? + XMLType sourceLogicalType = null; + + // There is some nesting of data types (when GeneratedDataTypes or arrays are used) so + // dig a bit deeper to find the real data type. Use a loop since for a multidimensional + // array, we might need to go more than one level deep. + while (source.getLogical() instanceof DataType) { + source = (DataType)source.getLogical(); + } + sourceLogicalType = (XMLType)source.getLogical(); + + XMLType targetLogicalType = null; + while (target.getLogical() instanceof DataType) { + target = (DataType)target.getLogical(); + } + targetLogicalType = (XMLType)target.getLogical(); + + // The logical type is null in some cases. This is when the + // runtime can't determine the XML type for a particular type, for + // example for a non-JAXB Java bean. This makes interface checking + // rather lenient with errors being detected at runtime + if (sourceLogicalType.getTypeName() == null || + targetLogicalType.getTypeName() == null) { + return true; + } + + boolean match = sourceLogicalType.getTypeName().equals(targetLogicalType.getTypeName()); + + if (!match){ + + QName anyType = new QName("http://www.w3.org/2001/XMLSchema", "anyType"); + if (sourceLogicalType.getTypeName().equals(anyType) || + targetLogicalType.getTypeName().equals(anyType)){ + // special case where a Java interface uses a generic type, e.g. + // public OMElement getGreetings(OMElement om) + // while the provided WSDL uses a specific type. So we assume + // that xsd:anyType matched anything + match = true; + } else { + if (audit != null){ + audit.append("Operation argument types source = " + + sourceLogicalType.getTypeName() + + " target = " + + targetLogicalType.getTypeName() + + " don't match for"); + } + } + } + + return match; + } + + } + + /** + * Check that two interface contracts are equal. The contracts are equal if the two contracts have the + * same set of operations, with each operation having the same signature, both for forward and callback + * interfaces + * @param source + * @param target + * @return + */ + public boolean isMutuallyCompatible(InterfaceContract source, InterfaceContract target) { + ExtensionType ext = source.getInterface().getExtensionType(); + InterfaceContract sourceContract = null; + + // Are the forward interfaces equal? + if (isMutuallyCompatible(source.getInterface(), target.getInterface())) { + // Is there a Callback interface? + if (source.getCallbackInterface() == null && target.getCallbackInterface() == null) { + return true; + } else { + if (isMutuallyCompatible(source.getCallbackInterface(), target.getCallbackInterface())) { + return true; + } // end if + } // end if + } // end if + return false; + } // end method isEqual + + /** + * Check that two interfaces are equal. The interfaces are equal if the two interfaces have the + * same set of operations, with each operation having the same signature. + * @param source + * @param target + * @return + */ + public boolean isMutuallyCompatible(Interface source, Interface target) { + if (source == target) { + // Shortcut for performance + return true; + } // end if + if (source == null || target == null) { + return false; + } // end if + + if (source.isDynamic() || target.isDynamic()) { + return true; + } + + if (source.isRemotable() != target.isRemotable()) { + return false; + } + if (source.getOperations().size() != target.getOperations().size()) { + return false; + } + + for (Operation operation : source.getOperations()) { + Operation targetOperation = getOperation(target.getOperations(), operation.getName()); + if (targetOperation == null) { + return false; + } + if (!isCompatible(operation, targetOperation, Compatibility.SUBSET)) { + return false; + } + } + return true; + } // end method isEqual + + public boolean isCompatible(Operation source, Operation target, Compatibility compatibilityType) { + return isCompatible(source, target, compatibilityType, true); + } + + public boolean isCompatible(Operation source, Operation target, Compatibility compatibilityType, boolean byValue) { + return isCompatible(source, target, compatibilityType, true, null); + } + + public boolean isCompatible(Operation source, Operation target, Compatibility compatibilityType, boolean byValue, Audit audit) { + if (source == target) { + return true; + } + + if (source.isDynamic() || target.isDynamic()) { + return true; + } + + // Check name + if (!source.getName().equals(target.getName())) { + if (audit != null){ + audit.append("operation names are not the same source = " + + source.getName() + + " target = " + + target.getName()); + audit.appendSeperator(); + } + return false; + } + + if (source.getInterface().isRemotable() != target.getInterface().isRemotable()) { + if (audit != null){ + audit.append("Interfaces have different remote settings source = " + + source.getName() + + " target = " + + target.getName()); + audit.appendSeperator(); + } + return false; + } + + if (source.isNonBlocking() != target.isNonBlocking()) { + if (audit != null){ + audit.append("operations one-way not the same, source = " + + source.isNonBlocking() + + " target = " + + target.isNonBlocking()); + audit.appendSeperator(); + } + return false; + } + + boolean passByValue = (source.getInterface().isRemotable()) && byValue; + + // FIXME: We need to deal with wrapped<-->unwrapped conversion + + List sourceOutputType = source.getOutputType().getLogical(); + List targetOutputType = target.getOutputType().getLogical(); + + List sourceInputType = source.getInputType().getLogical(); + List targetInputType = target.getInputType().getLogical(); + + if (source.isInputWrapperStyle() && source.getInputWrapper() != null) { + sourceInputType = source.getInputWrapper().getUnwrappedType().getLogical(); + } + + if (source.isOutputWrapperStyle() && source.getOutputWrapper() != null) { + sourceOutputType = source.getOutputWrapper().getUnwrappedType().getLogical(); + } + + if (target.isInputWrapperStyle() && target.getInputWrapper() != null) { + targetInputType = target.getInputWrapper().getUnwrappedType().getLogical(); + } + + if (target.isOutputWrapperStyle() && target.getOutputWrapper() != null) { + targetOutputType = target.getOutputWrapper().getUnwrappedType().getLogical(); + } + + if ( sourceOutputType.size() != targetOutputType.size()) { + if (audit != null){ + audit.append("different number of output types"); + audit.appendSeperator(); + } + return false; + } + + for ( int i=0; i < sourceOutputType.size(); i++) { + if (!isCompatible(targetOutputType.get(i), sourceOutputType.get(i), passByValue, audit)) { + if (audit != null){ + audit.append(" output types"); + audit.appendSeperator(); + } + return false; + } + } + + if (sourceInputType.size() != targetInputType.size()) { + if (audit != null){ + audit.append("different number of input types"); + audit.appendSeperator(); + } + return false; + } + + int size = sourceInputType.size(); + for (int i = 0; i < size; i++) { + if (!isCompatible(sourceInputType.get(i), targetInputType.get(i), passByValue, audit)) { + if (audit != null){ + audit.append(" input types"); + audit.appendSeperator(); + } + return false; + } + } + + // Check fault types + for (DataType targetFaultType : target.getFaultTypes()) { + // Source fault types must be the same or superset of target fault + // types + boolean found = true; + for (DataType sourceFaultType : source.getFaultTypes()) { + found = false; + if (isCompatible(targetFaultType, sourceFaultType, passByValue, audit)) { + // Target fault type can be covered by the source fault type + found = true; + break; + } + } + if (!found) { + if (audit != null){ + audit.append("Fault types incompatible"); + audit.appendSeperator(); + } + return false; + } + } + + return true; + } + public boolean isCompatibleByReference(Operation source, Operation target, Compatibility compatibilityType) { + return isCompatible(source, target, compatibilityType, false); + } + + public boolean isCompatibleByValue(Operation source, Operation target, Compatibility compatibilityType) { + return isCompatible(source, target, compatibilityType, true); + } + + @Override + public boolean isCompatibleWithoutUnwrapByValue(Operation source, Operation target, Compatibility compatibilityType) { + if (!source.isInputWrapperStyle() == target.isInputWrapperStyle()) { + return false; + } else if (!source.isOutputWrapperStyle() == target.isOutputWrapperStyle()) { + return false; + } else { + return isCompatible(source, target, compatibilityType, true); + } + } + + // FIXME: How to improve the performance for the lookup + private Operation getOperation(List operations, String name) { + for (Operation op : operations) { + if (op.getName().equals(name)) { + return op; + } + } + return null; + } + + /* + * this variant of the checkCompatibility method is intended to supersede the one without an audit argument + * Presence of both method variants indicates a state of partial development + */ + public boolean checkCompatibility(InterfaceContract source, + InterfaceContract target, + Compatibility compatibility, + boolean ignoreCallback, + boolean silent, + Audit audit) + throws IncompatibleInterfaceContractException { + + if (source == target) { + // Shortcut for performance + return true; + } + + if (source == null || target == null) { + return false; + } + + if (source.getInterface() == target.getInterface()) { + return ignoreCallback + || isCallbackCompatible(source, target, silent, audit); + } + + if (source.getInterface() == null || target.getInterface() == null) { + return false; + } + + if (source.getInterface().isDynamic() + || target.getInterface().isDynamic()) { + return ignoreCallback + || isCallbackCompatible(source, target, silent, audit); + } + + if (source.getInterface().isRemotable() != target.getInterface() + .isRemotable()) { + if (!silent) { + audit.append("Remotable settings do not match: "+ source + "," + target); // TODO see if serialization is sufficient + audit.appendSeperator(); + throw new IncompatibleInterfaceContractException( + "Remotable settings do not match", source, target); + + } else { + return false; + } + } + + for (Operation operation : source.getInterface().getOperations()) { + Operation targetOperation = map(target.getInterface(), operation); + if (targetOperation == null) { + if (!silent) { + audit.append("Operation " + operation.getName()+ " not found on target"); + audit.appendSeperator(); + throw new IncompatibleInterfaceContractException( + "Operation " + operation.getName() + + " not found on target", source, target); + } else { + return false; + } + } + + if (!silent) { + if (audit == null) + audit = new Audit(); + if (!isCompatible(operation, targetOperation, + Compatibility.SUBSET, true, audit)) { + audit.append("Operations called " + operation.getName()+ " are not compatible"); + audit.appendSeperator(); + throw new IncompatibleInterfaceContractException( + "Operations called " + operation.getName() + + " are not compatible " + audit, source, + target); + } + } else { + if (!isCompatible(operation, targetOperation, + Compatibility.SUBSET)) { + return false; + } + } + } + + return ignoreCallback || isCallbackCompatible(source, target, silent, audit); + } + + /* + * The old checkCompatibility operation without auditing. This just delegates to the new one for the time + * being while there are still calls that don't provide and audit object. In the medium term when the calls have + * been converted to sue the new opetion directly this should be removed. + */ + public boolean checkCompatibility(InterfaceContract source, + InterfaceContract target, + Compatibility compatibility, + boolean ignoreCallback, + boolean silent) + throws IncompatibleInterfaceContractException { + + // create dummy audit object. + Audit audit = new Audit(); + + return checkCompatibility(source, + target, + compatibility, + ignoreCallback, + silent, + audit); + } + + + + protected boolean isCallbackCompatible(InterfaceContract source, InterfaceContract target, boolean silent, Audit audit) + throws IncompatibleInterfaceContractException { + if (source.getCallbackInterface() == null && target.getCallbackInterface() == null) { + return true; + } + if (source.getCallbackInterface() == null || target.getCallbackInterface() == null) { + if (!silent) { + audit.append("Callback interface doesn't match as one of the callback interfaces is null"); + audit.appendSeperator(); + throw new IncompatibleInterfaceContractException("Callback interface doesn't match as one of the callback interfaces is null", source, target); + } else { + return false; + } + } + + for (Operation operation : source.getCallbackInterface().getOperations()) { + Operation targetOperation = + getOperation(target.getCallbackInterface().getOperations(), operation.getName()); + if (targetOperation == null) { + if (!silent) { + audit.append("Callback operation not found on target " + operation.getName()); + audit.appendSeperator(); + throw new IncompatibleInterfaceContractException("Callback operation not found on target", source, + target, null, targetOperation); + } else { + return false; + } + } + if (!source.getCallbackInterface().isRemotable()) { + // FIXME: for remotable operation, only compare name for now + if (!operation.equals(targetOperation)) { + if (!silent) { + audit.append("Target callback operation is not compatible " + operation.getName()); + audit.appendSeperator(); + throw new IncompatibleInterfaceContractException("Target callback operation is not compatible", + source, target, operation, targetOperation); + } else { + return false; + } + } + } + } + return true; + } + + public boolean isCompatibleSubset(Interface source, Interface target) { + if (source == target) { + // Shortcut for performance + return true; + } + if (source == null || target == null) { + return false; + } + + if (source.isDynamic() || target.isDynamic()) { + return true; + } + + if (source.isRemotable() != target.isRemotable()) { + return false; + } + + for (Operation operation : source.getOperations()) { + Operation targetOperation = getOperation(target.getOperations(), operation.getName()); + if (targetOperation == null) { + return false; + } + if (!isCompatible(operation, targetOperation, Compatibility.SUBSET)) { + return false; + } + } + return true; + } + + /* + * the variant of isCompatibleSubset with the audit parameter is intended to supersede the other + * -- the presence of both indicates a partial development state + */ + public boolean isCompatibleSubset(InterfaceContract source, InterfaceContract target, Audit audit) { + + try { + return checkCompatibility(source, target, Compatibility.SUBSET, false, false, audit); + } catch (IncompatibleInterfaceContractException e) { + return false; + } + } + + public boolean isCompatibleSubset(InterfaceContract source, InterfaceContract target) { + + try { + return checkCompatibility(source, target, Compatibility.SUBSET, false, false); + } catch (IncompatibleInterfaceContractException e) { + return false; + } + } + + /** + * @see org.apache.tuscany.sca.interfacedef.InterfaceContractMapper#map(org.apache.tuscany.sca.interfacedef.Interface, + * org.apache.tuscany.sca.interfacedef.Operation) + */ + public Operation map(Interface target, Operation source) { + // TODO: How to handle the case that source operation is dynamic? + if (target == null || target.isDynamic()) { + return source; + } else if (target.isRemotable()) { + for (Operation op : target.getOperations()) { + if (op.getName().equals(source.getName())) { + return op; + } + } + return null; + } else { + for (Operation op : target.getOperations()) { + if (isCompatible(source, op, Compatibility.SUBSET)) { + return op; + } + } + return null; + } + } + +} -- cgit v1.2.3