/*
* JBoss, Home of Professional Open Source
* Copyright 2007, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/
package com.jboss.transaction.txinterop.webservices.handlers;
import com.arjuna.webservices11.wscoor.CoordinationConstants;
import com.jboss.transaction.txinterop.webservices.CoordinationContextManager;
import javax.xml.namespace.QName;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.ProtocolException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import java.util.Set;
import java.util.Iterator;
import java.util.Collections;
import java.util.Map;
import org.oasis_open.docs.ws_tx.wscoor._2006._06.CoordinationContextType;
import org.w3c.dom.Node;
/**
* Handler to serialise and deserialise a coordination context to/from a SOAP header.
*/
public class CoordinationContextHandler implements SOAPHandler<SOAPMessageContext> {
/**
* Gets the header blocks that can be processed by this Handler
* instance.
*
* @return Set of QNames of header blocks processed by this
* handler instance. <code>QName</code> is the qualified
* name of the outermost element of the Header block.
*/
public Set<QName> getHeaders()
{
return headers;
}
/**
* Handle an outgoing message by inserting any current arjuna context attached to the context into the message
* headers and handle an incoming message by retrieving the context from the headers and attaching it to the
* context,
*
* @param context the message context.
* @return Always return true
* @throws RuntimeException Causes the JAX-WS runtime to cease
* handler processing and generate a fault.
* @throws javax.xml.ws.ProtocolException Causes the JAX-WS runtime to switch to
* fault message processing.
*/
public boolean handleMessage(SOAPMessageContext context) throws ProtocolException
{
final boolean outbound = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
return handleMessageOutbound(context);
} else {
return handlemessageInbound(context);
}
}
/**
* check for an arjuna context attached to the message context and, if found, install its identifier as the value
* of a soap message header element
* @param context
* @return
* @throws ProtocolException
*/
protected boolean handleMessageOutbound(SOAPMessageContext context) throws ProtocolException
{
try {
CoordinationContextType coordinationContext = CoordinationContextManager.getThreadContext();
if (coordinationContext != null) {
final JAXBContext jaxbCtx = getJaxbContext();
// insert a header into the current message containing the coordination context
final SOAPMessage soapMessage = context.getMessage();
final SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope();
SOAPHeader soapHeader = soapEnvelope.getHeader() ;
if (soapHeader == null)
{
soapHeader = soapEnvelope.addHeader() ;
}
/*
* this does not work but it is what we want!!
*
* The problem here is that the marshaller creates plain old elements and inserts them top
* down as it goes along. but the soap header add child method checks its argument and
* replaces plain elements with soap header elements before inserting them. it copies the
* inserted element substructure into the rpelacement but since it does not exist at
* copy time the chiuldren get lost
Marshaller marshaller = jaxbCtx.createMarshaller();
marshaller.marshal(coordinationContext, soapHeader);
*/
/*
* ok, here's the workaround -- marshall the object as a child of a dummy header, detach it and
* then insert it as a header element.
*/
SOAPHeaderElement headerElement = soapHeader.addHeaderElement(getDummyQName());
Marshaller marshaller = jaxbCtx.createMarshaller();
marshaller.marshal(coordinationContext, headerElement);
soapHeader.replaceChild(headerElement.getChildNodes().item(0), headerElement);
// ok, now we need to locate the inserted node and set the mustunderstand attribute
Iterator<SOAPHeaderElement> iterator = soapHeader.examineAllHeaderElements();
while (iterator.hasNext()) {
headerElement = iterator.next();
if (CoordinationConstants.WSCOOR_ELEMENT_COORDINATION_CONTEXT_QNAME.equals(headerElement.getElementQName())) {
headerElement.setMustUnderstand(true);
break;
}
}
}
} catch (Exception se) {
throw new ProtocolException(se);
}
return true;
}
/**
* check for an arjuna instance identifier element embedded in the soap message headesr and, if found, use it to
* label an arjuna context attached to the message context
* @param context
* @return
* @throws ProtocolException
*/
private boolean handlemessageInbound(SOAPMessageContext context) throws ProtocolException
{
try {
final SOAPMessage soapMessage = context.getMessage();
final SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope();
Iterator<SOAPHeaderElement> iterator = soapEnvelope.getHeader().examineAllHeaderElements();
while (iterator.hasNext()) {
final SOAPHeaderElement headerElement = iterator.next();
if (CoordinationConstants.WSCOOR_ELEMENT_COORDINATION_CONTEXT_QNAME.equals(headerElement.getElementQName())) {
// found it - clear the must understand flag, retrieve the value and store an arjuna
// context in the message context
headerElement.setMustUnderstand(false);
final JAXBContext jaxbCtx = getJaxbContext();
final JAXBElement<CoordinationContextType> elt = jaxbCtx.createUnmarshaller().unmarshal(headerElement, CoordinationContextType.class);
final CoordinationContextType coordinationContext = elt.getValue();
CoordinationContextManager.setContext(context, coordinationContext);
}
}
} catch (Exception se) {
throw new ProtocolException(se);
}
return true;
}
/**
* this handler ignores faults but allows other handlers to deal with them
*
* @param context the message context
* @return true to allow fault handling to continue
*/
public boolean handleFault(SOAPMessageContext context)
{
return true;
}
/**
* this hanlder ignores close messages
*
* @param context the message context
*/
public void close(javax.xml.ws.handler.MessageContext context)
{
}
/**
* a singleton set containing the only header this handler is interested in
*/
private static Set<QName> headers = Collections.singleton(CoordinationConstants.WSCOOR_ELEMENT_COORDINATION_CONTEXT_QNAME);
private static JAXBContext jaxbContext;
private synchronized JAXBContext getJaxbContext()
{
if (jaxbContext == null) {
try {
jaxbContext = JAXBContext.newInstance("org.oasis_open.docs.ws_tx.wscoor._2006._06");
} catch (JAXBException e) {
// TODO log error here
}
}
return jaxbContext;
}
private static QName dummyQName = null;
private synchronized QName getDummyQName()
{
if (dummyQName == null) {
dummyQName = new QName("http://transactions.jboss.com/xts/dummy/", "DummyElement", "dummy");
}
return dummyQName;
}
}