/*
* 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.binding.corba.provider.reference;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.tuscany.sca.binding.corba.provider.exceptions.CorbaException;
import org.apache.tuscany.sca.binding.corba.provider.exceptions.RequestConfigurationException;
import org.apache.tuscany.sca.binding.corba.provider.types.TypeTree;
import org.apache.tuscany.sca.binding.corba.provider.types.TypeTreeCreator;
import org.apache.tuscany.sca.binding.corba.provider.types.util.TypeHelpersProxy;
import org.apache.tuscany.sca.binding.corba.provider.types.util.Utils;
import org.apache.tuscany.sca.binding.corba.provider.util.MethodFinder;
import org.omg.CORBA.BAD_OPERATION;
import org.omg.CORBA.BAD_PARAM;
import org.omg.CORBA.Object;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.portable.ApplicationException;
import org.omg.CORBA.portable.InputStream;
import org.omg.CORBA.portable.ObjectImpl;
import org.omg.CORBA.portable.OutputStream;
/**
* @version $Rev$ $Date$ Represents single CORBA request
*/
public class DynaCorbaRequest {
private TypeTree returnTree;
private Map<String, TypeTree> exceptions = new HashMap<String, TypeTree>();
private InputStream inputStream;
private ObjectImpl remoteObject;
private String operation;
private List<java.lang.Object> arguments = new ArrayList<java.lang.Object>();
private List<TypeTree> argumentsTypes = new ArrayList<TypeTree>();
private Class<?> referenceClass;
private Map<Method, String> operationsMap;
/**
* Creates request.
*
* @param ObjectremoteObject remote object reference
* @param operation operation to invoke
* @param scaBindingRules apply SCA default binding mapping rules
*/
public DynaCorbaRequest(Object remoteObject, String operation) {
this.remoteObject = (ObjectImpl)remoteObject;
this.operation = operation;
}
/**
* Sets class which will be backed by this reference request
* @param referenceClass
*/
public void setReferenceClass(Class<?> referenceClass) {
this.referenceClass = referenceClass;
}
/**
* Sets method to operation names mapping
* @param operationsMap
*/
public void setOperationsMap(Map<Method, String> operationsMap) {
this.operationsMap = operationsMap;
}
/**
* Adds operation argument - stores arguments and caches its TypeTree. Annotations will be set to null by default.
*
* @param argument
*/
public void addArgument(java.lang.Object argument) throws RequestConfigurationException {
addArgument(argument, null);
}
/**
* Adds operation argument - stores arguments and caches its TypeTree
*
* @param argument
*/
public void addArgument(java.lang.Object argument, Annotation[] notes) throws RequestConfigurationException {
TypeTree tree = TypeTreeCreator.createTypeTree(argument.getClass(), notes);
argumentsTypes.add(tree);
arguments.add(argument);
}
/**
* Passing stored arguments to CORBA communication output stream
*
* @param outputStream
* @throws RequestConfigurationException
*/
private void passArguments(OutputStream outputStream) throws RequestConfigurationException {
for (int i = 0; i < arguments.size(); i++) {
TypeTree tree = argumentsTypes.get(i);
TypeHelpersProxy.write(tree.getRootNode(), outputStream, arguments.get(i));
}
}
/**
* Sets return type for operation. Annotations will be set to null by default.
*
* @param forClass
*/
public void setOutputType(Class<?> forClass) throws RequestConfigurationException {
setOutputType(forClass, null);
}
/**
* Sets return type for operation
*
* @param forClass
*/
public void setOutputType(Class<?> forClass, Annotation[] notes) throws RequestConfigurationException {
returnTree = TypeTreeCreator.createTypeTree(forClass, notes);
}
/**
* Configures possible exceptions
*
* @param forClass
*/
public void addExceptionType(Class<?> forClass) throws RequestConfigurationException {
TypeTree tree = TypeTreeCreator.createTypeTree(forClass, null);
String exceptionId = Utils.getTypeId(forClass);
exceptions.put(exceptionId, tree);
}
/**
* Handles application excpeition.
*
* @param ae occured exception
* @throws Exception
*/
private void handleApplicationException(ApplicationException ae) throws Exception {
try {
if (exceptions.size() == 0) {
RequestConfigurationException exception =
new RequestConfigurationException(
"ApplicationException occured, but no exception type was specified.",
ae.getId());
throw exception;
}
InputStream is = ae.getInputStream();
String exceptionId = is.read_string();
TypeTree tree = exceptions.get(exceptionId);
if (tree == null) {
RequestConfigurationException exception =
new RequestConfigurationException(
"ApplicationException occured, but no such exception was defined",
ae.getId());
throw exception;
} else {
Exception ex = (Exception)TypeHelpersProxy.read(tree.getRootNode(), is);
throw ex;
}
} catch (Exception e) {
throw e;
}
}
/**
* Handles exceptions generated by CORBA API
*
* @param se
*/
private void handleSystemException(SystemException se) throws Exception {
if (se instanceof BAD_OPERATION) {
throw new CorbaException("Bad operation name: " + operation, se);
} else if (se instanceof BAD_PARAM) {
throw new CorbaException("Bad parameter", se);
} else {
// TODO: handle more system exception types
throw new CorbaException(se.getMessage(), se);
}
}
/**
* Gets operation name which is includes mapping rules
* @return
*/
private String getFinalOperationName() {
String result = operation;
if (referenceClass != null) {
Class<?>[] argumentTypes = new Class<?>[arguments.size()];
for (int i = 0; i < arguments.size(); i++) {
argumentTypes[i] = arguments.get(i).getClass();
}
Method method = MethodFinder.findMethod(referenceClass, operation, argumentTypes);
String newOperation = (String)operationsMap.get(method);
if (newOperation != null) {
result = newOperation;
}
}
return result;
}
/**
* Invokes previously configured request
*
* @return
*/
public DynaCorbaResponse invoke() throws Exception {
DynaCorbaResponse response = new DynaCorbaResponse();
String finalOperationName = getFinalOperationName();
OutputStream outputStream = ((ObjectImpl)remoteObject)._request(finalOperationName, true);
passArguments(outputStream);
try {
inputStream = remoteObject._invoke(outputStream);
if (inputStream != null && returnTree != null) {
response.setContent(TypeHelpersProxy.read(returnTree.getRootNode(), inputStream));
}
} catch (ApplicationException ae) {
handleApplicationException(ae);
} catch (SystemException se) {
handleSystemException(se);
} catch (Exception e) {
throw e;
} finally {
release();
}
return response;
}
/**
* Releases request resources
*/
private void release() {
remoteObject._releaseReply(inputStream);
}
}