/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ws.core.jaxrpc.handler;
// $Id: HandlerChainBaseImpl.java 3146 2007-05-18 22:55:26Z thomas.diesler@jboss.com $
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.rpc.JAXRPCException;
import javax.xml.rpc.handler.Handler;
import javax.xml.rpc.handler.HandlerChain;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.soap.SOAPPart;
import org.jboss.logging.Logger;
import org.jboss.ws.Constants;
import org.jboss.ws.core.CommonMessageContext;
import org.jboss.ws.core.soap.SOAPElementImpl;
import org.jboss.ws.core.soap.SOAPElementWriter;
import org.jboss.ws.core.soap.SOAPEnvelopeImpl;
import org.jboss.wsf.spi.metadata.j2ee.serviceref.UnifiedHandlerMetaData.HandlerType;
/**
* Represents a list of handlers. All elements in the
* HandlerChain are of the type javax.xml.rpc.handler.Handler.
* <p/>
* Abstracts the policy and mechanism for the invocation of the registered handlers.
*
* @author Thomas.Diesler@jboss.org
* @since 06-May-2004
*/
public abstract class HandlerChainBaseImpl implements HandlerChain
{
private static Logger log = Logger.getLogger(HandlerChainBaseImpl.class);
public static final int STATE_DOES_NOT_EXIST = 0;
public static final int STATE_CREATED = 1;
public static final int STATE_READY = 2;
public static final int STATE_DESTROYED = 3;
// The List<Entry> objects
protected List<HandlerEntry> handlers = new ArrayList<HandlerEntry>();
// The roles associated with the handler chain
protected Set<String> roles = new HashSet<String>();
// The index of the first handler that returned false during processing
protected int falseIndex = -1;
// The state of this handler chain
protected int state;
/**
* Constructs a handler chain with the given handlers infos
*/
public HandlerChainBaseImpl(List<HandlerInfo> infos, Set<String> roles)
{
log.debug("Create a handler chain for roles: " + roles);
addHandlersToChain(infos, roles);
}
/** Get the list of handler infos
*/
public List<HandlerInfo> getHandlerInfos()
{
List<HandlerInfo> list = new ArrayList<HandlerInfo>();
for (int i = 0; i < handlers.size(); i++)
{
HandlerEntry entry = (HandlerEntry)handlers.get(i);
list.add(entry.info);
}
return list;
}
public void pullHeaders(Set<QName> headers)
{
for (HandlerEntry entry : handlers)
{
QName[] handlerHeaders = entry.handler.getHeaders();
if (handlerHeaders != null)
Collections.addAll(headers, handlerHeaders);
}
}
public Set<QName> getHeaders()
{
HashSet<QName> set = new HashSet<QName>();
pullHeaders(set);
return set;
}
/**
* Initialize the a handler chain with the given handlers infos
*/
private void addHandlersToChain(List<HandlerInfo> infos, Set<String> roleSet)
{
try
{
if (infos != null)
{
for (HandlerInfo info : infos)
{
HandlerWrapper handler = new HandlerWrapper((Handler)info.getHandlerClass().newInstance());
HandlerType type = (HandlerType)info.getHandlerConfig().get(HandlerType.class.getName());
handlers.add(new HandlerEntry(handler, info, type));
}
}
if (roleSet != null)
{
roles.addAll(roleSet);
}
}
catch (RuntimeException rte)
{
throw rte;
}
catch (Exception ex)
{
throw new JAXRPCException("Cannot initialize handler chain", ex);
}
// set state to created
state = STATE_CREATED;
}
/**
* Get the state of this handler chain
*/
public int getState()
{
return state;
}
/**
* Initializes the configuration for a HandlerChain.
*
* @param config Configuration for the initialization of this handler chain
* @throws javax.xml.rpc.JAXRPCException If any error during initialization
*/
public void init(Map config)
{
log.debug("init: [config=" + config + "]");
for (int i = 0; i < handlers.size(); i++)
{
HandlerEntry entry = (HandlerEntry)handlers.get(i);
entry.handler.init(entry.info);
}
// set state to ready
state = STATE_READY;
}
/**
* Indicates the end of lifecycle for a HandlerChain.
*
* @throws javax.xml.rpc.JAXRPCException If any error during destroy
*/
public void destroy()
{
log.debug("destroy");
for (int i = 0; i < handlers.size(); i++)
{
HandlerEntry entry = (HandlerEntry)handlers.get(i);
entry.handler.destroy();
}
handlers.clear();
// set state to destroyed
state = STATE_DESTROYED;
}
/**
* Gets SOAP actor roles registered for this HandlerChain at this SOAP node. The returned array includes the
* special SOAP actor next.
*
* @return SOAP Actor roles as URIs
*/
public String[] getRoles()
{
Set<String> auxlist = new HashSet<String>(roles);
auxlist.add(Constants.URI_SOAP11_NEXT_ACTOR);
String[] roleArr = new String[auxlist.size()];
auxlist.toArray(roleArr);
return roleArr;
}
/**
* Sets SOAP Actor roles for this HandlerChain. This specifies the set of roles in which this HandlerChain is to act
* for the SOAP message processing at this SOAP node. These roles assumed by a HandlerChain must be invariant during
* the processing of an individual SOAP message through the HandlerChain.
* <p/>
* A HandlerChain always acts in the role of the special SOAP actor next. Refer to the SOAP specification for the
* URI name for this special SOAP actor. There is no need to set this special role using this method.
*
* @param soapActorNames URIs for SOAP actor name
*/
public void setRoles(String[] soapActorNames)
{
List<String> newRoles = Arrays.asList(soapActorNames);
log.debug("setRoles: " + newRoles);
roles.clear();
roles.addAll(newRoles);
}
/**
* Initiates the request processing for this handler chain.
*
* @param msgContext MessageContext parameter provides access to the request SOAP message.
* @return Returns true if all handlers in chain have been processed. Returns false if a handler in the chain returned false from its handleRequest method.
* @throws javax.xml.rpc.JAXRPCException if any processing error happens
*/
public boolean handleRequest(MessageContext msgContext)
{
return handleRequestInternal(msgContext, HandlerType.ALL);
}
public boolean handleRequest(MessageContext msgContext, HandlerType type)
{
return handleRequestInternal(msgContext, type);
}
private boolean handleRequestInternal(MessageContext msgContext, HandlerType type)
{
boolean doNext = true;
if (handlers.size() > 0)
{
log.debug("Enter: handleRequest");
SOAPMessageContextJAXRPC jaxrpcContext = (SOAPMessageContextJAXRPC)msgContext;
jaxrpcContext.setProperty(CommonMessageContext.ALLOW_EXPAND_TO_DOM, Boolean.TRUE);
// Replace handlers that did not survive the previous call
replaceDirtyHandlers();
int handlerIndex = 0;
Handler currHandler = null;
try
{
String lastMessageTrace = null;
for (; doNext && handlerIndex < handlers.size(); handlerIndex++)
{
HandlerEntry handlerEntry = (HandlerEntry)handlers.get(handlerIndex);
if (type == HandlerType.ALL || type == handlerEntry.getType())
{
currHandler = handlerEntry.getHandler();
if (log.isTraceEnabled())
{
SOAPPart soapPart = jaxrpcContext.getSOAPMessage().getSOAPPart();
lastMessageTrace = traceSOAPPart("BEFORE handleRequest - " + currHandler, soapPart, lastMessageTrace);
}
doNext = currHandler.handleRequest(msgContext);
if (log.isTraceEnabled())
{
SOAPPart soapPart = jaxrpcContext.getSOAPMessage().getSOAPPart();
lastMessageTrace = traceSOAPPart("AFTER handleRequest - " + currHandler, soapPart, lastMessageTrace);
}
}
}
}
catch (RuntimeException e)
{
log.error("RuntimeException in request handler", e);
doNext = false;
throw e;
}
finally
{
// we start at this index in the response chain
if (doNext == false)
falseIndex = (handlerIndex - 1);
jaxrpcContext.removeProperty(CommonMessageContext.ALLOW_EXPAND_TO_DOM);
log.debug("Exit: handleRequest with status: " + doNext);
}
}
return doNext;
}
/**
* Initiates the response processing for this handler chain.
* <p/>
* In this implementation, the response handler chain starts processing from the same Handler
* instance (that returned false) and goes backward in the execution sequence.
*
* @return Returns true if all handlers in chain have been processed.
* Returns false if a handler in the chain returned false from its handleResponse method.
* @throws javax.xml.rpc.JAXRPCException if any processing error happens
*/
public boolean handleResponse(MessageContext msgContext)
{
return handleResponseInternal(msgContext, HandlerType.ALL);
}
public boolean handleResponse(MessageContext msgContext, HandlerType type)
{
return handleResponseInternal(msgContext, type);
}
private boolean handleResponseInternal(MessageContext msgContext, HandlerType type)
{
boolean doNext = true;
if (handlers.size() > 0)
{
log.debug("Enter: handleResponse");
SOAPMessageContextJAXRPC jaxrpcContext = (SOAPMessageContextJAXRPC)msgContext;
jaxrpcContext.setProperty(CommonMessageContext.ALLOW_EXPAND_TO_DOM, Boolean.TRUE);
int handlerIndex = handlers.size() - 1;
if (falseIndex != -1)
handlerIndex = falseIndex;
Handler currHandler = null;
try
{
String lastMessageTrace = null;
for (; doNext && handlerIndex >= 0; handlerIndex--)
{
HandlerEntry handlerEntry = (HandlerEntry)handlers.get(handlerIndex);
if (type == HandlerType.ALL || type == handlerEntry.getType())
{
currHandler = handlerEntry.getHandler();
if (log.isTraceEnabled())
{
SOAPPart soapPart = jaxrpcContext.getSOAPMessage().getSOAPPart();
lastMessageTrace = traceSOAPPart("BEFORE handleResponse - " + currHandler, soapPart, lastMessageTrace);
}
doNext = currHandler.handleResponse(msgContext);
if (log.isTraceEnabled())
{
SOAPPart soapPart = jaxrpcContext.getSOAPMessage().getSOAPPart();
lastMessageTrace = traceSOAPPart("AFTER handleResponse - " + currHandler, soapPart, lastMessageTrace);
}
}
}
}
catch (RuntimeException rte)
{
log.error("RuntimeException in response handler", rte);
doNext = false;
throw rte;
}
finally
{
// we start at this index in the fault chain
if (doNext == false)
falseIndex = (handlerIndex - 1);
jaxrpcContext.removeProperty(CommonMessageContext.ALLOW_EXPAND_TO_DOM);
log.debug("Exit: handleResponse with status: " + doNext);
}
}
return doNext;
}
/**
* Initiates the SOAP fault processing for this handler chain.
* <p/>
* In this implementation, the fault handler chain starts processing from the same Handler
* instance (that returned false) and goes backward in the execution sequence.
*
* @return Returns true if all handlers in chain have been processed.
* Returns false if a handler in the chain returned false from its handleFault method.
* @throws javax.xml.rpc.JAXRPCException if any processing error happens
*/
public boolean handleFault(MessageContext msgContext)
{
return handleFaultInternal(msgContext, HandlerType.ALL);
}
public boolean handleFault(MessageContext msgContext, HandlerType type)
{
return handleFaultInternal(msgContext, type);
}
private boolean handleFaultInternal(MessageContext msgContext, HandlerType type)
{
boolean doNext = true;
if (handlers.size() > 0)
{
log.debug("Enter: handleFault");
SOAPMessageContextJAXRPC jaxrpcContext = (SOAPMessageContextJAXRPC)msgContext;
jaxrpcContext.setProperty(CommonMessageContext.ALLOW_EXPAND_TO_DOM, Boolean.TRUE);
try
{
int handlerIndex = handlers.size() - 1;
if (falseIndex != -1)
handlerIndex = falseIndex;
Handler currHandler = null;
for (; doNext && handlerIndex >= 0; handlerIndex--)
{
HandlerEntry handlerEntry = (HandlerEntry)handlers.get(handlerIndex);
if (type == HandlerType.ALL || type == handlerEntry.getType())
{
currHandler = handlerEntry.getHandler();
log.debug("Handle fault: " + currHandler);
doNext = currHandler.handleFault(msgContext);
}
}
}
finally
{
jaxrpcContext.removeProperty(CommonMessageContext.ALLOW_EXPAND_TO_DOM);
log.debug("Exit: handleFault with status: " + doNext);
}
}
return doNext;
}
/** Trace the SOAPPart, do nothing if the String representation is equal to the last one.
*/
protected String traceSOAPPart(String logMsg, SOAPPart soapPart, String lastMessageTrace)
{
try
{
SOAPEnvelopeImpl soapEnv = (SOAPEnvelopeImpl)soapPart.getEnvelope();
String envStr = SOAPElementWriter.writeElement((SOAPElementImpl)soapEnv, true);
if (envStr.equals(lastMessageTrace))
{
log.trace(logMsg + ": unchanged");
}
else
{
log.trace(logMsg + "\n" + envStr);
lastMessageTrace = envStr;
}
return lastMessageTrace;
}
catch (Exception ex)
{
log.error("Cannot trace SOAP message", ex);
return null;
}
}
/**
* Replace handlers that did not survive the previous call
*/
protected void replaceDirtyHandlers()
{
for (int i = 0; i < handlers.size(); i++)
{
HandlerEntry entry = (HandlerEntry)handlers.get(i);
if (entry.handler.getState() == HandlerWrapper.DOES_NOT_EXIST)
{
log.debug("Replacing dirty handler: " + entry.handler);
try
{
HandlerWrapper handler = new HandlerWrapper((Handler)entry.info.getHandlerClass().newInstance());
entry.handler = handler;
handler.init(entry.info);
}
catch (RuntimeException rte)
{
throw rte;
}
catch (Exception ex)
{
log.error("Cannot create handler instance for: " + entry.info, ex);
}
}
}
}
/**
* Get the handler at the requested position
*/
protected Handler getHandlerAt(int pos)
{
if (pos < 0 || handlers.size() <= pos)
throw new IllegalArgumentException("No handler at position: " + pos);
HandlerEntry entry = (HandlerEntry)handlers.get(pos);
return entry.handler;
}
/**
* An entry in the handler list
*/
private class HandlerEntry
{
private HandlerWrapper handler;
private HandlerInfo info;
private HandlerType type;
public HandlerEntry(HandlerWrapper handler, HandlerInfo info, HandlerType type)
{
if (handler == null || info == null)
throw new IllegalStateException("Invalid handler entry");
if (type == null)
{
log.debug("Using handler type default: " + HandlerType.ENDPOINT);
type = HandlerType.ENDPOINT;
}
this.handler = handler;
this.info = info;
this.type = type;
}
public Handler getHandler()
{
return handler;
}
public HandlerInfo getInfo()
{
return info;
}
public HandlerType getType()
{
return type;
}
}
// java.util.List interface ****************************************************************************************
public boolean remove(Object o)
{
return handlers.remove(o);
}
public boolean containsAll(Collection c)
{
return handlers.containsAll(c);
}
public boolean removeAll(Collection c)
{
return handlers.removeAll(c);
}
public boolean retainAll(Collection c)
{
return handlers.retainAll(c);
}
public int hashCode()
{
return handlers.hashCode();
}
public boolean equals(Object o)
{
return handlers.equals(o);
}
public Iterator iterator()
{
return handlers.iterator();
}
public List subList(int fromIndex, int toIndex)
{
return handlers.subList(fromIndex, toIndex);
}
public ListIterator listIterator()
{
return handlers.listIterator();
}
public ListIterator listIterator(int index)
{
return handlers.listIterator(index);
}
public int size()
{
return handlers.size();
}
public void clear()
{
handlers.clear();
}
public boolean isEmpty()
{
return handlers.isEmpty();
}
public Object[] toArray()
{
return handlers.toArray();
}
public Object get(int index)
{
return handlers.get(index);
}
public Object remove(int index)
{
return handlers.remove(index);
}
public void add(int index, Object element)
{
handlers.add(index, (HandlerEntry)element);
}
public int indexOf(Object elem)
{
return handlers.indexOf(elem);
}
public int lastIndexOf(Object elem)
{
return handlers.lastIndexOf(elem);
}
public boolean add(Object o)
{
return handlers.add((HandlerEntry)o);
}
public boolean contains(Object elem)
{
return handlers.contains(elem);
}
public boolean addAll(int index, Collection c)
{
return handlers.addAll(index, c);
}
public boolean addAll(Collection c)
{
return handlers.addAll(c);
}
public Object set(int index, Object element)
{
return handlers.set(index, (HandlerEntry)element);
}
public Object[] toArray(Object[] a)
{
return handlers.toArray(a);
}
}