From 7d7d7269a21d2e857f7e69ae82b5604af503f3e9 Mon Sep 17 00:00:00 2001 From: edwardsmj Date: Fri, 25 Jun 2010 10:21:28 +0000 Subject: Introspection of SCA Async Server form on Java interface and Equivalence mapping of Async Server interface to Synchronous Java interface, as described in TUSCANY-3609 and TUSCANY-3610 git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@957877 13f79535-47bb-0310-9956-ffa450edef68 --- .../sca/interfacedef/java/JavaOperation.java | 12 ++ .../java/impl/JavaInterfaceFactoryImpl.java | 2 + .../interfacedef/java/impl/JavaInterfaceImpl.java | 186 +++++++++++++++++++++ .../java/impl/JavaInterfaceIntrospectorImpl.java | 17 ++ .../interfacedef/java/impl/JavaInterfaceUtil.java | 51 +++++- .../interfacedef/java/impl/JavaOperationImpl.java | 17 ++ 6 files changed, 284 insertions(+), 1 deletion(-) (limited to 'sca-java-2.x/trunk/modules/interface-java/src') diff --git a/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/JavaOperation.java b/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/JavaOperation.java index c44650de9f..e894ac279b 100644 --- a/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/JavaOperation.java +++ b/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/JavaOperation.java @@ -54,5 +54,17 @@ public interface JavaOperation extends Operation { * @param action the action value */ void setAction(String action); + + /** + * Sets whether this operation has async server style + * @param isAsync - "true" marks this operation as async server style + */ + public void setAsyncServer( boolean isAsync ); + + /** + * Indicates whether this operation is async server style + * @return - true if the operation is async server style + */ + public boolean isAsyncServer(); } diff --git a/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceFactoryImpl.java b/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceFactoryImpl.java index b443cb988e..dbedf1d55a 100644 --- a/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceFactoryImpl.java +++ b/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceFactoryImpl.java @@ -58,6 +58,8 @@ public abstract class JavaInterfaceFactoryImpl implements JavaInterfaceFactory { if (javaInterface == null) { javaInterface = createJavaInterface(); introspector.introspectInterface(javaInterface, interfaceClass); + // Now that all introspection is complete we can mark the interface resolved + javaInterface.setUnresolved(false); cache.put(interfaceClass, javaInterface); } return javaInterface; diff --git a/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceImpl.java b/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceImpl.java index 0b418054c5..54946a9413 100644 --- a/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceImpl.java +++ b/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceImpl.java @@ -19,10 +19,25 @@ package org.apache.tuscany.sca.interfacedef.java.impl; import java.lang.ref.WeakReference; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; +import java.util.List; +import java.lang.reflect.ParameterizedType; import javax.xml.namespace.QName; + +import org.apache.tuscany.sca.assembly.xml.Constants; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; import org.apache.tuscany.sca.interfacedef.impl.InterfaceImpl; import org.apache.tuscany.sca.interfacedef.java.JavaInterface; +import org.apache.tuscany.sca.interfacedef.java.JavaOperation; +import org.apache.tuscany.sca.interfacedef.util.XMLType; +import org.apache.tuscany.sca.policy.Intent; + +import org.oasisopen.sca.ResponseDispatch; /** * Represents a Java interface. @@ -37,6 +52,10 @@ public class JavaInterfaceImpl extends InterfaceImpl implements JavaInterface { private QName qname; protected JavaInterfaceImpl() { + super(); + // Mark the interface as unresolved until all the basic processing is complete + // including Intent & Policy introspection + this.setUnresolved(true); } public String getName() { @@ -135,5 +154,172 @@ public class JavaInterfaceImpl extends InterfaceImpl implements JavaInterface { return true; } + + public List getOperations() { + if( !isUnresolved() && isAsyncServer() ) { + return equivalentSyncOperations(); + } else { + return super.getOperations(); + } + } // end method getOperations + + + private List syncOperations = null; + private List equivalentSyncOperations() { + if( syncOperations != null ) return syncOperations; + List allOperations = super.getOperations(); + syncOperations = new ArrayList(); + for( Operation operation: allOperations) { + syncOperations.add( getSyncFormOfOperation( (JavaOperation) operation ) ); + // Store the actual async operations under the attribute "ASYNC-SERVER-OPERATIONS" + this.getAttributes().put("ASYNC-SERVER-OPERATIONS", allOperations); + } // end for + + return syncOperations; + } // end method equivalentSyncOperations + + private static final String UNKNOWN_DATABINDING = null; + /** + * Prepares the synchronous form of an asynchronous operation + * - async form: void someOperationAsync( FooType inputParam, DispatchResponse ) + * - sync form: BarType someOperation( FooType inputParam ) + * @param operation - the operation to convert + * @return - the synchronous form of the operation - for an input operation that is not async server in form, this + * method simply returns the original operation unchanged + */ + private Operation getSyncFormOfOperation( JavaOperation operation ) { + if( isAsyncServerOperation( operation ) ) { + JavaOperation syncOperation = new JavaOperationImpl(); + String opName = operation.getName().substring(0, operation.getName().length() - 5 ); + + // Prepare the list of equivalent input parameters, which simply excludes the (final) DispatchResponse object + // and the equivalent return parameter, which is the (generic) type from the DispatchResponse object + DataType> requestParams = operation.getInputType(); + + DataType> inputType = prepareSyncInputParams( requestParams ); + DataType returnDataType = prepareSyncReturnParam( requestParams ); + List faultDataTypes = prepareSyncFaults( operation ); + + syncOperation.setName(opName); + syncOperation.setAsyncServer(true); + syncOperation.setInputType(inputType); + syncOperation.setOutputType(returnDataType); + syncOperation.setFaultTypes(faultDataTypes); + syncOperation.setNonBlocking(operation.isNonBlocking()); + syncOperation.setJavaMethod(operation.getJavaMethod()); + syncOperation.setInterface(this); + return syncOperation; + } else { + // If it's not Async form, then it's a synchronous operation + return operation; + } // end if + } // end getSyncFormOfOperation + + /** + * Produce the equivalent sync method input parameters from the input parameters of the async method + * @param requestParams - async method input parameters + * @return - the equivalent sync method input parameters + */ + private DataType> prepareSyncInputParams( DataType> requestParams ) { + List requestLogical = requestParams.getLogical(); + int paramCount = requestLogical.size(); + + // Copy the list of async parameters, removing the final DispatchResponse + List asyncParams = new ArrayList( paramCount - 1); + for( int i = 0 ; i < (paramCount - 1) ; i++ ) { + asyncParams.add( requestLogical.get(i) ); + } // end for + + DataType> inputType = + new DataTypeImpl>(requestParams.getDataBinding(), + requestParams.getPhysical(), asyncParams); + return inputType; + } // end method prepareSyncInputParams + + /** + * Prepare the return data type of the equivalent sync operation, based on the parameterization of the ResponseDispatch object + * of the async operation - the return data type is the Generic type of the final DispatchResponse + * @param requestParams - - async method input parameters + * @return - the sync method return parameter + */ + private DataType prepareSyncReturnParam( DataType> requestParams ) { + List requestLogical = requestParams.getLogical(); + int paramCount = requestLogical.size(); + + DataType finalParam = requestLogical.get( paramCount - 1 ); + ParameterizedType t = (ParameterizedType)finalParam.getGenericType(); + XMLType returnXMLType = (XMLType)finalParam.getLogical(); + + String namespace = null; + if( returnXMLType.isElement() ) { + namespace = returnXMLType.getElementName().getNamespaceURI(); + } else { + namespace = returnXMLType.getTypeName().getNamespaceURI(); + } + + Type[] typeArgs = t.getActualTypeArguments(); + if( typeArgs.length != 1 ) throw new IllegalArgumentException( "ResponseDispatch parameter is not parameterized correctly"); + + Class returnType = (Class)typeArgs[0]; + + // Set outputType to null for void + XMLType xmlReturnType = new XMLType(new QName(namespace, "return"), null); + DataType returnDataType = + returnType == void.class ? null : new DataTypeImpl(UNKNOWN_DATABINDING, returnType, xmlReturnType); + + return returnDataType; + } // end method prepareSyncReturnParam + + /** + * Prepare the set of equivalent sync faults for a given async operation + * @return - the list of faults + */ + private List prepareSyncFaults( JavaOperation operation ) { + //TODO - deal with Faults - for now just copy through whatever is associated with the async operation + return operation.getFaultTypes(); + } + + /** + * Determines if an interface operation has the form of an async server operation + * - async form: void someOperationAsync( FooType inputParam, ...., DispatchResponse ) + * @param operation - the operation to examine + * @return - true if the operation has the form of an async operation, false otherwise + */ + private boolean isAsyncServerOperation( Operation operation ) { + // Async form operations have: + // 1) void return type + // 2) name ending in "Async" + // 3) final parameter which is of ResponseDispatch type + DataType response = operation.getOutputType(); + if( response != null ) { + if ( response.getPhysical() != void.class ) return false; + } // end if + + if ( !operation.getName().endsWith("Async") ) return false; + + DataType> requestParams = operation.getInputType(); + int paramCount = requestParams.getLogical().size(); + if( paramCount < 1 ) return false; + DataType finalParam = requestParams.getLogical().get( paramCount - 1 ); + if ( finalParam.getPhysical() != ResponseDispatch.class ) return false; + + return true; + } // end method isAsyncServerOperation + + static QName ASYNC_INVOCATION = new QName(Constants.SCA11_NS, "asyncInvocation"); + /** + * Indicates if this interface is an Async Server interface + * @return true if the interface is Async Server, false otherwise + */ + private boolean isAsyncServer() { + + List intents = getRequiredIntents(); + for( Intent intent: intents ) { + if ( intent.getName().equals(ASYNC_INVOCATION) ) { + return true; + } + } // end for + return false; + } // end method isAsyncServer } diff --git a/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceIntrospectorImpl.java b/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceIntrospectorImpl.java index d05dc7eba8..0f5962c964 100644 --- a/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceIntrospectorImpl.java +++ b/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceIntrospectorImpl.java @@ -51,6 +51,7 @@ import org.apache.tuscany.sca.interfacedef.java.JavaOperation; import org.apache.tuscany.sca.interfacedef.java.introspect.JavaInterfaceVisitor; import org.apache.tuscany.sca.interfacedef.util.JavaXMLMapper; import org.apache.tuscany.sca.interfacedef.util.XMLType; +import org.oasisopen.sca.annotation.AsyncFault; import org.oasisopen.sca.annotation.OneWay; import org.oasisopen.sca.annotation.Remotable; @@ -199,6 +200,11 @@ public class JavaInterfaceIntrospectorImpl { getActualTypes(method.getGenericParameterTypes(), method.getParameterTypes(), typeBindings); Class[] faultTypes = getActualTypes(method.getGenericExceptionTypes(), method.getExceptionTypes(), typeBindings); + + // For async server interfaces, faults are described using the @AsyncFaults annotation + if( method.isAnnotationPresent(AsyncFault.class) ) { + faultTypes = readAsyncFaultTypes( method ); + } // end if boolean nonBlocking = method.isAnnotationPresent(OneWay.class); if (nonBlocking) { @@ -256,6 +262,17 @@ public class JavaInterfaceIntrospectorImpl { } return operations; } + + /** + * Reads the fault types declared in an @AsyncFault annotation on an async server method + * @param method - the Method + * @return - an array of fault/exception classes + */ + private Class[] readAsyncFaultTypes( Method method ) { + AsyncFault theFaults = method.getAnnotation(AsyncFault.class); + if ( theFaults == null ) return null; + return theFaults.value(); + } // end method readAsyncFaultTypes private boolean jaxwsAsyncMethod(Method method) { if (method.getName().endsWith("Async")) { diff --git a/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceUtil.java b/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceUtil.java index 61d38dac76..352cffeef2 100644 --- a/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceUtil.java +++ b/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaInterfaceUtil.java @@ -27,6 +27,7 @@ import java.util.List; import org.apache.tuscany.sca.interfacedef.DataType; import org.apache.tuscany.sca.interfacedef.Interface; import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.java.JavaInterface; import org.apache.tuscany.sca.interfacedef.java.JavaOperation; /** @@ -54,7 +55,12 @@ public final class JavaInterfaceUtil { public static Method findMethod(Class implClass, Operation operation) throws NoSuchMethodException { String name = operation.getName(); if (operation instanceof JavaOperation) { - name = ((JavaOperation)operation).getJavaMethod().getName(); + if( ((JavaOperation)operation).isAsyncServer() ) { + // In this case, the operation is a mapped async server style method and needs special handling + return findAsyncServerMethod( implClass, (JavaOperation)operation ); + } else { + name = ((JavaOperation)operation).getJavaMethod().getName(); + } // end if } Interface interface1 = operation.getInterface(); int numParams = operation.getInputType().getLogical().size(); @@ -85,6 +91,49 @@ public final class JavaInterfaceUtil { Class[] paramTypes = getPhysicalTypes(operation); return implClass.getMethod(name, paramTypes); } + + /** + * Return the method on the implementation class that matches the async server version of the operation. + * + * @param implClass the implementation class or interface + * @param operation the operation to match - this is the sync equivalent of an async server operation + * @return the method described by the operation + * @throws NoSuchMethodException if no such method exists + */ + public static Method findAsyncServerMethod(Class implClass, JavaOperation operation) throws NoSuchMethodException { + String name = operation.getJavaMethod().getName(); + List actualOps = (List) operation.getInterface().getAttributes().get("ASYNC-SERVER-OPERATIONS"); + Operation matchingOp = null; + for( Operation op: actualOps ) { + if( op.getName().equals(name) ) { + matchingOp = op; + break; + } + } // end for + if( matchingOp == null ) throw new NoSuchMethodException("No matching async method for operation " + operation.getName()); + + int numParams = matchingOp.getInputType().getLogical().size(); + + List matchingMethods = new ArrayList(); + for (Method m : implClass.getMethods()) { + if (m.getName().equals(name) && m.getParameterTypes().length == (numParams) ) { + matchingMethods.add(m); + } + } + + if (matchingMethods.size() == 1) { + return matchingMethods.get(0); + } + if (matchingMethods.size() > 1) { + Class[] paramTypes = getPhysicalTypes(matchingOp); + return implClass.getMethod(name, paramTypes); + } + + // No matching method found + throw new NoSuchMethodException("No matching method for operation " + operation.getName() + + " is found on " + implClass); + + } // end method findAsyncServerMethod /** * @Deprecated diff --git a/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaOperationImpl.java b/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaOperationImpl.java index 4384213bf5..7169cc164d 100644 --- a/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaOperationImpl.java +++ b/sca-java-2.x/trunk/modules/interface-java/src/main/java/org/apache/tuscany/sca/interfacedef/java/impl/JavaOperationImpl.java @@ -33,6 +33,7 @@ public class JavaOperationImpl extends OperationImpl implements JavaOperation { private Method method; private String action; + private boolean isAsyncServer = false; public Method getJavaMethod() { return method; @@ -74,6 +75,22 @@ public class JavaOperationImpl extends OperationImpl implements JavaOperation { return false; return true; } + + /** + * Sets whether this operation has async server style + * @param isAsync - "true" marks this operation as async server style + */ + public void setAsyncServer( boolean isAsync ) { + isAsyncServer = isAsync; + } + + /** + * Indicates whether this operation is async server style + * @return - true if the operation is async server style + */ + public boolean isAsyncServer() { + return isAsyncServer; + } @Override public String toString() { -- cgit v1.2.3