blob: 224a3293c5a8bea27890010108a1a15a22e6b058 [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.handler;
import com.sun.xml.internal.ws.spi.runtime.WSConnection;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.LogicalMessage;
import javax.xml.ws.ProtocolException;
import javax.xml.ws.handler.Handler;
import javax.xml.ws.handler.LogicalHandler;
import javax.xml.ws.handler.LogicalMessageContext;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.http.HTTPException;
import javax.xml.ws.soap.SOAPFaultException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sun.xml.internal.ws.encoding.soap.SOAPConstants;
import com.sun.xml.internal.ws.encoding.soap.SOAP12Constants;
import com.sun.xml.internal.ws.encoding.soap.streaming.SOAP12NamespaceConstants;
/**
* The class stores the actual "chain" of handlers that is called
* during a request or response. On the client side, it is created
* by a {@link com.sun.xml.internal.ws.binding.BindingImpl} class when a
* binding provider is created. On the server side, where a Binding
* object may be passed from an outside source, the handler chain
* caller may be created by the message dispatcher classes.
*
* <p>When created, a java.util.List of Handlers is passed in. This list
* is sorted into logical and protocol handlers, so the handler order
* that is returned from getHandlerChain() may be different from the
* original that was passed in.
*
* <p>At runtime, one of the callHandlers() methods is invoked by the
* soap or xml message dispatchers, passing in a {@link HandlerContext}
* or {@link XMLHandlerContext} object along with other information
* about the current message that is required for proper handler flow.
*
* <p>Exceptions are logged in many cases here before being rethrown. This
* is to help primarily with server side handlers.
*
* <p>Currently, the handler chain caller checks for a null soap
* message context to see if the binding in use is XML/HTTP.
*
* @see com.sun.xml.internal.ws.binding.BindingImpl
* @see com.sun.xml.internal.ws.protocol.soap.client.SOAPMessageDispatcher
* @see com.sun.xml.internal.ws.protocol.soap.server.SOAPMessageDispatcher
* @see com.sun.xml.internal.ws.protocol.xml.server.XMLMessageDispatcher
*
* @author WS Development Team
*/
public class HandlerChainCaller {
public static final String HANDLER_CHAIN_CALLER = "handler_chain_caller";
public static final String IGNORE_FAULT_PROPERTY =
"ignore fault in message";
private static final Logger logger = Logger.getLogger(
com.sun.xml.internal.ws.util.Constants.LoggingDomain + ".handler");
// need request or response for Handle interface
public enum RequestOrResponse { REQUEST, RESPONSE }
public enum Direction { OUTBOUND, INBOUND }
private Set<QName> understoodHeaders;
private List<Handler> handlers; // may be logical/soap mixed
private List<LogicalHandler> logicalHandlers;
private List<SOAPHandler> soapHandlers;
private Set<String> roles;
/**
* The handlers that are passed in will be sorted into
* logical and soap handlers. During this sorting, the
* understood headers are also obtained from any soap
* handlers.
*
* @param chain A list of handler objects, which can
* be protocol or logical handlers.
*/
public HandlerChainCaller(List<Handler> chain) {
if (chain == null) { // should only happen in testing
chain = new ArrayList<Handler>();
}
handlers = chain;
logicalHandlers = new ArrayList<LogicalHandler>();
soapHandlers = new ArrayList<SOAPHandler>();
understoodHeaders = new HashSet<QName>();
sortHandlers();
}
/**
* This list may be different than the chain that is passed
* in since the logical and protocol handlers must be separated.
*
* @return The list of handlers, sorted by logical and then protocol.
*/
public List<Handler> getHandlerChain() {
return handlers;
}
public boolean hasHandlers() {
return (handlers.size() != 0);
}
/**
* These are set by the SOAPBindingImpl when it creates the
* HandlerChainCaller or when new roles are set on the binding.
*
* @param roles A set of roles strings.
*/
public void setRoles(Set<String> roles) {
this.roles = roles;
}
/**
* Returns the roles that were passed in by the binding
* in the case of soap binding.
*/
public Set<String> getRoles() {
return roles;
}
/**
* Returns the headers understood by the handlers. This set
* is created when the handler chain caller is instantiated and
* the handlers are sorted. The set is comprised of headers
* returned from SOAPHandler.getHeaders() method calls.
*
* @return The set of all headers that the handlers declare
* that they understand.
*/
public Set<QName> getUnderstoodHeaders() {
return understoodHeaders;
}
/**
* This method separates the logical and protocol handlers. When
* this method returns, the original "handlers" List has been
* resorted.
*/
private void sortHandlers() {
for (Handler handler : handlers) {
if (LogicalHandler.class.isAssignableFrom(handler.getClass())) {
logicalHandlers.add((LogicalHandler) handler);
} else if (SOAPHandler.class.isAssignableFrom(handler.getClass())) {
soapHandlers.add((SOAPHandler) handler);
Set<QName> headers = ((SOAPHandler) handler).getHeaders();
if (headers != null) {
understoodHeaders.addAll(headers);
}
} else if (Handler.class.isAssignableFrom(handler.getClass())) {
throw new HandlerException(
"cannot.extend.handler.directly",
handler.getClass().toString());
} else {
throw new HandlerException("handler.not.valid.type",
handler.getClass().toString());
}
}
handlers.clear();
handlers.addAll(logicalHandlers);
handlers.addAll(soapHandlers);
}
/**
* Replace the message in the given message context with a
* fault message. If the context already contains a fault
* message, then return without changing it.
* Also sets the HTTP_RESPONSE_CODE in the context on Server-side.
*/
private void insertFaultMessage(ContextHolder holder,
ProtocolException exception) {
try {
SOAPMessageContext context = holder.getSMC();
if (context == null) { // non-soap case
LogicalMessageContext lmc = holder.getLMC();
LogicalMessage msg = lmc.getMessage();
if (msg != null) {
msg.setPayload(null);
}
//Set Status Code only if it is on server
if((Boolean)lmc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)){
if (exception instanceof HTTPException) {
lmc.put(MessageContext.HTTP_RESPONSE_CODE,((HTTPException)exception).getStatusCode());
} else {
lmc.put(MessageContext.HTTP_RESPONSE_CODE,WSConnection.INTERNAL_ERR);
}
}
return;
}
//Set Status Code only if it is on server
if((Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)){
context.put(MessageContext.HTTP_RESPONSE_CODE,WSConnection.INTERNAL_ERR);
}
SOAPMessage message = context.getMessage();
SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
SOAPBody body = envelope.getBody();
if (body.hasFault()) {
return;
}
if (envelope.getHeader() != null) {
envelope.getHeader().detachNode();
}
body.removeContents();
SOAPFault fault = body.addFault();
String envelopeNamespace = envelope.getNamespaceURI();
if (exception instanceof SOAPFaultException) {
SOAPFaultException sfe = (SOAPFaultException) exception;
SOAPFault userFault = sfe.getFault();
QName faultCode = userFault.getFaultCodeAsQName();
if (faultCode == null) {
faultCode = determineFaultCode(context);
}
fault.setFaultCode(faultCode);
String faultString = userFault.getFaultString();
if (faultString == null) {
if (sfe.getMessage() != null) {
faultString = sfe.getMessage();
} else {
faultString = sfe.toString();
}
}
fault.setFaultString(faultString);
String faultActor = userFault.getFaultActor();
if (faultActor == null) {
faultActor = "";
}
fault.setFaultActor(faultActor);
if (userFault.getDetail() != null) {
fault.addChildElement(userFault.getDetail());
}
} else {
fault.setFaultCode(determineFaultCode(context));
if (exception.getMessage() != null) {
fault.setFaultString(exception.getMessage());
} else {
fault.setFaultString(exception.toString());
}
}
} catch (Exception e) {
// severe since this is from runtime and not handler
logger.log(Level.SEVERE,
"exception while creating fault message in handler chain", e);
throw new RuntimeException(e);
}
}
/**
* <p>The expectation of the rest of the code is that,
* if a ProtocolException is thrown from the handler chain,
* the message contents reflect the protocol exception.
* However, if a new ProtocolException is thrown from
* the handleFault method, then the fault should be
* ignored and the new exception should be dispatched.
*
* <p>This method simply sets a property that is checked
* by the client and server code when a ProtocolException
* is caught. The property can be checked with
* {@link MessageContextUtil#ignoreFaultInMessage}
*/
private void addIgnoreFaultProperty(ContextHolder holder) {
LogicalMessageContext context = holder.getLMC();
context.put(IGNORE_FAULT_PROPERTY, Boolean.TRUE);
}
/**
* <p>Figure out if the fault code local part is client,
* server, sender, receiver, etc. This is called by
* insertFaultMessage.
*
* <p>This method should only be called when there is a ProtocolException
* during request. Reverse the Message direction first,
* So this method can use the MESSAGE_OUTBOUND_PROPERTY
* to determine whether it is being called on the client
* or the server side. If this changes in the spec, then
* something else will need to be passed to the method
* to determine whether the fault code is client or server.
*
* <p>For determining soap version, start checking with the
* latest version and default to soap 1.1.
*/
private QName determineFaultCode(SOAPMessageContext context)
throws SOAPException {
SOAPEnvelope envelope =
context.getMessage().getSOAPPart().getEnvelope();
String uri = envelope.getNamespaceURI();
// client case
if (!(Boolean) context.get(
MessageContext.MESSAGE_OUTBOUND_PROPERTY)) {
if (uri.equals(SOAP12NamespaceConstants.ENVELOPE)) {
return SOAP12Constants.FAULT_CODE_CLIENT;
}
return SOAPConstants.FAULT_CODE_CLIENT;
}
//server case
if (uri.equals(SOAP12NamespaceConstants.ENVELOPE)) {
return SOAP12Constants.FAULT_CODE_SERVER;
}
return SOAPConstants.FAULT_CODE_SERVER;
}
/**
* <p>Method used to call handlers with a HandlerContext that
* may contain logical and protocol handlers. This is the
* main entry point for calling the handlers in the case
* of SOAP binding. Before calling the handlers, the
* handler chain caller will set the outbound property and
* the roles on the message context.
*
* <p>Besides the context object passed in, the other information
* is used to control handler execution and closing. See the
* handler section of the spec for the rules concering handlers
* returning false, throwing exceptions, etc.
*
* @param direction Inbound or outbound.
* @param messageType Request or response.
* @param context A soap handler context containing the message.
* @param responseExpected A boolean indicating whether or not
* a response is expected to the current message (should be false
* for responses or one-way requests).
*
* @return True in the normal case, false if a handler
* returned false. This normally means that the runtime
* should reverse direction if called during a request.
*/
public boolean callHandlers(Direction direction,
RequestOrResponse messageType,
SOAPHandlerContext context,
boolean responseExpected) {
return internalCallHandlers(direction, messageType,
new ContextHolder(context), responseExpected);
}
/**
* Method used to call handlers with a HandlerContext that
* may contain logical handlers only. This is the
* main entry point for calling the handlers in the case
* of http binding. Before calling the handlers, the
* handler chain caller will set the outbound property on
* the message context.
*
* <p>Besides the context object passed in, the other information
* is used to control handler execution and closing. See the
* handler section of the spec for the rules concering handlers
* returning false, throwing exceptions, etc.
*
* @param direction Inbound or outbound.
* @param messageType Request or response.
* @param context A soap handler context containing the message.
* @param responseExpected A boolean indicating whether or not
* a response is expected to the current message (should be false
* for responses or one-way requests).
*
* @return True in the normal case, false if a handler
* returned false. This normally means that the runtime
* should reverse direction if called during a request.
*/
public boolean callHandlers(Direction direction,
RequestOrResponse messageType,
XMLHandlerContext context,
boolean responseExpected) {
return internalCallHandlers(direction, messageType,
new ContextHolder(context), responseExpected);
}
/**
* Main runtime method, called internally by the callHandlers()
* methods that may be called with HandlerContext or
* XMLHandlerContext objects.
*
* The boolean passed in is whether or not a response is required
* for the current message. See section 5.3.2. (todo: this section
* is going to change).
*
* The callLogicalHandlers and callProtocolHandlers methods will
* take care of execution once called and return true or false or
* throw an exception.
*/
private boolean internalCallHandlers(Direction direction,
RequestOrResponse messageType,
ContextHolder ch,
boolean responseExpected) {
// set outbound property
ch.getLMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY,
(direction == Direction.OUTBOUND));
// if there is as soap message context, set roles
if (ch.getSMC() != null) {
((SOAPMessageContextImpl) ch.getSMC()).setRoles(getRoles());
}
// call handlers
if (direction == Direction.OUTBOUND) {
if (callLogicalHandlers(ch, direction, messageType,
responseExpected) == false) {
return false;
}
if (callProtocolHandlers(ch, direction, messageType,
responseExpected) == false) {
return false;
}
} else {
if (callProtocolHandlers(ch, direction, messageType,
responseExpected) == false) {
return false;
}
if (callLogicalHandlers(ch, direction, messageType,
responseExpected) == false) {
return false;
}
}
/*
* Close if MEP finished. Server code responsible for closing
* handlers if it determines that an incoming request is a
* one way message.
*/
if (!responseExpected) {
if (messageType == RequestOrResponse.REQUEST) {
if (direction == Direction.INBOUND) {
closeHandlersServer(ch);
} else {
closeHandlersClient(ch);
}
} else {
if (direction == Direction.INBOUND) {
closeHandlersClient(ch);
} else {
closeHandlersServer(ch);
}
}
}
return true;
}
/**
* This method called by the server when an endpoint has thrown
* an exception. This method calls handleFault on the handlers
* and closes them. Because this method is called only during
* a response after the endpoint has been reached, all of the
* handlers have been called during the request and so all are
* closed.
*/
public boolean callHandleFault(SOAPHandlerContext context) {
ContextHolder ch = new ContextHolder(context);
ch.getSMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY, true);
((SOAPMessageContextImpl) ch.getSMC()).setRoles(getRoles());
int i = 0; // counter for logical handlers
int j = 0; // counter for protocol handlers
try {
while (i < logicalHandlers.size()) {
if (logicalHandlers.get(i).handleFault(ch.getLMC()) == false) {
return false;
}
i++;
}
while (j < soapHandlers.size()) {
if (soapHandlers.get(j).handleFault(ch.getSMC()) == false) {
return false;
}
j++;
}
} catch (RuntimeException re) {
logger.log(Level.FINER, "exception in handler chain", re);
throw re;
} finally {
closeHandlersServer(ch); // this is always called on server side
}
return true;
}
/**
* This method called by the client when it sees a SOAPFault message.
* This method calls handleFault on the handlers and closes them. Because
* this method is called only during a response, all of the handlers have
* been called during the request and so all are closed.
*/
public boolean callHandleFaultOnClient(SOAPHandlerContext context) {
ContextHolder ch = new ContextHolder(context);
ch.getSMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY, false);
((SOAPMessageContextImpl) ch.getSMC()).setRoles(getRoles());
try {
for (int i=soapHandlers.size()-1; i>=0; i--) {
if (soapHandlers.get(i).handleFault(ch.getSMC()) == false) {
return false;
}
}
for (int i=logicalHandlers.size()-1; i>=0; i--) {
if (logicalHandlers.get(i).handleFault(ch.getLMC()) == false) {
return false;
}
}
} catch (RuntimeException re) {
logger.log(Level.FINER, "exception in handler chain", re);
throw re;
} finally {
closeHandlersClient(ch);
}
return true;
}
/**
* Called from the main callHandlers() method.
* Logical message context updated before this method is called.
*/
private boolean callLogicalHandlers(ContextHolder holder,
Direction direction, RequestOrResponse type, boolean responseExpected) {
if (direction == Direction.OUTBOUND) {
int i = 0;
try {
while (i < logicalHandlers.size()) {
if (logicalHandlers.get(i).
handleMessage(holder.getLMC()) == false) {
if (responseExpected) {
// reverse and call handle message
holder.getLMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY,false);
callLogicalHandleMessage(holder, i-1, 0);
}
if (type == RequestOrResponse.RESPONSE) {
closeHandlersServer(holder);
} else {
closeLogicalHandlers(holder, i, 0);
}
return false;
}
i++;
}
} catch (RuntimeException re) {
logger.log(Level.FINER, "exception in handler chain", re);
if (responseExpected && re instanceof ProtocolException) {
// reverse direction and handle fault
holder.getLMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY,false);
insertFaultMessage(holder, (ProtocolException) re);
if (i>0) {
try {
callLogicalHandleFault(holder, i-1, 0);
} catch (ProtocolException re1) {
addIgnoreFaultProperty(holder);
re = re1;
} catch (RuntimeException re2) {
re = re2;
}
}
}
if (type == RequestOrResponse.RESPONSE) {
closeHandlersServer(holder);
} else {
closeLogicalHandlers(holder, i, 0);
}
throw re;
}
} else { // inbound case, H(x) -> H(x-1) -> ... H(1) -> H(0)
int i = logicalHandlers.size()-1;
try {
while (i >= 0) {
if (logicalHandlers.get(i).
handleMessage(holder.getLMC()) == false) {
if (responseExpected) {
// reverse and call handle message/response
holder.getLMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY,true);
callLogicalHandleMessage(holder, i+1,
logicalHandlers.size()-1);
callProtocolHandleMessage(holder, 0,
soapHandlers.size()-1);
}
if (type == RequestOrResponse.RESPONSE) {
closeHandlersClient(holder);
} else {
closeLogicalHandlers(holder, i,
logicalHandlers.size()-1);
closeProtocolHandlers(holder, 0,
soapHandlers.size()-1);
}
return false;
}
i--;
}
} catch (RuntimeException re) {
logger.log(Level.FINER, "exception in handler chain", re);
if (responseExpected && re instanceof ProtocolException) {
// reverse direction and handle fault
holder.getLMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY,true);
insertFaultMessage(holder, (ProtocolException) re);
try {
// if i==size-1, no more logical handlers to call
if (i == logicalHandlers.size()-1 ||
callLogicalHandleFault(holder, i+1,
logicalHandlers.size()-1)) {
callProtocolHandleFault(holder, 0,
soapHandlers.size()-1);
}
} catch (ProtocolException re1) {
addIgnoreFaultProperty(holder);
re = re1;
} catch (RuntimeException re2) {
re = re2;
}
}
if (type == RequestOrResponse.RESPONSE) {
closeHandlersClient(holder);
} else {
closeLogicalHandlers(holder, i, logicalHandlers.size()-1);
closeProtocolHandlers(holder, 0, soapHandlers.size()-1);
}
throw re;
}
}
return true;
}
/**
* Called from the main callHandlers() method.
* SOAP message context updated before this method is called.
*/
private boolean callProtocolHandlers(ContextHolder holder,
Direction direction, RequestOrResponse type, boolean responseExpected) {
if (direction == Direction.OUTBOUND) {
int i = 0;
try {
while (i<soapHandlers.size()) {
if (soapHandlers.get(i).
handleMessage(holder.getSMC()) == false) {
if (responseExpected) {
// reverse and call handle message/response
holder.getSMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY,false);
if (i>0) {
callProtocolHandleMessage(holder, i-1, 0);
}
callLogicalHandleMessage(holder,
logicalHandlers.size()-1, 0);
}
if (type == RequestOrResponse.RESPONSE) {
closeHandlersServer(holder);
} else {
closeProtocolHandlers(holder, i, 0);
closeLogicalHandlers(holder,
logicalHandlers.size()-1 , 0);
}
return false;
}
i++;
}
} catch (RuntimeException re) {
logger.log(Level.FINER, "exception in handler chain", re);
if (responseExpected && re instanceof ProtocolException) {
// reverse direction and handle fault
holder.getSMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY,false);
insertFaultMessage(holder, (ProtocolException) re);
try {
if (i == 0 || // still on first handler
callProtocolHandleFault(holder, i-1, 0)) {
callLogicalHandleFault(holder,
logicalHandlers.size()-1, 0);
}
} catch (ProtocolException re1) {
addIgnoreFaultProperty(holder);
re = re1;
} catch (RuntimeException re2) {
re = re2;
}
}
if (type == RequestOrResponse.RESPONSE) {
closeHandlersServer(holder);
} else {
closeProtocolHandlers(holder, i, 0);
closeLogicalHandlers(holder, logicalHandlers.size()-1, 0);
}
throw re;
}
} else { // inbound case, H(x) -> H(x-1) -> ... H(1) -> H(0)
int i = soapHandlers.size()-1;
try {
while (i >= 0) {
if (soapHandlers.get(i).
handleMessage(holder.getSMC()) == false) {
// reverse and call handle message/response
holder.getSMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY,true);
if (responseExpected && i != soapHandlers.size()-1) {
callProtocolHandleMessage(holder, i+1,
soapHandlers.size()-1);
}
if (type == RequestOrResponse.RESPONSE) {
closeHandlersClient(holder);
} else {
closeProtocolHandlers(holder, i,
soapHandlers.size()-1);
}
return false;
}
i--;
}
} catch (RuntimeException re) {
logger.log(Level.FINER, "exception in handler chain", re);
if (responseExpected && re instanceof ProtocolException) {
// reverse direction and handle fault
holder.getSMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY,true);
insertFaultMessage(holder, (ProtocolException) re);
try {
if (i < soapHandlers.size()-1) {
callProtocolHandleFault(holder, i+1,
soapHandlers.size()-1);
}
} catch (ProtocolException re1) {
addIgnoreFaultProperty(holder);
re = re1;
} catch (RuntimeException re2) {
re = re2;
}
}
if (type == RequestOrResponse.RESPONSE) {
closeHandlersClient(holder);
} else {
closeProtocolHandlers(holder, i, soapHandlers.size()-1);
}
throw re;
}
}
return true;
}
/**
* Method called for abnormal processing (for instance, as the
* result of a handler returning false during normal processing).
* Start and end indices are inclusive.
*/
private void callLogicalHandleMessage(ContextHolder holder,
int start, int end) {
if (logicalHandlers.isEmpty() ||
start == -1 ||
start == logicalHandlers.size()) {
return;
}
callGenericHandleMessage(logicalHandlers,holder.getLMC(),start,end);
}
/**
* Method called for abnormal processing (for instance, as the
* result of a handler returning false during normal processing).
* Start and end indices are inclusive.
*/
private void callProtocolHandleMessage(ContextHolder holder,
int start, int end) {
if (soapHandlers.isEmpty()) {
return;
}
callGenericHandleMessage(soapHandlers,holder.getSMC(),start,end);
}
/**
* Utility method for calling handleMessage during abnormal processing(for
* instance, as the result of a handler returning false during normal
* processing). Start and end indices are inclusive.
*/
private <C extends MessageContext> void callGenericHandleMessage(List<? extends Handler> handlerList,
C context, int start, int end) {
if (handlerList.isEmpty()) {
return ;
}
int i = start;
if (start > end) {
try {
while (i >= end) {
if (handlerList.get(i).handleMessage(context) == false)
return;
i--;
}
} catch (RuntimeException re) {
logger.log(Level.FINER,
"exception in handler chain", re);
throw re;
}
} else {
try {
while (i <= end) {
if (handlerList.get(i).handleMessage(context) == false)
return ;
i++;
}
} catch (RuntimeException re) {
logger.log(Level.FINER,
"exception in handler chain", re);
throw re;
}
}
return;
}
/*
* Calls handleFault on the logical handlers. Indices are
* inclusive. Exceptions get passed up the chain, and an
* exception or return of 'false' ends processing.
*/
private boolean callLogicalHandleFault(ContextHolder holder,
int start, int end) {
return callGenericHandleFault(logicalHandlers,
holder.getLMC(), start, end);
}
/**
* Calls handleFault on the protocol handlers. Indices are
* inclusive. Exceptions get passed up the chain, and an
* exception or return of 'false' ends processing.
*/
private boolean callProtocolHandleFault(ContextHolder holder,
int start, int end) {
return callGenericHandleFault(soapHandlers,
holder.getSMC(), start, end);
}
/*
* Used by callLogicalHandleFault and callProtocolHandleFault.
*/
private <C extends MessageContext> boolean callGenericHandleFault(List<? extends Handler> handlerList,
C context, int start, int end) {
if (handlerList.isEmpty()) {
return true;
}
int i = start;
if (start > end) {
try {
while (i >= end) {
if (handlerList.get(i).
handleFault(context) == false) {
return false;
}
i--;
}
} catch (RuntimeException re) {
logger.log(Level.FINER,
"exception in handler chain", re);
throw re;
}
} else {
try {
while (i <= end) {
if (handlerList.get(i).
handleFault(context) == false) {
return false;
}
i++;
}
} catch (RuntimeException re) {
logger.log(Level.FINER,
"exception in handler chain", re);
throw re;
}
}
return true;
}
/**
* Method that closes protocol handlers and then
* logical handlers.
*/
private void closeHandlersClient(ContextHolder holder) {
closeProtocolHandlers(holder, soapHandlers.size()-1, 0);
closeLogicalHandlers(holder, logicalHandlers.size()-1, 0);
}
/**
* Method that closes logical handlers and then
* protocol handlers.
*/
private void closeHandlersServer(ContextHolder holder) {
closeLogicalHandlers(holder, 0, logicalHandlers.size()-1);
closeProtocolHandlers(holder, 0, soapHandlers.size()-1);
}
/**
* This version is called by the server code once it determines
* that an incoming message is a one-way request.
*/
public void forceCloseHandlersOnServer(SOAPHandlerContext context) {
ContextHolder ch = new ContextHolder(context);
// only called after an inbound request
ch.getSMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY, false);
((SOAPMessageContextImpl) ch.getSMC()).setRoles(getRoles());
closeHandlersServer(ch);
}
/**
* It is called by the client when an MU fault occurs since the handlerchain
* never gets invoked. The direction is an inbound message.
*/
public void forceCloseHandlersOnClient(SOAPHandlerContext context) {
ContextHolder ch = new ContextHolder(context);
// only called after an inbound request
ch.getSMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY, false);
((SOAPMessageContextImpl) ch.getSMC()).setRoles(getRoles());
closeHandlersClient(ch);
}
/**
* Version of forceCloseHandlers(HandlerContext) that is used
* by XML binding.
*/
public void forceCloseHandlersOnServer(XMLHandlerContext context) {
ContextHolder ch = new ContextHolder(context);
// only called after an inbound request
ch.getLMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY, false);
closeHandlersServer(ch);
}
/**
* Version of forceCloseHandlers(HandlerContext) that is used
* by XML binding.
*/
public void forceCloseHandlersOnClient(XMLHandlerContext context) {
ContextHolder ch = new ContextHolder(context);
// only called after an inbound request
ch.getLMC().put(MessageContext.MESSAGE_OUTBOUND_PROPERTY, false);
closeHandlersClient(ch);
}
private void closeProtocolHandlers(ContextHolder holder,
int start, int end) {
closeGenericHandlers(soapHandlers, holder.getSMC(), start, end);
}
private void closeLogicalHandlers(ContextHolder holder,
int start, int end) {
closeGenericHandlers(logicalHandlers, holder.getLMC(), start, end);
}
/**
* Calls close on the handlers from the starting
* index through the ending index (inclusive). Made indices
* inclusive to allow both directions more easily.
*/
private void closeGenericHandlers(List<? extends Handler> handlerList,
MessageContext context, int start, int end) {
if (handlerList.isEmpty()) {
return;
}
if (start > end) {
for (int i=start; i>=end; i--) {
try {
handlerList.get(i).close(context);
} catch (RuntimeException re) {
logger.log(Level.INFO,
"Exception ignored during close", re);
}
}
} else {
for (int i=start; i<=end; i++) {
try {
handlerList.get(i).close(context);
} catch (RuntimeException re) {
logger.log(Level.INFO,
"Exception ignored during close", re);
}
}
}
}
/**
* Used to hold the context objects that are used to get
* and set the current message.
*
* If a HandlerContext is passed in, both logical and soap
* handlers are used. If XMLHandlerContext is passed in,
* only logical handlers are assumed to be present.
*/
static class ContextHolder {
boolean logicalOnly;
SOAPHandlerContext context;
XMLHandlerContext xmlContext;
ContextHolder(SOAPHandlerContext context) {
this.context = context;
logicalOnly = false;
}
ContextHolder(XMLHandlerContext xmlContext) {
this.xmlContext = xmlContext;
logicalOnly = true;
}
LogicalMessageContext getLMC() {
return (logicalOnly ? xmlContext.getLogicalMessageContext() :
context.getLogicalMessageContext());
}
SOAPMessageContext getSMC() {
return (logicalOnly ? null : context.getSOAPMessageContext());
}
}
}