From e5b7380c874745c989d1816b8f552504f038e1bc Mon Sep 17 00:00:00 2001 From: lresende Date: Thu, 26 Sep 2013 20:33:20 +0000 Subject: 2.0 branch for possible maintenance release git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1526672 13f79535-47bb-0310-9956-ffa450edef68 --- .../java/jaxws/JAXWSAsyncInterfaceProcessor.java | 293 +++++++++++++++++++++ 1 file changed, 293 insertions(+) create mode 100644 sca-java-2.x/branches/2.0/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSAsyncInterfaceProcessor.java (limited to 'sca-java-2.x/branches/2.0/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSAsyncInterfaceProcessor.java') diff --git a/sca-java-2.x/branches/2.0/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSAsyncInterfaceProcessor.java b/sca-java-2.x/branches/2.0/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSAsyncInterfaceProcessor.java new file mode 100644 index 0000000000..672c01a0b9 --- /dev/null +++ b/sca-java-2.x/branches/2.0/modules/interface-java-jaxws/src/main/java/org/apache/tuscany/sca/interfacedef/java/jaxws/JAXWSAsyncInterfaceProcessor.java @@ -0,0 +1,293 @@ +/* + * 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.java.jaxws; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Future; + +import javax.xml.ws.AsyncHandler; +import javax.xml.ws.Response; + +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.InvalidInterfaceException; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; +import org.apache.tuscany.sca.interfacedef.java.JavaOperation; +import org.apache.tuscany.sca.interfacedef.java.introspect.JavaInterfaceVisitor; + +public class JAXWSAsyncInterfaceProcessor implements JavaInterfaceVisitor { + private static String ASYNC = "Async"; + + public JAXWSAsyncInterfaceProcessor(ExtensionPointRegistry registry) { + } + + public void visitInterface(JavaInterface javaInterface) throws InvalidInterfaceException { + List validOperations = new ArrayList(); + List asyncOperations = new ArrayList(); + + validOperations.addAll(javaInterface.getOperations()); + for (Operation o : javaInterface.getOperations()) { + if (!o.getName().endsWith(ASYNC)) { + JavaOperation op = (JavaOperation)o; + if (op.getJavaMethod().getName().endsWith(ASYNC)) { + continue; + } + for (Operation asyncOp : getAsyncOperations(javaInterface.getOperations(), op)) { + if (isJAXWSAsyncPoolingOperation(op, asyncOp) || isJAXWSAsyncCallbackOperation(op, asyncOp)) { + validOperations.remove(asyncOp); + asyncOperations.add(asyncOp); + } + } + } + } + + javaInterface.getOperations().clear(); + javaInterface.getOperations().addAll(validOperations); + + javaInterface.getAttributes().put("JAXWS-ASYNC-OPERATIONS", asyncOperations); + } + + /** + * The additional client-side asynchronous polling and callback methods defined by JAX-WS are recognized in a Java interface as follows: + * For each method M in the interface, if another method P in the interface has + * + * a) a method name that is M's method name with the characters "Async" appended, and + * b) the same parameter signature as M, and + * c)a return type of Response where R is the return type of M + * + * @param operation + * @param asyncOperation + * @return + */ + private static boolean isJAXWSAsyncPoolingOperation(Operation operation, Operation asyncOperation) { + + if (asyncOperation.getOutputType().getLogical().size() == 0 || Response.class != asyncOperation.getOutputType().getLogical().get(0).getPhysical()) { + // The return type is not Response + return false; + } + + //the same parameter signature as M + List operationInputType = operation.getInputType().getLogical(); + List asyncOperationInputType = asyncOperation.getInputType().getLogical(); + int size = operationInputType.size(); + if (asyncOperationInputType.size() != size) { + return false; + } + for (int i = 0; i < size; i++) { + if (!isCompatible(operationInputType.get(i), asyncOperationInputType.get(i))) { + return false; + } + } + + //a return type of Response where R is the return type of M + DataType operationOutputType = null; + if (operation.getOutputType()!= null && operation.getOutputType().getLogical() != null && operation.getOutputType().getLogical().size() > 0) { + operationOutputType = operation.getOutputType().getLogical().get(0); + } + DataType asyncOperationOutputType = asyncOperation.getOutputType().getLogical().get(0); + + if (operationOutputType != null && asyncOperationOutputType != null) { + Class asyncReturnTypeClass = (Class)asyncOperationOutputType.getPhysical(); + if (asyncReturnTypeClass == Response.class) { + //now check the actual type of the Response with R + Class returnType = operationOutputType.getPhysical(); + Class asyncActualReturnTypeClass = Object.class; + if (asyncOperationOutputType.getGenericType() instanceof ParameterizedType) { + ParameterizedType asyncReturnType = (ParameterizedType)asyncOperationOutputType.getGenericType(); + asyncActualReturnTypeClass = (Class)asyncReturnType.getActualTypeArguments()[0]; + } + + if (operation.getOutputWrapper() != null) { + // The return type could be the wrapper type per JAX-WS spec + Class wrapperClass = operation.getOutputWrapper().getWrapperClass(); + if (wrapperClass == asyncActualReturnTypeClass) { + return true; + } + } + if (returnType == asyncActualReturnTypeClass || returnType.isPrimitive() + && primitiveAssignable(returnType, asyncActualReturnTypeClass)) { + return true; + } else { + return false; + } + } + } + + return true; + } + + /** + * For each method M in the interface, if another method C in the interface has + * a) a method name that is M's method name with the characters "Async" appended, and + * b) a parameter signature that is M's parameter signature with an additional + * final parameter of type AsyncHandler where R is the return type of M, and + * c) a return type of Future + * + * then C is a JAX-WS callback method that isn't part of the SCA interface contract. + * + * @param operation + * @param asyncOperation + * @return + */ + private static boolean isJAXWSAsyncCallbackOperation(Operation operation, Operation asyncOperation) { + + if (asyncOperation.getOutputType().getLogical().size() == 0 || Future.class != asyncOperation.getOutputType().getLogical().get(0).getPhysical()) { + // The return type is not Future + return false; + } + + //a parameter signature that is M's parameter signature + //with an additional final parameter of type AsyncHandler where R is the return type of M, and + List operationInputType = operation.getInputType().getLogical(); + List asyncOperationInputType = asyncOperation.getInputType().getLogical(); + int size = operationInputType.size(); + if (asyncOperationInputType.size() != size + 1) { + return false; + } + for (int i = 0; i < size; i++) { + if (!isCompatible(operationInputType.get(i), asyncOperationInputType.get(i))) { + return false; + } + } + + Type genericParamType = asyncOperationInputType.get(size).getGenericType(); + + Class asyncLastParameterTypeClass = asyncOperationInputType.get(size).getPhysical(); + if (asyncLastParameterTypeClass == AsyncHandler.class) { + //now check the actual type of the AsyncHandler with R + Class asyncActualLastParameterTypeClass = Object.class; + if (genericParamType instanceof ParameterizedType) { + ParameterizedType asyncLastParameterType = (ParameterizedType)genericParamType; + asyncActualLastParameterTypeClass = (Class)asyncLastParameterType.getActualTypeArguments()[0]; + } + + if (operation.getOutputWrapper() != null) { + // The return type could be the wrapper type per JAX-WS spec + Class wrapperClass = operation.getOutputWrapper().getWrapperClass(); + if (wrapperClass == asyncActualLastParameterTypeClass) { + return true; + } + } + + Class returnType = null; + if (operation.getOutputType() != null && operation.getOutputType().getLogical() != null && operation.getOutputType().getLogical().size() > 0) { + returnType = operation.getOutputType().getLogical().get(0).getPhysical(); + } + if (returnType != null) { + if (returnType == asyncActualLastParameterTypeClass || returnType.isPrimitive() + && primitiveAssignable(returnType, asyncActualLastParameterTypeClass)) { + return true; + } + } + return false; + } + + return true; + } + + /** + * Get operation by name + * + * @param operations + * @param operationName + * @return + */ + private static List getAsyncOperations(List operations, JavaOperation op) { + List returnOperations = new ArrayList(); + + for (Operation o : operations) { + if (o == op) { + continue; + } + String operationName = op.getName(); + if (o.getName().equals(operationName)) { + // Async operations have the same name when @WebMethod is present + /* + JavaOperation jop = (JavaOperation)o; + if (op.getJavaMethod().getName().equals(jop.getJavaMethod().getName() + ASYNC)) { + returnOperations.add(o); + } + */ + returnOperations.add(o); + } else if (o.getName().equals(operationName + ASYNC)) { + returnOperations.add(o); + } + } + + return returnOperations; + } + + /** + * Check if two operation parameters are compatible + * + * @param source + * @param target + * @return + */ + private static boolean isCompatible(DataType source, DataType target) { + if (source == target) { + return true; + } + + return target.getPhysical().isAssignableFrom(source.getPhysical()); + } + + /** + * Compares a two types, assuming one is a primitive, to determine if the + * other is its object counterpart + */ + private static boolean primitiveAssignable(Class memberType, Class param) { + if (memberType == Integer.class) { + return param == Integer.TYPE; + } else if (memberType == Double.class) { + return param == Double.TYPE; + } else if (memberType == Float.class) { + return param == Float.TYPE; + } else if (memberType == Short.class) { + return param == Short.TYPE; + } else if (memberType == Character.class) { + return param == Character.TYPE; + } else if (memberType == Boolean.class) { + return param == Boolean.TYPE; + } else if (memberType == Byte.class) { + return param == Byte.TYPE; + } else if (param == Integer.class) { + return memberType == Integer.TYPE; + } else if (param == Double.class) { + return memberType == Double.TYPE; + } else if (param == Float.class) { + return memberType == Float.TYPE; + } else if (param == Short.class) { + return memberType == Short.TYPE; + } else if (param == Character.class) { + return memberType == Character.TYPE; + } else if (param == Boolean.class) { + return memberType == Boolean.TYPE; + } else if (param == Byte.class) { + return memberType == Byte.TYPE; + } else { + return false; + } + } +} -- cgit v1.2.3