/**
* 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.openejb.server.axis;
import org.apache.axis.AxisFault;
import org.apache.axis.Constants;
import org.apache.axis.Handler;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.description.OperationDesc;
import org.apache.axis.description.ParameterDesc;
import org.apache.axis.description.ServiceDesc;
import org.apache.axis.encoding.XMLType;
import org.apache.axis.encoding.DeserializationContext;
import org.apache.axis.message.RPCElement;
import org.apache.axis.message.RPCParam;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.axis.message.SOAPBodyElement;
import org.apache.axis.message.SOAPFault;
import org.apache.axis.providers.java.RPCProvider;
import org.apache.axis.utils.JavaUtils;
import org.apache.openejb.ApplicationException;
import org.apache.openejb.BeanContext;
import org.apache.openejb.InvalidateReferenceException;
import org.apache.openejb.RpcContainer;
import org.apache.openejb.InterfaceType;
import org.apache.openejb.server.ServerRuntimeException;
import org.xml.sax.SAXException;
import org.xml.sax.InputSource;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import javax.xml.rpc.handler.HandlerChain;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.holders.IntHolder;
import javax.xml.rpc.soap.SOAPFaultException;
import javax.xml.soap.SOAPMessage;
import javax.xml.namespace.QName;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
public class EjbRpcProvider extends RPCProvider {
private final BeanContext ejbDeployment;
private final List<HandlerInfo> handlerInfos;
public EjbRpcProvider(BeanContext ejbDeployment) {
this.ejbDeployment = ejbDeployment;
this.handlerInfos = new ArrayList<HandlerInfo>();
}
public EjbRpcProvider(BeanContext ejbDeployment, List<HandlerInfo> handlerInfos) {
this.ejbDeployment = ejbDeployment;
this.handlerInfos = handlerInfos;
}
public void processMessage(MessageContext msgContext, SOAPEnvelope reqEnv, SOAPEnvelope resEnv, Object obj) throws Exception {
RPCElement body = getBody(reqEnv, msgContext);
OperationDesc operation = getOperationDesc(msgContext, body);
AxisRpcInterceptor interceptor = new AxisRpcInterceptor(operation, msgContext);
SOAPMessage message = msgContext.getMessage();
try {
message.getSOAPPart().getEnvelope();
msgContext.setProperty(org.apache.axis.SOAPPart.ALLOW_FORM_OPTIMIZATION, Boolean.FALSE);
RpcContainer container = (RpcContainer) ejbDeployment.getContainer();
Object[] arguments = {msgContext, interceptor};
Class callInterface = ejbDeployment.getServiceEndpointInterface();
Object result = container.invoke(ejbDeployment.getDeploymentID(), InterfaceType.SERVICE_ENDPOINT, callInterface, operation.getMethod(), arguments, null);
interceptor.createResult(result);
} catch (InvalidateReferenceException e) {
interceptor.createExceptionResult(e.getCause());
} catch (ApplicationException e) {
interceptor.createExceptionResult(e.getCause());
} catch (Throwable throwable) {
throw new AxisFault("Web Service EJB Invocation failed: method " + operation.getMethod(), throwable);
}
}
public Object getServiceObject(MessageContext msgContext, Handler service, String clsName, IntHolder scopeHolder) throws Exception {
return ejbDeployment;
}
/**
* This class is intentionally not static or top level class
* as it leverages logic in RPCProvider
*
* @see org.apache.axis.providers.java.RPCProvider
*/
public class AxisRpcInterceptor {
private OperationDesc operation;
private MessageContext messageContext;
public AxisRpcInterceptor(OperationDesc operation, MessageContext msgContext) throws Exception {
this.messageContext = msgContext;
this.operation = operation;
}
@AroundInvoke
public Object intercept(InvocationContext context) throws Exception {
HandlerChain handlerChain = new HandlerChainImpl(handlerInfos);
try {
Object invocationResult = null;
try {
if (handlerChain.handleRequest(messageContext)) {
// update arguments as handlers could change the soap msg
context.setParameters(getArguments());
invocationResult = context.proceed();
// update the soap msg so that handlers see invocation result
if (!handlerChain.isEmpty()) {
createResult(invocationResult);
}
} else {
/* The Handler implementation class has the responsibility of setting
* the response SOAP message in the handleRequest method and perform
* additional processing in the handleResponse method.
*/
invocationResult = null;
}
} catch (SOAPFaultException e) {
handlerChain.handleFault(messageContext);
throw e;
}
handlerChain.handleResponse(messageContext);
if (!handlerChain.isEmpty()) {
/*
* Deserialize the result value from soap msg as handers could have
* changed it.
*/
try {
invocationResult = demarshallResult();
} catch (Exception e) {
// if this fails, return invocationResult from above
}
}
return invocationResult;
} finally {
handlerChain.destroy();
}
}
public Object[] getArguments() {
try {
return demarshallArguments();
} catch (Exception e) {
throw (IllegalStateException) new IllegalStateException("Cannot demarshal the soap parts into arguments").initCause(e);
}
}
private Object[] demarshallArguments() throws Exception {
SOAPMessage message = messageContext.getMessage();
messageContext.setProperty(org.apache.axis.SOAPPart.ALLOW_FORM_OPTIMIZATION, Boolean.TRUE);
if (message != null) {
message.saveChanges();
}
try {
Message reqMsg = messageContext.getRequestMessage();
SOAPEnvelope requestEnvelope = reqMsg.getSOAPEnvelope();
RPCElement body = getBody(requestEnvelope, messageContext);
body.setNeedDeser(true);
Vector args = null;
try {
args = body.getParams();
} catch (SAXException e) {
if (e.getException() != null) {
throw e.getException();
}
throw e;
}
Object[] argValues = new Object[operation.getNumParams()];
for (int i = 0; i < args.size(); i++) {
RPCParam rpcParam = (RPCParam) args.get(i);
Object value = rpcParam.getObjectValue();
ParameterDesc paramDesc = rpcParam.getParamDesc();
if (paramDesc != null && paramDesc.getJavaType() != null) {
value = JavaUtils.convert(value, paramDesc.getJavaType());
rpcParam.setObjectValue(value);
}
int order = (paramDesc == null || paramDesc.getOrder() == -1) ? i : paramDesc.getOrder();
argValues[order] = value;
}
return argValues;
} finally {
messageContext.setProperty(org.apache.axis.SOAPPart.ALLOW_FORM_OPTIMIZATION, Boolean.FALSE);
}
}
private Object demarshallResult() throws Exception {
Message resMsg = messageContext.getResponseMessage();
/*
* This is not the most efficient way to deserialize the result
* but could not find better or more reliable way to do this.
*/
ByteArrayOutputStream out = new ByteArrayOutputStream();
resMsg.writeTo(out);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
DeserializationContext dser =
new DeserializationContext(new InputSource(in), resMsg.getMessageContext(), null);
dser.parse();
SOAPEnvelope responseEnvelope = dser.getEnvelope();
SOAPBodyElement bodyEl = responseEnvelope.getFirstBody();
if (bodyEl == null) {
return null;
}
QName returnType = operation.getReturnType();
if (XMLType.AXIS_VOID.equals(returnType)) {
return null;
}
Object result = null;
if (bodyEl instanceof RPCElement) {
RPCElement body = (RPCElement)bodyEl;
body.setNeedDeser(true);
Vector args = null;
try {
args = body.getParams();
} catch (SAXException e) {
if (e.getException() != null) {
throw e.getException();
}
throw e;
}
QName returnParamQName = operation.getReturnQName();
if (args != null && args.size() > 0) {
if (returnParamQName == null) {
RPCParam param = (RPCParam) args.get(0);
result = param.getObjectValue();
} else {
for (int i = 0; i < args.size(); i++) {
RPCParam param = (RPCParam) args.get(i);
if (returnParamQName.equals(param.getQName())) {
result = param.getObjectValue();
break;
}
}
}
}
} else {
try {
result = bodyEl.getValueAsType(returnType);
} catch (Exception e) {
result = bodyEl;
}
}
if (operation.getReturnClass() != null) {
result = JavaUtils.convert(result, operation.getReturnClass());
}
return result;
}
public void createResult(Object object) {
messageContext.setPastPivot(true);
try {
Message requestMessage = messageContext.getRequestMessage();
SOAPEnvelope requestEnvelope = requestMessage.getSOAPEnvelope();
RPCElement requestBody = getBody(requestEnvelope, messageContext);
Message responseMessage = messageContext.getResponseMessage();
SOAPEnvelope responseEnvelope = responseMessage.getSOAPEnvelope();
ServiceDesc serviceDescription = messageContext.getService().getServiceDescription();
RPCElement responseBody = createResponseBody(requestBody, messageContext, operation, serviceDescription, object, responseEnvelope, getInOutParams());
responseEnvelope.removeBody();
responseEnvelope.addBodyElement(responseBody);
} catch (Exception e) {
throw new ServerRuntimeException("Failed while creating response message body", e);
}
}
public void createExceptionResult(Throwable exception) {
messageContext.setPastPivot(true);
AxisFault axisFault = null;
if (exception instanceof Exception) {
axisFault = AxisFault.makeFault((Exception)exception);
axisFault.setFaultCodeAsString(Constants.FAULT_SERVER_GENERAL);
} else {
axisFault = new AxisFault("Server", "Server Error", null, null);
}
SOAPFault fault = new SOAPFault(axisFault);
SOAPEnvelope envelope = new SOAPEnvelope();
envelope.addBodyElement(fault);
Message message = new Message(envelope);
message.setMessageType(Message.RESPONSE);
messageContext.setResponseMessage(message);
}
public ArrayList getInOutParams() {
return new ArrayList(); //TODO collect out an inout params in demarshalArguments
}
}
}