From 51e4f54ea42fc2009a99577e98f1af048d7841bd Mon Sep 17 00:00:00 2001 From: rfeng Date: Fri, 1 May 2009 04:21:18 +0000 Subject: Refactor the data transformation and copy logic into Mediator utility git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@770532 13f79535-47bb-0310-9956-ffa450edef68 --- .../module/DataBindingModuleActivator.java | 1 + .../wire/DataBindingRuntimeWireProcessor.java | 17 +- .../wire/DataTransformationInterceptor.java | 198 +---------- .../databinding/wire/PassByValueInterceptor.java | 212 +----------- .../apache/tuscany/sca/databinding/Mediator.java | 113 ++++++- .../tuscany/sca/databinding/impl/MediatorImpl.java | 372 ++++++++++++++++++++- .../impl/TransformationContextImpl.java | 18 +- .../javabeans/JavaBeansDataBinding.java | 52 ++- 8 files changed, 557 insertions(+), 426 deletions(-) diff --git a/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/module/DataBindingModuleActivator.java b/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/module/DataBindingModuleActivator.java index 9e926506f4..4e0e58e6a1 100644 --- a/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/module/DataBindingModuleActivator.java +++ b/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/module/DataBindingModuleActivator.java @@ -62,6 +62,7 @@ public class DataBindingModuleActivator implements ModuleActivator { xmlAdapterExtensionPoint.addAdapter(CallableReference.class, CallableReferenceXMLAdapter.class); xmlAdapterExtensionPoint.addAdapter(OMElement.class, OMElementXMLAdapter.class); FaultExceptionMapper faultExceptionMapper = new JAXWSFaultExceptionMapper(dataBindings, xmlAdapterExtensionPoint); + registry.getExtensionPoint(UtilityExtensionPoint.class).addUtility(faultExceptionMapper); Mediator mediator = registry.getExtensionPoint(UtilityExtensionPoint.class).getUtility(Mediator.class); diff --git a/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/wire/DataBindingRuntimeWireProcessor.java b/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/wire/DataBindingRuntimeWireProcessor.java index 4e67c4dee0..bad2be6679 100644 --- a/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/wire/DataBindingRuntimeWireProcessor.java +++ b/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/wire/DataBindingRuntimeWireProcessor.java @@ -6,15 +6,15 @@ * 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. + * under the License. */ package org.apache.tuscany.sca.core.databinding.wire; @@ -37,7 +37,7 @@ import org.apache.tuscany.sca.runtime.RuntimeWireProcessor; /** * This processor is responsible to add an interceptor to invocation chain if * the source and target operations have different databinding requirements - * + * * @version $Rev$ $Date$ */ public class DataBindingRuntimeWireProcessor implements RuntimeWireProcessor { @@ -88,7 +88,7 @@ public class DataBindingRuntimeWireProcessor implements RuntimeWireProcessor { if (source.isInputWrapperStyle() != target.isInputWrapperStyle()) { return true; } - + if (source.isOutputWrapperStyle() != target.isOutputWrapperStyle()) { return true; } @@ -153,14 +153,13 @@ public class DataBindingRuntimeWireProcessor implements RuntimeWireProcessor { // Add the interceptor to the source side because multiple // references can be wired to the same service interceptor = - new DataTransformationInterceptor(wire, sourceOperation, targetOperation, mediator, - faultExceptionMapper); + new DataTransformationInterceptor(wire, sourceOperation, targetOperation, mediator); } else { // assume pass-by-values copies are required if interfaces are remotable and there is no data binding // transformation, i.e. a transformation will result in a copy so another pass-by-value copy is unnecessary if (!isOnMessage(targetOperation) && isRemotable(chain, sourceOperation, targetOperation)) { interceptor = - new PassByValueInterceptor(dataBindings, faultExceptionMapper, chain, targetOperation); + new PassByValueInterceptor(mediator, chain, targetOperation); } } if (interceptor != null) { @@ -176,7 +175,7 @@ public class DataBindingRuntimeWireProcessor implements RuntimeWireProcessor { /** * FIXME: TUSCANY-2586, temporary work around till the JIRA is fixed to prevent * the PassByValueInterceptor being used for services when the binding protocol - * doesn't need the copies done. + * doesn't need the copies done. */ protected boolean isOnMessage(Operation op) { return "onMessage".equals(op.getName()); diff --git a/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/wire/DataTransformationInterceptor.java b/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/wire/DataTransformationInterceptor.java index 66b5cabb11..79c442450b 100644 --- a/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/wire/DataTransformationInterceptor.java +++ b/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/wire/DataTransformationInterceptor.java @@ -19,28 +19,16 @@ package org.apache.tuscany.sca.core.databinding.wire; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; -import javax.xml.namespace.QName; - -import org.apache.tuscany.sca.databinding.DataBinding; import org.apache.tuscany.sca.databinding.Mediator; -import org.apache.tuscany.sca.interfacedef.DataType; -import org.apache.tuscany.sca.interfacedef.FaultExceptionMapper; import org.apache.tuscany.sca.interfacedef.Operation; -import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; -import org.apache.tuscany.sca.interfacedef.java.JavaOperation; -import org.apache.tuscany.sca.interfacedef.util.FaultException; -import org.apache.tuscany.sca.interfacedef.util.XMLType; import org.apache.tuscany.sca.invocation.DataExchangeSemantics; import org.apache.tuscany.sca.invocation.Interceptor; import org.apache.tuscany.sca.invocation.Invoker; import org.apache.tuscany.sca.invocation.Message; import org.apache.tuscany.sca.runtime.RuntimeWire; -import org.osoa.sca.ServiceRuntimeException; /** * An interceptor to transform data across databindings on the wire @@ -55,24 +43,16 @@ public class DataTransformationInterceptor implements Interceptor, DataExchangeS private Operation targetOperation; private RuntimeWire wire; private Mediator mediator; - private FaultExceptionMapper faultExceptionMapper; public DataTransformationInterceptor(RuntimeWire wire, Operation sourceOperation, Operation targetOperation, - Mediator mediator, - FaultExceptionMapper faultExceptionMapper) { + Mediator mediator) { super(); this.sourceOperation = sourceOperation; this.targetOperation = targetOperation; - if ( sourceOperation instanceof JavaOperation ) { - JavaOperation javaOp = (JavaOperation) sourceOperation; - Method sourceMethod = javaOp.getJavaMethod(); - } - this.mediator = mediator; this.wire = wire; - this.faultExceptionMapper = faultExceptionMapper; } public Invoker getNext() { @@ -80,9 +60,9 @@ public class DataTransformationInterceptor implements Interceptor, DataExchangeS } public Message invoke(Message msg) { - // System.out.println( "DataTransformationInterceptor.invoke source input type=" + sourceOperation.getInputType() ); - // System.out.println( "DataTransformationInterceptor.invoke target input type =" + targetOperation.getInputType() ); - Object input = transform(msg.getBody(), sourceOperation.getInputType(), targetOperation.getInputType(), false); + Map metadata = new HashMap(); + metadata.put("wire", wire); + Object input = mediator.mediateInput(msg.getBody(), sourceOperation, targetOperation, metadata); msg.setBody(input); Message resultMsg = next.invoke(msg); if (sourceOperation.isNonBlocking()) { @@ -91,178 +71,28 @@ public class DataTransformationInterceptor implements Interceptor, DataExchangeS } Object result = resultMsg.getBody(); - // FIXME: Should we fix the Operation model so that getOutputType - // returns DataType>? - DataType targetType = - new DataTypeImpl(DataBinding.IDL_OUTPUT, Object.class, targetOperation.getOutputType()); - - DataType sourceType = - new DataTypeImpl(DataBinding.IDL_OUTPUT, Object.class, sourceOperation.getOutputType()); if (resultMsg.isFault()) { - - // FIXME: We need to figure out what fault type it is and then - // transform it - // back the source fault type - // throw new InvocationRuntimeException((Throwable) result); - + Object transformedFault = null; if ((result instanceof Exception) && !(result instanceof RuntimeException)) { - // FIXME: How to match fault data to a fault type for the - // operation? - - // If the result is from an InvocationTargetException look at - // the actual cause. - if (result instanceof InvocationTargetException) { - result = ((InvocationTargetException)result).getCause(); - } - DataType targetDataType = null; - for (DataType exType : targetOperation.getFaultTypes()) { - if (((Class)exType.getPhysical()).isInstance(result)) { - if (result instanceof FaultException) { - DataType faultType = (DataType)exType.getLogical(); - if (((FaultException)result).isMatchingType(faultType.getLogical())) { - targetDataType = exType; - break; - } - } else { - targetDataType = exType; - break; - } - } - } - - /* - if (targetDataType == null) { - // Not a business exception - return resultMsg; - } - */ - - DataType targetFaultType = getFaultType(targetDataType); - if (targetFaultType == null) { - // No matching fault type, it's a system exception - Throwable cause = (Throwable) result; - throw new ServiceRuntimeException(cause); - } - - // FIXME: How to match a source fault type to a target fault - // type? - DataType sourceDataType = null; - DataType sourceFaultType = null; - for (DataType exType : sourceOperation.getFaultTypes()) { - DataType faultType = getFaultType(exType); - // Match by the QName (XSD element) of the fault type - if (faultType != null && typesMatch(targetFaultType.getLogical(), faultType.getLogical())) { - sourceDataType = exType; - sourceFaultType = faultType; - break; - } - } - - if (sourceFaultType == null) { - // No matching fault type, it's a system exception - Throwable cause = (Throwable) result; - throw new ServiceRuntimeException(cause); - } - - Object newResult = - transformException(result, targetDataType, sourceDataType, targetFaultType, sourceFaultType); - if (newResult != result) { - resultMsg.setFaultBody(newResult); + transformedFault = mediator.mediateFault(result, sourceOperation, targetOperation, metadata); + if (transformedFault != result) { + resultMsg.setFaultBody(transformedFault); } } - + // + // Leave it to another layer to actually throw the Exception which constitutes + // the message body. We don't throw it here. + // } else { - // !resultMsg.isFault() assert !(result instanceof Throwable) : "Expected messages that are not throwable " + result; - - Object newResult = transform(result, targetType, sourceType, true); - if (newResult != result) { - resultMsg.setBody(newResult); - } + Object newResult = mediator.mediateOutput(result, sourceOperation, targetOperation, metadata); + resultMsg.setBody(newResult); } return resultMsg; } - private Object transform(Object source, DataType sourceType, DataType targetType, boolean isResponse) { - if (sourceType == targetType || (sourceType != null && sourceType.equals(targetType))) { - return source; - } - Map metadata = new HashMap(); - metadata.put("source.operation", isResponse ? targetOperation : sourceOperation); - metadata.put("target.operation", isResponse ? sourceOperation : targetOperation); - metadata.put("wire", wire); - return mediator.mediate(source, sourceType, targetType, metadata); - } - - private DataType getFaultType(DataType exceptionType) { - return exceptionType == null ? null : (DataType)exceptionType.getLogical(); - } - - private boolean typesMatch(Object first, Object second) { - if (first.equals(second)) { - return true; - } - if (first instanceof XMLType && second instanceof XMLType) { - XMLType t1 = (XMLType)first; - XMLType t2 = (XMLType)second; - // TUSCANY-2113, we should compare element names only - return matches(t1.getElementName(), t2.getElementName()); - } - return false; - } - - /** - * @param qn1 - * @param qn2 - */ - private boolean matches(QName qn1, QName qn2) { - if (qn1 == qn2) { - return true; - } - if (qn1 == null || qn2 == null) { - return false; - } - String ns1 = qn1.getNamespaceURI(); - String ns2 = qn2.getNamespaceURI(); - String e1 = qn1.getLocalPart(); - String e2 = qn2.getLocalPart(); - if (e1.equals(e2) && (ns1.equals(ns2) || ns1.equals(ns2 + "/") || ns2.equals(ns1 + "/"))) { - // Tolerating the trailing / which is required by JAX-WS java package --> xml ns mapping - return true; - } - return false; - } - - /** - * @param source The source exception - * @param sourceExType The data type for the source exception - * @param targetExType The data type for the target exception - * @param sourceType The fault type for the source - * @param targetType The fault type for the target - * @return - */ - private Object transformException(Object source, - DataType sourceExType, - DataType targetExType, - DataType sourceType, - DataType targetType) { - if (sourceType == targetType || (sourceType != null && sourceType.equals(targetType))) { - return source; - } - Map metadata = new HashMap(); - metadata.put("source.operation", targetOperation); - metadata.put("target.operation", sourceOperation); - metadata.put("wire", wire); - DataType eSourceDataType = - new DataTypeImpl("idl:fault", sourceExType.getPhysical(), sourceType); - DataType eTargetDataType = - new DataTypeImpl("idl:fault", targetExType.getPhysical(), targetType); - - return mediator.mediate(source, eSourceDataType, eTargetDataType, metadata); - } - public void setNext(Invoker next) { this.next = next; } diff --git a/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/wire/PassByValueInterceptor.java b/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/wire/PassByValueInterceptor.java index 3586f54d71..44e5072e61 100644 --- a/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/wire/PassByValueInterceptor.java +++ b/branches/sca-java-1.x/modules/core-databinding/src/main/java/org/apache/tuscany/sca/core/databinding/wire/PassByValueInterceptor.java @@ -6,45 +6,25 @@ * 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. + * under the License. */ package org.apache.tuscany.sca.core.databinding.wire; -import java.io.Serializable; -import java.lang.reflect.Array; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.URI; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import javax.xml.namespace.QName; - -import org.apache.tuscany.sca.databinding.DataBinding; -import org.apache.tuscany.sca.databinding.DataBindingExtensionPoint; -import org.apache.tuscany.sca.databinding.javabeans.JavaBeansDataBinding; -import org.apache.tuscany.sca.databinding.jaxb.JAXBDataBinding; -import org.apache.tuscany.sca.interfacedef.DataType; -import org.apache.tuscany.sca.interfacedef.FaultExceptionMapper; +import org.apache.tuscany.sca.databinding.Mediator; import org.apache.tuscany.sca.interfacedef.Operation; -import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; -import org.apache.tuscany.sca.interfacedef.util.XMLType; import org.apache.tuscany.sca.invocation.Interceptor; import org.apache.tuscany.sca.invocation.InvocationChain; import org.apache.tuscany.sca.invocation.Invoker; import org.apache.tuscany.sca.invocation.Message; -import org.osoa.sca.ServiceRuntimeException; /** * Implementation of an interceptor that enforces pass-by-value semantics @@ -54,13 +34,7 @@ import org.osoa.sca.ServiceRuntimeException; */ public class PassByValueInterceptor implements Interceptor { - private DataBindingExtensionPoint dataBindings; - private FaultExceptionMapper faultExceptionMapper; - - private DataBinding[] inputDataBindings; - private DataBinding outputDataBinding; - private DataBinding javaBeanDataBinding; - private DataBinding jaxbDataBinding; + private Mediator mediator; private Operation operation; private Invoker nextInvoker; private InvocationChain chain; @@ -70,36 +44,10 @@ public class PassByValueInterceptor implements Interceptor { * @param dataBindings databinding extension point * @param operation the intercepted operation */ - public PassByValueInterceptor(DataBindingExtensionPoint dataBindings, - FaultExceptionMapper faultExceptionMapper, - InvocationChain chain, - Operation operation) { + public PassByValueInterceptor(Mediator mediator, InvocationChain chain, Operation operation) { + this.mediator = mediator; this.chain = chain; this.operation = operation; - - // Cache data bindings to use - this.dataBindings = dataBindings; - this.faultExceptionMapper = faultExceptionMapper; - - jaxbDataBinding = dataBindings.getDataBinding(JAXBDataBinding.NAME); - javaBeanDataBinding = dataBindings.getDataBinding(JavaBeansDataBinding.NAME); - - // Determine the input databindings - if (operation.getInputType() != null) { - List inputTypes = operation.getInputType().getLogical(); - inputDataBindings = new DataBinding[inputTypes.size()]; - int i = 0; - for (DataType inputType : inputTypes) { - String id = inputType.getDataBinding(); - inputDataBindings[i++] = dataBindings.getDataBinding(id); - } - } - - // Determine the output databinding - if (operation.getOutputType() != null) { - String id = operation.getOutputType().getDataBinding(); - outputDataBinding = dataBindings.getDataBinding(id); - } } public Message invoke(Message msg) { @@ -107,156 +55,20 @@ public class PassByValueInterceptor implements Interceptor { return nextInvoker.invoke(msg); } - msg.setBody(copy((Object[])msg.getBody(), inputDataBindings, operation.getInputType().getLogical())); + msg.setBody(mediator.copyInput(msg.getBody(), operation)); Message resultMsg = nextInvoker.invoke(msg); - if (!msg.isFault() && operation.getOutputType() != null) { - resultMsg.setBody(copy(resultMsg.getBody(), outputDataBinding, operation.getOutputType())); + if (!resultMsg.isFault() && operation.getOutputType() != null) { + resultMsg.setBody(mediator.copyOutput(resultMsg.getBody(), operation)); } - if (msg.isFault()) { - msg.setFaultBody(copyFault(msg.getBody())); + if (resultMsg.isFault()) { + resultMsg.setFaultBody(mediator.copyFault(resultMsg.getBody(), operation)); } return resultMsg; } - private Object copyFault(Object fault) { - if (faultExceptionMapper == null) { - return fault; - } - for (DataType et : operation.getFaultTypes()) { - if (et.getPhysical().isInstance(fault)) { - Throwable ex = (Throwable)fault; - DataType exType = - new DataTypeImpl(ex.getClass(), new DataTypeImpl(ex.getClass(), XMLType.UNKNOWN)); - faultExceptionMapper.introspectFaultDataType(exType, operation, false); - DataType faultType = exType.getLogical(); - Object faultInfo = faultExceptionMapper.getFaultInfo(ex, faultType.getPhysical(), operation); - faultInfo = copy(faultInfo, dataBindings.getDataBinding(faultType.getDataBinding()), faultType); - fault = faultExceptionMapper.wrapFaultInfo(exType, ex.getMessage(), faultInfo, ex.getCause(), operation); - return fault; - } - } - return fault; - } - - /** - * Copy an array of data objects passed to an operation - * @param data array of objects to copy - * @return the copy - */ - private Object[] copy(Object[] data, DataBinding[] dataBindings, List dataTypes) { - if (data == null) { - return null; - } - Object[] copy = new Object[data.length]; - Map map = new IdentityHashMap(); - for (int i = 0; i < data.length; i++) { - Object arg = data[i]; - if (arg == null) { - copy[i] = null; - } else { - Object copiedArg = map.get(arg); - if (copiedArg != null) { - copy[i] = copiedArg; - } else { - copiedArg = copy(arg, dataBindings[i], dataTypes.get(i)); - map.put(arg, copiedArg); - copy[i] = copiedArg; - } - } - } - return copy; - } - - /** - * Copy data using the specified databinding. - * @param data input data - * @param dataBinding databinding to use - * @param dataType TODO - * @return a copy of the data - */ - private Object copy(Object data, DataBinding dataBinding, DataType dataType) { - if (data == null) { - return null; - } - Class clazz = data.getClass(); - if (String.class == clazz || clazz.isPrimitive() - || Number.class.isAssignableFrom(clazz) - || Boolean.class.isAssignableFrom(clazz) - || Character.class.isAssignableFrom(clazz) - || Byte.class.isAssignableFrom(clazz) - || URI.class == clazz - || UUID.class == clazz - || QName.class == clazz) { - // Immutable classes - return data; - } - // If no databinding was specified, introspect the given arg to - // determine its databinding - if (dataBinding == null) { - dataType = dataBindings.introspectType(data, operation); - if (dataType != null) { - String db = dataType.getDataBinding(); - dataBinding = dataBindings.getDataBinding(db); - if (dataBinding == null && db != null) { - return data; - } - } - if (dataBinding == null) { - - // Default to the JavaBean databinding - dataBinding = javaBeanDataBinding; - } - } - - // Use the JAXB databinding to copy non-Serializable data - if (dataBinding == javaBeanDataBinding) { - - // If the input data is an array containing non Serializable elements - // use JAXB - clazz = data.getClass(); - if (clazz.isArray()) { - if (Array.getLength(data) != 0) { - Object element = Array.get(data, 0); - if (element != null && !(element instanceof Serializable)) { - dataBinding = jaxbDataBinding; - } - } - } else { - - // If the input data is not Serializable use JAXB - if (!(data instanceof Serializable)) { - dataBinding = jaxbDataBinding; - } - - if (data instanceof Cloneable) { - Method clone; - try { - clone = data.getClass().getMethod("clone", (Class[])null); - try { - return clone.invoke(data, (Object[])null); - } catch (InvocationTargetException e) { - if (e.getTargetException() instanceof CloneNotSupportedException) { - // Ignore - } else { - throw new ServiceRuntimeException(e); - } - } catch (Exception e) { - throw new ServiceRuntimeException(e); - } - } catch (NoSuchMethodException e) { - // Ignore it - } - } - } - } - - Object copy = dataBinding.copy(data, dataType, operation); - return copy; - } - public Invoker getNext() { return nextInvoker; } diff --git a/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/Mediator.java b/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/Mediator.java index 6b8acfc7b4..8184d739a8 100644 --- a/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/Mediator.java +++ b/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/Mediator.java @@ -6,25 +6,26 @@ * 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. + * under the License. */ package org.apache.tuscany.sca.databinding; import java.util.Map; import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.Operation; /** * This interface will be used as a Tuscany system service to perform data mediations - * + * * Mediate the data from one type to the other one * * @version $Rev$ $Date$ @@ -36,10 +37,11 @@ public interface Mediator { * @param source The data to be mediated * @param sourceDataType Data type for the source data * @param targetDataType Data type for the target data - * @param context + * @param context * @return */ Object mediate(Object source, DataType sourceDataType, DataType targetDataType, Map context); + /** * Mediate the source data into the target which is a sink to receive the data * @param source The data to be mediated @@ -47,22 +49,105 @@ public interface Mediator { * @param sourceDataType Data type for the source data * @param targetDataType Data type for the target data */ - void mediate( - Object source, - Object target, - DataType sourceDataType, - DataType targetDataType, - Map context); - + void mediate(Object source, + Object target, + DataType sourceDataType, + DataType targetDataType, + Map context); + + /** + * Transform the input parameters for the source operation to the expected parameters for + * the target operation + * @param input The input data, typically an array of parameters + * @param sourceOperation The source operation + * @param targetOperation The target operation + * @param metadata Additional metadata + * @return The transformed input data for the target operation + */ + Object mediateInput(Object input, Operation sourceOperation, Operation targetOperation, Map metadata); + + /** + * Transform the return value for the target operation to the expected return value for + * the source operation + * @param output The output data, typically the return value + * @param sourceOperation The source operation + * @param targetOperation The target operation + * @param metadata Additional metadata + * @return The transformed output data for the source operation + */ + Object mediateOutput(Object output, + Operation sourceOperation, + Operation targetOperation, + Map metadata); + + /** + * Transform the fault data for the target operation to the expected fault data for + * the source operation + * @param fault The fault data, such as Java exception or fault message + * @param sourceOperation The source operation + * @param targetOperation The target operation + * @param metadata Additional metadata + * @return The transformed fault data for the source operation + */ + Object mediateFault(Object fault, Operation sourceOperation, Operation targetOperation, Map metadata); + + /** + * Copy the data + * @param data The orginal data + * @param dataType The data type + * @return The copy + */ + Object copy(Object data, DataType dataType); + + /** + * Copy an array of data objects passed to an operation + * @param data array of objects to copy + * @return the copy + */ + public Object copyInput(Object input, Operation operation); + + /** + * Copy the output data + * @param data The orginal output + * @param operation The operation + * @return The copy + */ + Object copyOutput(Object data, Operation operation); + + /** + * Copy the fault data + * @param fault The orginal fault data + * @param operation The operation + * @return The copy + */ + Object copyFault(Object fault, Operation operation); + /** * Get the DataBindings used by this mediator. * @return */ DataBindingExtensionPoint getDataBindings(); - + /** * Get the Transformers used by this mediator. * @return */ - TransformerExtensionPoint getTransformers(); + TransformerExtensionPoint getTransformers(); + + /** + * Create an instance of TransformationContext + * @return + */ + TransformationContext createTransformationContext(); + + /** + * Create an instance of TransformationContext + * @param sourceDataType + * @param targetDataType + * @param metadata + * @return + */ + TransformationContext createTransformationContext(DataType sourceDataType, + DataType targetDataType, + Map metadata); } diff --git a/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/impl/MediatorImpl.java b/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/impl/MediatorImpl.java index 08d7301f02..3594aea139 100644 --- a/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/impl/MediatorImpl.java +++ b/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/impl/MediatorImpl.java @@ -18,13 +18,26 @@ */ package org.apache.tuscany.sca.databinding.impl; +import static org.apache.tuscany.sca.databinding.DataBinding.IDL_FAULT; +import static org.apache.tuscany.sca.databinding.DataBinding.IDL_OUTPUT; + +import java.io.Serializable; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.net.URI; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import java.util.UUID; + +import javax.xml.namespace.QName; import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.core.UtilityExtensionPoint; +import org.apache.tuscany.sca.databinding.DataBinding; import org.apache.tuscany.sca.databinding.DataBindingExtensionPoint; import org.apache.tuscany.sca.databinding.DataPipe; import org.apache.tuscany.sca.databinding.DataPipeTransformer; @@ -35,9 +48,14 @@ import org.apache.tuscany.sca.databinding.TransformationContext; import org.apache.tuscany.sca.databinding.TransformationException; import org.apache.tuscany.sca.databinding.Transformer; import org.apache.tuscany.sca.databinding.TransformerExtensionPoint; +import org.apache.tuscany.sca.databinding.javabeans.JavaBeansDataBinding; import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.FaultExceptionMapper; import org.apache.tuscany.sca.interfacedef.Operation; import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; +import org.apache.tuscany.sca.interfacedef.util.FaultException; +import org.apache.tuscany.sca.interfacedef.util.XMLType; +import org.osoa.sca.ServiceRuntimeException; /** * Default Mediator implementation @@ -45,9 +63,12 @@ import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; * @version $Rev$ $Date$ */ public class MediatorImpl implements Mediator { + private static final String TARGET_OPERATION = "target.operation"; + private static final String SOURCE_OPERATION = "source.operation"; private ExtensionPointRegistry registry; private DataBindingExtensionPoint dataBindings; private TransformerExtensionPoint transformers; + private FaultExceptionMapper faultExceptionMapper; MediatorImpl(DataBindingExtensionPoint dataBindings, TransformerExtensionPoint transformers) { this.dataBindings = dataBindings; @@ -58,13 +79,16 @@ public class MediatorImpl implements Mediator { this.registry = registry; this.dataBindings = registry.getExtensionPoint(DataBindingExtensionPoint.class); this.transformers = registry.getExtensionPoint(TransformerExtensionPoint.class); + this.faultExceptionMapper = + registry.getExtensionPoint(UtilityExtensionPoint.class).getUtility(FaultExceptionMapper.class); + } @SuppressWarnings("unchecked") public Object mediate(Object source, DataType sourceDataType, DataType targetDataType, Map metadata) { if (sourceDataType == null || sourceDataType.getDataBinding() == null) { if (source != null) { - Operation operation = (Operation) metadata.get("source.operation"); + Operation operation = (Operation)metadata.get(SOURCE_OPERATION); sourceDataType = dataBindings.introspectType(source, operation); } } @@ -142,7 +166,7 @@ public class MediatorImpl implements Mediator { return; } if (sourceDataType == null || sourceDataType.getDataBinding() == null) { - Operation operation = (Operation) metadata.get("source.operation"); + Operation operation = (Operation)metadata.get(SOURCE_OPERATION); sourceDataType = dataBindings.introspectType(source, operation); } if (sourceDataType == null) { @@ -193,4 +217,348 @@ public class MediatorImpl implements Mediator { return transformers; } + private DataType getFaultType(DataType exceptionType) { + return exceptionType == null ? null : (DataType)exceptionType.getLogical(); + } + + /** + * @param qn1 + * @param qn2 + */ + private boolean matches(QName qn1, QName qn2) { + if (qn1 == qn2) { + return true; + } + if (qn1 == null || qn2 == null) { + return false; + } + String ns1 = qn1.getNamespaceURI(); + String ns2 = qn2.getNamespaceURI(); + String e1 = qn1.getLocalPart(); + String e2 = qn2.getLocalPart(); + if (e1.equals(e2) && (ns1.equals(ns2) || ns1.equals(ns2 + "/") || ns2.equals(ns1 + "/"))) { + // Tolerating the trailing / which is required by JAX-WS java package --> xml ns mapping + return true; + } + return false; + } + + /** + * @param source The source exception + * @param sourceExType The data type for the source exception + * @param targetExType The data type for the target exception + * @param sourceType The fault type for the source + * @param targetType The fault type for the target + * @return + */ + private Object transformException(Object source, + DataType sourceExType, + DataType targetExType, + DataType sourceType, + DataType targetType, + Map metadata) { + + if (sourceType == targetType || (sourceType != null && sourceType.equals(targetType))) { + return source; + } + + DataType eSourceDataType = + new DataTypeImpl(IDL_FAULT, sourceExType.getPhysical(), sourceType); + DataType eTargetDataType = + new DataTypeImpl(IDL_FAULT, targetExType.getPhysical(), targetType); + + return mediate(source, eSourceDataType, eTargetDataType, metadata); + } + + // + // Assumes we're going from target->source, knowing that we're throwing BACK an exception, rather than the more + // obvious source->target + // + public Object mediateFault(Object result, + Operation sourceOperation, + Operation targetOperation, + Map metadata) { + + // FIXME: How to match fault data to a fault type for the + // operation? + + // If the result is from an InvocationTargetException look at + // the actual cause. + if (result instanceof InvocationTargetException) { + result = ((InvocationTargetException)result).getCause(); + } + DataType targetDataType = null; + for (DataType exType : targetOperation.getFaultTypes()) { + if (((Class)exType.getPhysical()).isInstance(result)) { + if (result instanceof FaultException) { + DataType faultType = (DataType)exType.getLogical(); + if (((FaultException)result).isMatchingType(faultType.getLogical())) { + targetDataType = exType; + break; + } + } else { + targetDataType = exType; + break; + } + } + } + + /* + if (targetDataType == null) { + // Not a business exception + return resultMsg; + } + */ + + DataType targetFaultType = getFaultType(targetDataType); + if (targetFaultType == null) { + // No matching fault type, it's a system exception + Throwable cause = (Throwable)result; + throw new ServiceRuntimeException(cause); + } + + // FIXME: How to match a source fault type to a target fault + // type? + DataType sourceDataType = null; + DataType sourceFaultType = null; + for (DataType exType : sourceOperation.getFaultTypes()) { + DataType faultType = getFaultType(exType); + // Match by the QName (XSD element) of the fault type + if (faultType != null && typesMatch(targetFaultType.getLogical(), faultType.getLogical())) { + sourceDataType = exType; + sourceFaultType = faultType; + break; + } + } + + if (sourceFaultType == null) { + // No matching fault type, it's a system exception + Throwable cause = (Throwable)result; + throw new ServiceRuntimeException(cause); + } + + Object newResult = + transformException(result, targetDataType, sourceDataType, targetFaultType, sourceFaultType, metadata); + + return newResult; + + } + + private boolean typesMatch(Object first, Object second) { + if (first.equals(second)) { + return true; + } + if (first instanceof XMLType && second instanceof XMLType) { + XMLType t1 = (XMLType)first; + XMLType t2 = (XMLType)second; + // TUSCANY-2113, we should compare element names only + return matches(t1.getElementName(), t2.getElementName()); + } + return false; + } + + /** + * @param output + * @param sourceOperation + * @param targetOperation + * @return + */ + public Object mediateOutput(Object output, + Operation sourceOperation, + Operation targetOperation, + Map metadata) { + // Create a data type to represent the ouput produced by the target operation + DataType targetType = + new DataTypeImpl(IDL_OUTPUT, Object.class, targetOperation.getOutputType()); + + // Create a data type to represent the ouput expected by the source operation + DataType sourceType = + new DataTypeImpl(IDL_OUTPUT, Object.class, sourceOperation.getOutputType()); + + if (sourceType == targetType || (sourceType != null && sourceType.equals(targetType))) { + return output; + } + Map context = new HashMap(); + if (metadata != null) { + context.putAll(metadata); + } + if (targetOperation != null) { + context.put(SOURCE_OPERATION, targetOperation); + } + if (sourceOperation != null) { + context.put(TARGET_OPERATION, sourceOperation); + } + return mediate(output, targetType, sourceType, context); + } + + public Object mediateInput(Object input, + Operation sourceOperation, + Operation targetOperation, + Map metadata) { + // Get the data type to represent the input passed in by the source operation + DataType sourceType = sourceOperation.getInputType(); + + // Get the data type to represent the input expected by the target operation + DataType targetType = targetOperation.getInputType(); + + if (sourceType == targetType || (sourceType != null && sourceType.equals(targetType))) { + return input; + } + Map context = new HashMap(); + if (metadata != null) { + context.putAll(metadata); + } + if (sourceOperation != null) { + context.put(SOURCE_OPERATION, sourceOperation); + } + if (targetOperation != null) { + context.put(TARGET_OPERATION, targetOperation); + } + return mediate(input, sourceType, targetType, context); + } + + public TransformationContext createTransformationContext() { + return new TransformationContextImpl(); + } + + public TransformationContext createTransformationContext(DataType sourceDataType, + DataType targetDataType, + Map metadata) { + return new TransformationContextImpl(sourceDataType, targetDataType, metadata); + } + + public Object copy(Object data, DataType dataType) { + return copy(data, dataType, null); + } + + /** + * Copy data using the specified databinding. + * @param data input data + * @param dataType + * @return a copy of the data + */ + private Object copy(Object data, DataType dataType, Operation operation) { + if (data == null) { + return null; + } + Class clazz = data.getClass(); + if (String.class == clazz || clazz.isPrimitive() + || Number.class.isAssignableFrom(clazz) + || Boolean.class.isAssignableFrom(clazz) + || Character.class.isAssignableFrom(clazz) + || Byte.class.isAssignableFrom(clazz) + || URI.class == clazz + || UUID.class == clazz + || QName.class == clazz) { + // Immutable classes + return data; + } + + DataBinding javaBeansDataBinding = dataBindings.getDataBinding(JavaBeansDataBinding.NAME); + // FIXME: The JAXB databinding is hard-coded here + DataBinding jaxbDataBinding = dataBindings.getDataBinding("javax.xml.bind.JAXBElement"); + DataBinding dataBinding = dataBindings.getDataBinding(dataType.getDataBinding()); + // If no databinding was specified, introspect the given arg to + // determine its databinding + if (dataBinding == null) { + dataType = dataBindings.introspectType(data, operation); + if (dataType != null) { + String db = dataType.getDataBinding(); + dataBinding = dataBindings.getDataBinding(db); + if (dataBinding == null && db != null) { + return data; + } + } + if (dataBinding == null) { + + // Default to the JavaBean databinding + dataBinding = dataBindings.getDataBinding(JavaBeansDataBinding.NAME); + } + } + + // Use the JAXB databinding to copy non-Serializable data + if (dataBinding == javaBeansDataBinding) { + + // If the input data is an array containing non Serializable elements + // use JAXB + clazz = data.getClass(); + if (clazz.isArray()) { + if (Array.getLength(data) != 0) { + Object element = Array.get(data, 0); + if (element != null && !(element instanceof Serializable)) { + dataBinding = jaxbDataBinding; + } + } + } else { + + // If the input data is not Serializable use JAXB + if (!((data instanceof Serializable) || (data instanceof Cloneable))) { + dataBinding = jaxbDataBinding; + } + } + } + + if (dataBinding != null) { + return dataBinding.copy(data, dataType, operation); + } else { + return data; + } + } + + /** + * Copy an array of data objects passed to an operation + * @param data array of objects to copy + * @return the copy + */ + public Object copyInput(Object input, Operation operation) { + if (input == null) { + return null; + } + Object[] data = (input instanceof Object[]) ? (Object[])input : new Object[] {input}; + List inputTypes = operation.getInputType().getLogical(); + Object[] copy = new Object[data.length]; + Map map = new IdentityHashMap(); + for (int i = 0, size = inputTypes.size(); i < size; i++) { + Object arg = data[i]; + if (arg == null) { + copy[i] = null; + } else { + Object copiedArg = map.get(arg); + if (copiedArg != null) { + copy[i] = copiedArg; + } else { + copiedArg = copy(arg, inputTypes.get(i)); + map.put(arg, copiedArg); + copy[i] = copiedArg; + } + } + } + return copy; + } + + public Object copyOutput(Object data, Operation operation) { + return copy(data, operation.getOutputType(), operation); + } + + public Object copyFault(Object fault, Operation operation) { + if (faultExceptionMapper == null) { + return fault; + } + for (DataType et : operation.getFaultTypes()) { + if (et.getPhysical().isInstance(fault)) { + Throwable ex = (Throwable)fault; + DataType exType = + new DataTypeImpl(ex.getClass(), new DataTypeImpl(ex.getClass(), XMLType.UNKNOWN)); + faultExceptionMapper.introspectFaultDataType(exType, operation, false); + DataType faultType = exType.getLogical(); + Object faultInfo = faultExceptionMapper.getFaultInfo(ex, faultType.getPhysical(), operation); + faultInfo = copy(faultInfo, faultType); + fault = + faultExceptionMapper.wrapFaultInfo(exType, ex.getMessage(), faultInfo, ex.getCause(), operation); + return fault; + } + } + return fault; + } + } diff --git a/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/impl/TransformationContextImpl.java b/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/impl/TransformationContextImpl.java index a6c0639411..afc6417284 100644 --- a/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/impl/TransformationContextImpl.java +++ b/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/impl/TransformationContextImpl.java @@ -6,15 +6,15 @@ * 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. + * under the License. */ package org.apache.tuscany.sca.databinding.impl; @@ -54,6 +54,18 @@ public class TransformationContextImpl implements TransformationContext { } } + public TransformationContextImpl(DataType sourceDataType, + DataType targetDataType, + Map metadata) { + super(); + this.sourceDataType = sourceDataType; + this.targetDataType = targetDataType; + setClassLoader(Thread.currentThread().getContextClassLoader()); + if (metadata != null) { + this.metadata.putAll(metadata); + } + } + public DataType getSourceDataType() { return sourceDataType; } diff --git a/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/javabeans/JavaBeansDataBinding.java b/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/javabeans/JavaBeansDataBinding.java index fb04014051..9ee3c1f7d5 100644 --- a/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/javabeans/JavaBeansDataBinding.java +++ b/branches/sca-java-1.x/modules/databinding/src/main/java/org/apache/tuscany/sca/databinding/javabeans/JavaBeansDataBinding.java @@ -6,15 +6,15 @@ * 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. + * under the License. */ package org.apache.tuscany.sca.databinding.javabeans; @@ -28,10 +28,14 @@ import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.io.OutputStream; import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.logging.Logger; import org.apache.tuscany.sca.databinding.impl.BaseDataBinding; import org.apache.tuscany.sca.interfacedef.DataType; import org.apache.tuscany.sca.interfacedef.Operation; +import org.osoa.sca.ServiceRuntimeException; /** * DataBinding for JavaBeans @@ -39,6 +43,7 @@ import org.apache.tuscany.sca.interfacedef.Operation; * @version $Rev$ $Date$ */ public class JavaBeansDataBinding extends BaseDataBinding { + private final static Logger logger = Logger.getLogger(JavaBeansDataBinding.class.getName()); /** * Defining a weight to a very high number so that the transformer won't be picked * up by other paths unless it's the only available path @@ -53,13 +58,13 @@ public class JavaBeansDataBinding extends BaseDataBinding { protected JavaBeansDataBinding(String name, Class baseType) { super(name, baseType); } - + @Override public Object copy(Object arg, DataType dataType, Operation operation) { if (arg == null) { return null; } - final Class clazz = arg.getClass(); + final Class clazz = arg.getClass(); if (String.class == clazz || clazz.isPrimitive() || Number.class.isAssignableFrom(clazz) || Boolean.class.isAssignableFrom(clazz) @@ -82,7 +87,7 @@ public class JavaBeansDataBinding extends BaseDataBinding { // * The ThreadContext ClassLoader if the ClassLoader of arg is the System ClassLoader // because Collection classes are loaded by the System ClassLoader but their contents // may be loaded from another ClassLoader - // + // ClassLoader classLoaderToUse = clazz.getClassLoader(); if (classLoaderToUse == null) { @@ -90,20 +95,39 @@ public class JavaBeansDataBinding extends BaseDataBinding { // instead classLoaderToUse = Thread.currentThread().getContextClassLoader(); } - + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = getObjectInputStream(bis, classLoaderToUse); Object objectCopy = ois.readObject(); ois.close(); bis.close(); return objectCopy; - } else { - // return arg; - throw new IllegalArgumentException("Argument type '" + arg.getClass().getCanonicalName() + "' is not Serializable. " + - " Pass-by-value cannot be performed on this argument"); + } else if (arg instanceof Cloneable) { + Method clone; + try { + clone = arg.getClass().getMethod("clone"); + try { + return clone.invoke(arg, (Object[])null); + } catch (InvocationTargetException e) { + if (e.getTargetException() instanceof CloneNotSupportedException) { + // Ignore + } else { + throw new ServiceRuntimeException(e); + } + } catch (Exception e) { + throw new ServiceRuntimeException(e); + } + } catch (NoSuchMethodException e) { + // Ignore it + } } - } catch (Exception e) { - throw new IllegalArgumentException("Pass-by-value is not supported for the given object", e); + // return arg; + logger.warning("Argument type '" + arg.getClass().getName() + + "' is not Serializable or Cloneable. Pass-by-value is skipped."); + return arg; + } catch (Exception e) { + throw new IllegalArgumentException("Pass-by-value is not supported for the given object: " + arg.getClass() + .getName(), e); } } @@ -135,5 +159,5 @@ public class JavaBeansDataBinding extends BaseDataBinding { }; return ois; } - + } -- cgit v1.2.3