/** * 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 (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 } } }