package org.jboss.seam.webservice;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import org.jboss.seam.contexts.Lifecycle;
import org.jboss.seam.contexts.ServletLifecycle;
import org.jboss.seam.core.ConversationPropagation;
import org.jboss.seam.core.Manager;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.jboss.seam.servlet.ServletRequestSessionMap;
import org.jboss.seam.web.ServletContexts;
/**
* A SOAP request handler for controling Seam's lifecycle and managing
* conversation propagation.
*
* @author Shane Bryzak
*/
public class SOAPRequestHandler implements SOAPHandler
{
/**
* The QName of the conversation ID element in the SOAP request header
*/
public static final QName CIDQN = new QName("http://www.jboss.org/seam/webservice", "conversationId", "seam");
private static final LogProvider log = Logging.getLogProvider(SOAPRequestHandler.class);
private Set<QName> headers = new HashSet<QName>();
private String handlerName;
/**
* Handle inbound and outbound messages
*
* @param msgContext The message context
* @return boolean true if processing should continue
*/
public boolean handleMessage(MessageContext msgContext)
{
Boolean outbound = (Boolean)msgContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound == null)
throw new IllegalStateException("Cannot obtain required property: " + MessageContext.MESSAGE_OUTBOUND_PROPERTY);
return outbound ? handleOutbound(msgContext) : handleInbound(msgContext);
}
/**
* Inbound message handler. Seam contexts should be initialized here, and
* the conversation ID (if present) is extracted from the request.
*
* @param messageContext The message context
* @return boolean true if processing should continue
*/
public boolean handleInbound(MessageContext messageContext)
{
try
{
HttpServletRequest request = (HttpServletRequest) messageContext.get(MessageContext.SERVLET_REQUEST);
ServletLifecycle.beginRequest(request);
ServletContexts.instance().setRequest(request);
String conversationId = extractConversationId(messageContext);
ConversationPropagation.instance().setConversationId( conversationId );
Manager.instance().restoreConversation();
ServletLifecycle.resumeConversation(request);
return true;
}
catch (SOAPException ex)
{
log.error("Error handling inbound SOAP request", ex);
return false;
}
}
/**
* Sets the conversation ID in the outbound SOAP message.
*
* @param messageContext The message context
* @return boolean true if processing should continue
*/
public boolean handleOutbound(MessageContext messageContext)
{
try
{
HttpServletRequest request = (HttpServletRequest) messageContext.get(MessageContext.SERVLET_REQUEST);
String conversationId = Manager.instance().getCurrentConversationId();
if (conversationId != null)
{
SOAPMessageContext smc = (SOAPMessageContext) messageContext;
SOAPHeader header = smc.getMessage().getSOAPHeader();
if (header != null)
{
SOAPElement element = header.addChildElement(CIDQN);
element.addTextNode(conversationId);
smc.getMessage().saveChanges();
}
}
Manager.instance().endRequest( new ServletRequestSessionMap(request) );
return true;
}
catch (SOAPException ex)
{
log.error("Exception processing outbound message", ex);
return false;
}
}
/**
* Extracts the conversation ID from an incoming SOAP message
*
* @param messageContext
* @return The conversation ID, or null if there is no conversation ID set
* @throws SOAPException
*/
private String extractConversationId(MessageContext messageContext)
throws SOAPException
{
SOAPMessageContext smc = (SOAPMessageContext) messageContext;
SOAPHeader header = smc.getMessage().getSOAPHeader();
if (header != null)
{
Iterator iter = header.getChildElements(CIDQN);
if (iter.hasNext())
{
SOAPElement element = (SOAPElement) iter.next();
return element.getFirstChild().getNodeValue();
}
}
return null;
}
/**
* Called just prior to dispatching a message, fault or exception. The
* Seam request lifecycle is ended here
*/
public void close(MessageContext messageContext)
{
Lifecycle.endRequest();
}
public Set<QName> getHeaders()
{
return headers;
}
public void setHeaders(Set<QName> headers)
{
this.headers = headers;
}
public String getHandlerName()
{
return handlerName;
}
public void setHandlerName(String handlerName)
{
this.handlerName = handlerName;
}
public boolean handleFault(MessageContext messagecontext)
{
return true;
}
@Override
public String toString()
{
return (handlerName != null ? handlerName : super.toString());
}
}