/* * 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.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.contribution.Contribution; 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 static org.apache.tuscany.sca.interfacedef.Operation.IDL_INPUT; import static org.apache.tuscany.sca.interfacedef.Operation.IDL_OUTPUT; import org.oasisopen.sca.ResponseDispatch; /** * Represents a Java interface. * * @version $Rev$ $Date$ */ public class JavaInterfaceImpl extends InterfaceImpl implements JavaInterface { private String className; private WeakReference> javaClass; private Class callbackClass; private QName qname; private String jaxwsWSDLLocation; private String jaxwsJavaInterfaceName; private Contribution contributionContainingClass; 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() { if (isUnresolved()) { return className; } else if (javaClass != null) { return javaClass.get().getName(); } else { return null; } } public void setName(String className) { if (!isUnresolved()) { throw new IllegalStateException(); } this.className = className; } public QName getQName() { return qname; } public void setQName(QName interfacename) { qname = interfacename; } public Class getJavaClass() { if (javaClass != null){ return javaClass.get(); } else { return null; } } public void setJavaClass(Class javaClass) { this.javaClass = new WeakReference>(javaClass); if (javaClass != null) { this.className = javaClass.getName(); } } public Class getCallbackClass() { return callbackClass; } public void setCallbackClass(Class callbackClass) { this.callbackClass = callbackClass; } @Override public String toString() { return getName(); } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((className == null) ? 0 : className.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; JavaInterfaceImpl other = (JavaInterfaceImpl)obj; if (isUnresolved() || other.isUnresolved()) { if (className == null) { if (other.className != null) return false; } else if (!className.equals(other.className)) return false; } else { if (javaClass == null) { if (other.javaClass != null) return false; } else if (!javaClass.get().equals(other.javaClass.get())) return false; if (callbackClass == null) { if (other.callbackClass != null) return false; } else if (!callbackClass.equals(other.callbackClass)) return false; } 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 ); String opName = operation.getName(); // 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.setInputWrapper(operation.getInputWrapper()); syncOperation.setOutputWrapper(operation.getOutputWrapper()); syncOperation.setInputWrapperStyle(operation.isInputWrapperStyle()); syncOperation.setOutputWrapperStyle(operation.isOutputWrapperStyle()); syncOperation.setHasArrayWrappedOutput(operation.hasArrayWrappedOutput()); syncOperation.setNotSubjectToWrapping(operation.isNotSubjectToWrapping()); // syncOperation.setInputType(inputType); // syncOperation.setOutputType(returnDataType); syncOperation.setInputType(operation.getInputType()); syncOperation.setOutputType(operation.getOutputType()); 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 */ @SuppressWarnings("rawtypes") 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); ArrayList returnTypes = new ArrayList(); returnTypes.add(returnDataType); DataType> outputType = new DataTypeImpl>(IDL_OUTPUT, requestParams.getPhysical(), returnTypes); return outputType; } // 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 ) { if (operation.isAsyncServer()) { return true; } // Async form operations have: // 1) void return type (equivalent to an output logical List of size '0') // 2) name ending in "Async" // 3) final parameter which is of ResponseDispatch type int size = operation.getOutputType().getLogical().size(); if (size != 0) { return false; } 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 public String getJAXWSWSDLLocation() { return jaxwsWSDLLocation; } public void setJAXWSWSDLLocation(String wsdlLocation) { this.jaxwsWSDLLocation = wsdlLocation; } public String getJAXWSJavaInterfaceName() { return jaxwsJavaInterfaceName; } public void setJAXWSJavaInterfaceName(String javaInterfaceName) { this.jaxwsJavaInterfaceName = javaInterfaceName; } /** * A Java class may reference a WSDL file via a JAXWS annotation. We need to resolve * the WSDL file location in the context of the same contribution that holds the * Java file. In order to do this we need to remember the actual contribution that * was used to resolve a Java class. * * @return */ public Contribution getContributionContainingClass() { return contributionContainingClass; } public void setContributionContainingClass(Contribution contributionContainingClass) { this.contributionContainingClass = contributionContainingClass; } }