blob: 7eb6b9cf31327512396c236ac2bf240f1fc12bf6 [file] [log] [blame]
/*
* Portions Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package com.sun.xml.internal.ws.protocol.soap.server;
import com.sun.xml.internal.ws.pept.ept.MessageInfo;
import com.sun.xml.internal.ws.pept.presentation.MessageStruct;
import com.sun.xml.internal.ws.pept.presentation.TargetFinder;
import com.sun.xml.internal.ws.pept.presentation.Tie;
import com.sun.xml.internal.ws.pept.protocol.MessageDispatcher;
import com.sun.xml.internal.ws.binding.BindingImpl;
import com.sun.xml.internal.ws.binding.soap.SOAPBindingImpl;
import com.sun.xml.internal.ws.client.BindingProviderProperties;
import com.sun.xml.internal.ws.encoding.JAXWSAttachmentMarshaller;
import com.sun.xml.internal.ws.encoding.soap.SOAPConstants;
import com.sun.xml.internal.ws.encoding.soap.SOAPDecoder;
import com.sun.xml.internal.ws.encoding.soap.SOAPEPTFactory;
import com.sun.xml.internal.ws.encoding.soap.SOAPEncoder;
import com.sun.xml.internal.ws.encoding.soap.internal.InternalMessage;
import com.sun.xml.internal.ws.encoding.soap.message.SOAPFaultInfo;
import com.sun.xml.internal.ws.handler.HandlerChainCaller;
import com.sun.xml.internal.ws.handler.HandlerChainCaller.Direction;
import com.sun.xml.internal.ws.handler.HandlerChainCaller.RequestOrResponse;
import com.sun.xml.internal.ws.handler.MessageContextUtil;
import com.sun.xml.internal.ws.handler.SOAPHandlerContext;
import com.sun.xml.internal.ws.model.soap.SOAPRuntimeModel;
import com.sun.xml.internal.ws.server.AppMsgContextImpl;
import com.sun.xml.internal.ws.server.RuntimeContext;
import com.sun.xml.internal.ws.server.RuntimeEndpointInfo;
import com.sun.xml.internal.ws.server.ServerRtException;
import com.sun.xml.internal.ws.spi.runtime.Invoker;
import com.sun.xml.internal.ws.spi.runtime.SystemHandlerDelegate;
import com.sun.xml.internal.ws.spi.runtime.WSConnection;
import com.sun.xml.internal.ws.spi.runtime.WebServiceContext;
import com.sun.xml.internal.ws.util.FastInfosetUtil;
import com.sun.xml.internal.ws.util.MessageInfoUtil;
import com.sun.xml.internal.ws.util.SOAPConnectionUtil;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.Binding;
import javax.xml.ws.ProtocolException;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.MessageContext.Scope;
import javax.xml.ws.soap.SOAPBinding;
import javax.xml.ws.soap.SOAPFaultException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.sun.xml.internal.ws.client.BindingProviderProperties.CONTENT_NEGOTIATION_PROPERTY;
public class SOAPMessageDispatcher implements MessageDispatcher {
private static final String[] contentTypes = {
"text/xml", "application/soap+xml", "application/xop+xml",
"application/fastinfoset", "application/soap+fastinfoset" };
private static final Logger logger = Logger.getLogger(
com.sun.xml.internal.ws.util.Constants.LoggingDomain + ".server.soapmd");
public SOAPMessageDispatcher() {
}
public void send(MessageInfo messageInfo) {
// Not required for server
throw new UnsupportedOperationException();
}
public void receive(MessageInfo messageInfo) {
// Checks the Content-Type to send unsupported media error
try {
checkContentType(messageInfo);
} catch(ServerRtException e) {
SOAPConnectionUtil.sendKnownError(messageInfo,
WSConnection.UNSUPPORTED_MEDIA);
return;
}
// Gets request stream from WSConnection and creates SOAP message
SOAPMessage soapMessage = null;
try {
soapMessage = getSOAPMessage(messageInfo);
} catch(Exception e) {
sendResponseError(messageInfo, e);
return;
}
// Set it before response is sent on transport. If transport creates
// any exception, this can be used not to send again
boolean sent = false;
try {
// Content negotiation logic
try {
// If FI is accepted by client, set property to optimistic
if (((com.sun.xml.internal.messaging.saaj.soap.MessageImpl) soapMessage).acceptFastInfoset()) {
messageInfo.setMetaData(CONTENT_NEGOTIATION_PROPERTY, "optimistic");
}
}
catch (ClassCastException e) {
// Content negotiation fails
}
// context holds MessageInfo, InternalMessage, SOAPMessage
SOAPHandlerContext context = new SOAPHandlerContext(messageInfo, null,
soapMessage);
// WebServiceContext's MessageContext is set into HandlerContext
updateHandlerContext(messageInfo, context);
context.getMessageContext().put(
MessageContext.MESSAGE_OUTBOUND_PROPERTY, Boolean.FALSE);
//set MESSAGE_ATTACHMENTS property
MessageContext msgCtxt = MessageInfoUtil.getMessageContext(messageInfo);
if (msgCtxt != null) {
MessageContextUtil.copyInboundMessageAttachments(msgCtxt, soapMessage.getAttachments());
}
SystemHandlerDelegate shd = getSystemHandlerDelegate(messageInfo);
SoapInvoker implementor = new SoapInvoker(messageInfo, soapMessage,
context, shd);
try {
if (shd == null) {
// Invokes request handler chain, endpoint, response handler chain
implementor.invoke();
} else {
context.setInvoker(implementor);
if (shd.processRequest(context.getSHDSOAPMessageContext())) {
implementor.invoke();
context.getMessageContext().put(
MessageContext.MESSAGE_OUTBOUND_PROPERTY, Boolean.TRUE);
shd.processResponse(context.getSHDSOAPMessageContext());
}
}
} finally {
sent = implementor.isSent(); // response is sent or not
}
if (!isOneway(messageInfo)) {
makeSOAPMessage(messageInfo, context);
sent = true;
sendResponse(messageInfo, context);
} else if (!sent) {
// Oneway and request handler chain reversed the execution direction
sent = true;
sendResponseOneway(messageInfo);
}
} catch(Exception e) {
e.printStackTrace();
logger.log(Level.SEVERE, e.getMessage(), e);
if (!sent) {
sendResponseError(messageInfo, e);
}
}
assert sent; // Make sure response is sent
}
/*
* This decodes the SOAPMessage into InternalMessage. Then InternalMessage
* is converted to java method and parameters and populates them into
* MessageInfo.
*
*/
protected void toMessageInfo(MessageInfo messageInfo, SOAPHandlerContext context) {
InternalMessage internalMessage = context.getInternalMessage();
try {
SOAPMessage soapMessage = context.getSOAPMessage();
if (internalMessage == null) {
// Bind headers, body from SOAPMessage
SOAPEPTFactory eptf = (SOAPEPTFactory)messageInfo.getEPTFactory();
SOAPDecoder decoder = eptf.getSOAPDecoder();
internalMessage = decoder.toInternalMessage(soapMessage, messageInfo);
} else {
// Bind headers from SOAPMessage
SOAPEPTFactory eptf = (SOAPEPTFactory)messageInfo.getEPTFactory();
SOAPDecoder decoder = eptf.getSOAPDecoder();
internalMessage = decoder.toInternalMessage(soapMessage, internalMessage, messageInfo);
}
//setup JAXWSAttachmentMarshaller for outgoing attachments
SOAPEPTFactory eptf = (SOAPEPTFactory)messageInfo.getEPTFactory();
SOAPEncoder encoder = eptf.getSOAPEncoder();
encoder.setAttachmentsMap(messageInfo, internalMessage);
} catch(Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
messageInfo.setResponseType(MessageStruct.UNCHECKED_EXCEPTION_RESPONSE);
messageInfo.setResponse(e);
}
// InternalMessage to MessageInfo
if (!isFailure(messageInfo)) {
SOAPEPTFactory eptf = (SOAPEPTFactory)messageInfo.getEPTFactory();
eptf.getInternalEncoder().toMessageInfo(internalMessage, messageInfo);
Binding binding = MessageInfoUtil.getRuntimeContext(messageInfo).getRuntimeEndpointInfo().getBinding();
String bindingId = (binding != null)?((SOAPBindingImpl)binding).getBindingId():SOAPBinding.SOAP11HTTP_BINDING;
if (messageInfo.getMethod() == null) {
messageInfo.setResponseType(MessageStruct.UNCHECKED_EXCEPTION_RESPONSE);
SOAPFaultInfo faultInfo = new SOAPFaultInfo(
"Cannot find dispatch method",
SOAPConstants.FAULT_CODE_SERVER,
null, null, bindingId);
messageInfo.setResponse(faultInfo);
}
}
}
/*
* Creates SOAPMessage from the connection's request stream
*/
private SOAPMessage getSOAPMessage(MessageInfo messageInfo) {
WSConnection con = (WSConnection)messageInfo.getConnection();
return SOAPConnectionUtil.getSOAPMessage(con, messageInfo, null);
}
/*
* Checks against known Content-Type headers
*/
private void checkContentType(MessageInfo mi) {
WSConnection con = (WSConnection)mi.getConnection();
Map<String, List<String>> headers = con.getHeaders();
List<String> cts = headers.get("Content-Type");
if (cts != null && cts.size() > 0) {
String ct = cts.get(0);
for(String contentType : contentTypes) {
if (ct.indexOf(contentType) != -1) {
return;
}
}
}
throw new ServerRtException("Incorrect Content-Type="+cts);
}
/*
* Sets the WebServiceContext with correct MessageContext which contains
* APPLICATION scope properties
*/
protected void updateWebServiceContext(MessageInfo messageInfo, SOAPHandlerContext hc) {
RuntimeContext rtCtxt = MessageInfoUtil.getRuntimeContext(messageInfo);
//rtCtxt.setHandlerContext(hc);
RuntimeEndpointInfo endpointInfo = rtCtxt.getRuntimeEndpointInfo();
WebServiceContext wsContext = endpointInfo.getWebServiceContext();
hc.getMessageContext().put(CONTENT_NEGOTIATION_PROPERTY,
messageInfo.getMetaData(CONTENT_NEGOTIATION_PROPERTY));
hc.getMessageContext().setScope(CONTENT_NEGOTIATION_PROPERTY, Scope.APPLICATION);
if (wsContext != null) {
AppMsgContextImpl appCtxt = new AppMsgContextImpl(hc.getMessageContext());
wsContext.setMessageContext(appCtxt);
}
}
/*
* Invokes the endpoint. For Provider endpoints, whether the operation is
* oneway or not known only after the endpoint is invoked. ProviderSOAPMD
* overrides this and sends the response message on transport for oneway
* operations.
*
* @return true if response is sent on transport
*/
protected void invokeEndpoint(MessageInfo messageInfo, SOAPHandlerContext hc) {
TargetFinder targetFinder =
messageInfo.getEPTFactory().getTargetFinder(messageInfo);
Tie tie = targetFinder.findTarget(messageInfo);
tie._invoke(messageInfo);
}
/**
* Converts java method parameters, and return value to InternalMessage.
* It calls response handlers. At the end, the context has either
* InternalMessage or SOAPMessage
*
*/
protected void getResponse(MessageInfo messageInfo, SOAPHandlerContext context) {
setResponseInContext(messageInfo, context);
try {
HandlerChainCaller handlerCaller =
getCallerFromMessageInfo(messageInfo);
if (handlerCaller != null && handlerCaller.hasHandlers()) {
int messageType = messageInfo.getResponseType();
if (messageType == MessageInfo.CHECKED_EXCEPTION_RESPONSE ||
messageType == MessageInfo.UNCHECKED_EXCEPTION_RESPONSE) {
callHandleFault(handlerCaller, context);
} else {
//there are handlers so disable Xop encoding if enabled, so that they dont
// see xop:Include reference
JAXWSAttachmentMarshaller am = MessageInfoUtil.getAttachmentMarshaller(messageInfo);
boolean isXopped = false;
if((am != null) && am.isXOPPackage()){
isXopped = am.isXOPPackage();
am.setXOPPackage(false);
}
callHandlersOnResponse(handlerCaller, context);
// now put back the old value
if((am != null)){
am.setXOPPackage(isXopped);
}
}
}
} catch(Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
createInternalMessageForException(messageInfo, e, context);
}
}
/*
*
*
*
*/
private boolean createInternalMessageForException(MessageInfo messageInfo,
Exception e, SOAPHandlerContext context) {
boolean soap12 = false;
RuntimeEndpointInfo rei = MessageInfoUtil.getRuntimeContext(
messageInfo).getRuntimeEndpointInfo();
String id = ((SOAPBindingImpl)rei.getBinding()).getBindingId();
InternalMessage internalMessage = null;
if (id.equals(SOAPBinding.SOAP11HTTP_BINDING)) {
internalMessage = SOAPRuntimeModel.createFaultInBody(
e, null, null, null);
} else if (id.equals(SOAPBinding.SOAP12HTTP_BINDING)) {
internalMessage = SOAPRuntimeModel.createSOAP12FaultInBody(
e, null, null, null, null);
soap12 = true;
}
context.setInternalMessage(internalMessage);
context.setSOAPMessage(null);
return soap12;
}
private void makeSOAPMessage(MessageInfo messageInfo, SOAPHandlerContext context) {
InternalMessage internalMessage = context.getInternalMessage();
if (internalMessage != null) {
SOAPEPTFactory eptf = (SOAPEPTFactory)messageInfo.getEPTFactory();
SOAPEncoder encoder = eptf.getSOAPEncoder();
SOAPMessage soapMesage = encoder.toSOAPMessage(internalMessage, messageInfo);
context.setSOAPMessage(soapMesage);
context.setInternalMessage(null);
}
}
/*
* MessageInfo contains the endpoint invocation results. The information
* is converted to InternalMessage or SOAPMessage and set in HandlerContext
*/
protected void setResponseInContext(MessageInfo messageInfo,
SOAPHandlerContext context) {
// MessageInfo to InternalMessage
SOAPEPTFactory eptf = (SOAPEPTFactory)messageInfo.getEPTFactory();
InternalMessage internalMessage = (InternalMessage)eptf.getInternalEncoder().toInternalMessage(
messageInfo);
// set handler context
context.setInternalMessage(internalMessage);
context.setSOAPMessage(null);
}
/*
* Sends SOAPMessage response on the connection
*/
private void sendResponse(MessageInfo messageInfo, SOAPHandlerContext ctxt) {
SOAPMessage soapMessage = ctxt.getSOAPMessage();
WSConnection con = (WSConnection)messageInfo.getConnection();
Integer status = MessageContextUtil.getHttpStatusCode(ctxt.getMessageContext());
int statusCode = (status == null) ? WSConnection.OK : status;
SOAPConnectionUtil.setStatus(con, statusCode);
SOAPConnectionUtil.sendResponse(con, soapMessage);
}
protected void sendResponseOneway(MessageInfo messageInfo) {
SOAPConnectionUtil.sendResponseOneway(messageInfo);
}
private void sendResponseError(MessageInfo messageInfo, Exception e) {
e.printStackTrace();
WSConnection con = (WSConnection)messageInfo.getConnection();
Binding binding = MessageInfoUtil.getRuntimeContext(messageInfo).getRuntimeEndpointInfo().getBinding();
String bindingId = ((SOAPBindingImpl)binding).getBindingId();
SOAPConnectionUtil.sendResponseError(con, bindingId);
}
/*
* Calls inbound handlers. It also calls outbound handlers incase flow is
* reversed. If the handler throws a ProtocolException, SOAP message is
* already set in the context. Otherwise, it creates InternalMessage,
* and that is used to create SOAPMessage.
*
* returns whether to invoke endpoint or not.
*/
private boolean callHandlersOnRequest(MessageInfo messageInfo,
SOAPHandlerContext context, boolean responseExpected) {
boolean skipEndpoint = false;
HandlerChainCaller handlerCaller =
getCallerFromMessageInfo(messageInfo);
if (handlerCaller != null && handlerCaller.hasHandlers()) {
try {
skipEndpoint = !handlerCaller.callHandlers(Direction.INBOUND,
RequestOrResponse.REQUEST, context, responseExpected);
} catch(ProtocolException pe) {
skipEndpoint = true;
if (MessageContextUtil.ignoreFaultInMessage(
context.getMessageContext())) {
// don't use the fault, use the exception
createInternalMessageForException(messageInfo, pe, context);
}
} catch(RuntimeException re) {
skipEndpoint = true;
createInternalMessageForException(messageInfo, re, context);
}
}
return skipEndpoint;
}
private HandlerChainCaller getCallerFromMessageInfo(MessageInfo info) {
RuntimeContext context = (RuntimeContext) info.getMetaData(
BindingProviderProperties.JAXWS_RUNTIME_CONTEXT);
BindingImpl binding = (BindingImpl)context.getRuntimeEndpointInfo().getBinding();
if (binding.hasHandlers()) {
HandlerChainCaller caller = binding.getHandlerChainCaller();
MessageInfoUtil.setHandlerChainCaller(info, caller);
return caller;
}
return null;
}
/**
*
* Invokes response handler chain
*/
protected boolean callHandlersOnResponse(HandlerChainCaller caller,
SOAPHandlerContext context) {
return caller.callHandlers(Direction.OUTBOUND,
RequestOrResponse.RESPONSE, context, false);
}
/**
* Used when the endpoint throws an exception. HandleFault is called
* on the server handlers rather than handleMessage.
*/
protected boolean callHandleFault(HandlerChainCaller caller, SOAPHandlerContext context) {
return caller.callHandleFault(context);
}
/**
* Server does not know if a message is one-way until after
* the handler chain has finished processing the request. If
* it is a one-way message, have the handler chain caller
* call close on the handlers.
*/
private void closeHandlers(MessageInfo info, SOAPHandlerContext context) {
HandlerChainCaller handlerCaller = getCallerFromMessageInfo(info);
if (handlerCaller != null && handlerCaller.hasHandlers()) {
handlerCaller.forceCloseHandlersOnServer(context);
}
}
private static boolean isFailure(MessageInfo messageInfo) {
return (messageInfo.getResponseType() == MessageStruct.UNCHECKED_EXCEPTION_RESPONSE);
}
public static boolean isOneway(MessageInfo messageInfo) {
return (messageInfo.getMEP() == MessageStruct.ONE_WAY_MEP);
}
/**
* Sets MessageContext into HandlerContext and sets HandlerContext in
* RuntimeContext
*/
private void updateHandlerContext(MessageInfo messageInfo,
SOAPHandlerContext context) {
MessageInfoUtil.getRuntimeContext(messageInfo).setHandlerContext(context);
RuntimeEndpointInfo endpointInfo =
MessageInfoUtil.getRuntimeContext(messageInfo).getRuntimeEndpointInfo();
context.setBindingId(((BindingImpl)endpointInfo.getBinding()).getActualBindingId());
WebServiceContext wsContext = endpointInfo.getWebServiceContext();
if (wsContext != null) {
context.setMessageContext(wsContext.getMessageContext());
}
}
/**
* Gets SystemHandlerDelegate from endpoint's Binding
*/
private SystemHandlerDelegate getSystemHandlerDelegate(MessageInfo mi) {
RuntimeContext rtCtxt = MessageInfoUtil.getRuntimeContext(mi);
RuntimeEndpointInfo endpointInfo = rtCtxt.getRuntimeEndpointInfo();
return endpointInfo.getBinding().getSystemHandlerDelegate();
}
/**
* Invokes request handler chain, endpoint and response handler chain.
* Separated as a separate class, so that SHD can call this in doPriv()
* block.
*/
private class SoapInvoker implements Invoker {
MessageInfo messageInfo;
SOAPMessage soapMessage;
SOAPHandlerContext context;
boolean skipEndpoint;
SystemHandlerDelegate shd;
boolean sent;
SoapInvoker(MessageInfo messageInfo, SOAPMessage soapMessage,
SOAPHandlerContext context, SystemHandlerDelegate shd) {
this.messageInfo = messageInfo;
this.soapMessage = soapMessage;
this.context = context;
this.shd = shd;
}
public void invoke() throws Exception {
boolean peekOneWay = false;
if (!skipEndpoint) {
try {
SOAPEPTFactory eptf = (SOAPEPTFactory)messageInfo.getEPTFactory();
SOAPDecoder decoder = eptf.getSOAPDecoder();
// add handler chain caller to message info
getCallerFromMessageInfo(messageInfo);
peekOneWay = decoder.doMustUnderstandProcessing(soapMessage,
messageInfo, context, true);
context.setMethod(messageInfo.getMethod());
} catch (SOAPFaultException e) {
skipEndpoint = true;
boolean soap12 = createInternalMessageForException(messageInfo, e, context);
SOAPRuntimeModel.addHeaders(context.getInternalMessage(),
messageInfo);
}
}
// Call inbound handlers. It also calls outbound handlers incase of
// reversal of flow.
if (!skipEndpoint) {
skipEndpoint = callHandlersOnRequest(
messageInfo, context, !peekOneWay);
}
if (skipEndpoint) {
soapMessage = context.getSOAPMessage();
if (soapMessage == null) {
InternalMessage internalMessage = context.getInternalMessage();
SOAPEPTFactory eptf = (SOAPEPTFactory)messageInfo.getEPTFactory();
SOAPEncoder encoder = eptf.getSOAPEncoder();
soapMessage = encoder.toSOAPMessage(internalMessage, messageInfo);
}
// Ensure message is encoded according to conneg
FastInfosetUtil.ensureCorrectEncoding(messageInfo, soapMessage);
context.setSOAPMessage(soapMessage);
context.setInternalMessage(null);
} else {
toMessageInfo(messageInfo, context);
if (isOneway(messageInfo)) {
sent = true;
sendResponseOneway(messageInfo);
if (!peekOneWay) { // handler chain didn't already clos
closeHandlers(messageInfo, context);
}
}
if (!isFailure(messageInfo)) {
if (shd != null) {
shd.preInvokeEndpointHook(context.getSHDSOAPMessageContext());
}
updateWebServiceContext(messageInfo, context);
invokeEndpoint(messageInfo, context);
// For Provider endpoints Oneway is known only after executing endpoint
if (!sent && isOneway(messageInfo)) {
sent = true;
sendResponseOneway(messageInfo);
}
context.getMessageContext().put(
MessageContext.MESSAGE_OUTBOUND_PROPERTY, Boolean.TRUE);
}
if (isOneway(messageInfo)) {
if (isFailure(messageInfo)) {
// Just log the error. Not much to do
}
} else {
getResponse(messageInfo, context);
}
}
}
/**
* Gets the dispatch method in the endpoint for the payload's QName
*
* @return dispatch method
*/
public Method getMethod(QName name) {
RuntimeContext rtCtxt = MessageInfoUtil.getRuntimeContext(messageInfo);
return rtCtxt.getDispatchMethod(name, messageInfo);
}
/*
* Is the message sent on transport. Happens when the operation is oneway
*
* @return true if the message is sent
* false otherwise
*/
public boolean isSent() {
return sent;
}
}
}