[jbossws-issues] [JBoss JIRA] Updated: (JBWS-2026) jax-ws and saaj attachments

Thomas Diesler (JIRA) jira-events at lists.jboss.org
Wed Apr 9 11:01:18 EDT 2008


     [ http://jira.jboss.com/jira/browse/JBWS-2026?page=all ]

Thomas Diesler updated JBWS-2026:
---------------------------------

    Description: 
Dear jbossws developers.
I'm having an issue dealing with a webservice provided from a third party.
The problem I encountered is as follows:

   1: the service returns as output some data in the response
      and an optional attachment sent with saaj.
   2: the attachment comes along and is stored
      in a temporary directory by jboss.
   3: jbossws fails to identify the attachment and does not bind it
      to the response of the service.
   4: jboss throws the unused attachment away.

I've tracked the issue on the class CommonSoapBinding


in class org.jboss.ws.core.

This issue is related to the inability of the mentioned class
to deal with attachment by id, but only by part name.

The fact is the service provider sends attachment
without sending the content id complete with the part name

let me explain:

The http headers sent with the response message do not contain
a header [Content-Id="MyAttachment=<blablabla...>"]
but      [Content-Id="<blablabla...>"]

Hence, method "getAttachmentFromMessage" fails to find the attachment.

As a solution, I made some modification to that method in order
it to deal with the requirement to handle attachments by id.

I also modified the behaviour of the method not to throw an exception
when it does not find the attachment it expects to find, because otherwise
you might loose the chance to get fault text messages embedded in the service
response, as this exception breaks the process of rebuilding the java
representation of the soap message.

This is reflected also in method "unbindResponseMessage".

I'm attaching the modified java source
so that you can have evidence of the changes
(which have a comment "\\ m." on the right side of the line)

Many thanks for your time



  was:
Dear jbossws developers.
I'm having an issue dealing with a webservice provided from a third party.
The problem I encountered is as follows:

   1: the service returns as output some data in the response
      and an optional attachment sent with saaj.
   2: the attachment comes along and is stored
      in a temporary directory by jboss.
   3: jbossws fails to identify the attachment and does not bind it
      to the response of the service.
   4: jboss throws the unused attachment away.

I've tracked the issue on the class CommonSoapBinding


in class org.jboss.ws.core.

This issue is related to the inability of the mentioned class
to deal with attachment by id, but only by part name.

The fact is the service provider sends attachment
without sending the content id complete with the part name

let me explain:

The http headers sent with the response message do not contain
a header [Content-Id="MyAttachment=<blablabla...>"]
but      [Content-Id="<blablabla...>"]

Hence, method "getAttachmentFromMessage" fails to find the attachment.

As a solution, I made some modification to that method in order
it to deal with the requirement to handle attachments by id.

I also modified the behaviour of the method not to throw an exception
when it does not find the attachment it expects to find, because otherwise
you might loose the chance to get fault text messages embedded in the service
response, as this exception breaks the process of rebuilding the java
representation of the soap message.

This is reflected also in method "unbindResponseMessage".

I'm attaching the modified java source
so that you can have evidence of the changes
(which have a comment "\\ m." on the right side of the line)

Many thanks for your time

Here is the modified source.

/*
 * 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;

// $Id: CommonSOAPBinding.java 5322 2007-12-14 17:58:07Z richard.opalka at jboss.com $

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.activation.DataHandler;
import javax.xml.namespace.QName;
import javax.xml.rpc.ParameterMode;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.Name;
import javax.xml.soap.Node;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;

import org.apache.xerces.xs.XSElementDeclaration;
import org.apache.xerces.xs.XSTypeDefinition;
import org.jboss.logging.Logger;
import org.jboss.ws.Constants;
import org.jboss.ws.WSException;
import org.jboss.ws.core.binding.BindingException;
import org.jboss.ws.core.jaxrpc.ParameterWrapping;
import org.jboss.ws.core.jaxws.handler.MessageContextJAXWS;
import org.jboss.ws.core.soap.MessageContextAssociation;
import org.jboss.ws.core.soap.MessageFactoryImpl;
import org.jboss.ws.core.soap.NameImpl;
import org.jboss.ws.core.soap.SOAPBodyElementDoc;
import org.jboss.ws.core.soap.SOAPBodyElementRpc;
import org.jboss.ws.core.soap.SOAPBodyImpl;
import org.jboss.ws.core.soap.SOAPContentElement;
import org.jboss.ws.core.soap.SOAPElementImpl;
import org.jboss.ws.core.soap.SOAPFactoryImpl;
import org.jboss.ws.core.soap.SOAPFaultImpl;
import org.jboss.ws.core.soap.SOAPHeaderElementImpl;
import org.jboss.ws.core.soap.SOAPMessageImpl;
import org.jboss.ws.core.soap.Style;
import org.jboss.ws.core.soap.UnboundHeader;
import org.jboss.ws.core.soap.Use;
import org.jboss.ws.core.soap.attachment.AttachmentPartImpl;
import org.jboss.ws.core.soap.attachment.CIDGenerator;
import org.jboss.ws.core.utils.MimeUtils;
import org.jboss.ws.extensions.wsrm.RMConstant;
import org.jboss.ws.extensions.wsrm.common.RMHelper;
import org.jboss.ws.extensions.xop.XOPContext;
import org.jboss.ws.metadata.umdm.OperationMetaData;
import org.jboss.ws.metadata.umdm.ParameterMetaData;
import org.jboss.ws.metadata.umdm.TypesMetaData;
import org.jboss.wsf.common.DOMUtils;
import org.jboss.wsf.common.JavaUtils;
import org.jboss.xb.binding.NamespaceRegistry;
import org.w3c.dom.Element;

/**
 * The SOAPBinding interface is an abstraction for the SOAP binding.
 *
 * @author Thomas.Diesler at jboss.com
 * @since 04-Jul-2006
 */
public abstract class CommonSOAPBinding implements CommonBinding
{
   // provide logging
   protected Logger log = Logger.getLogger(getClass());

   private boolean mtomEnabled;

   protected HeaderSource headerSource;

   /** A constant representing the identity of the SOAP 1.1 over HTTP binding. */
   public static final String SOAP11HTTP_BINDING = "http://schemas.xmlsoap.org/wsdl/soap/http";
   /** A constant representing the identity of the SOAP 1.2 over HTTP binding. */
   public static final String SOAP12HTTP_BINDING = "http://www.w3.org/2003/05/soap/bindings/HTTP/";
   /** A constant representing the identity of the SOAP 1.1 over HTTP binding with MTOM enabled by default. */
   public static final String SOAP11HTTP_MTOM_BINDING = "http://schemas.xmlsoap.org/wsdl/soap/http?mtom=true";
   /** A constant representing the identity of the SOAP 1.2 over HTTP binding with MTOM enabled by default. */
   public static final String SOAP12HTTP_MTOM_BINDING = "http://www.w3.org/2003/05/soap/bindings/HTTP/?mtom=true";
   /** The SOAP encoded Array name */
   private static final Name SOAP_ARRAY_NAME = new NameImpl("Array", Constants.PREFIX_SOAP11_ENC, Constants.URI_SOAP11_ENC);

   public CommonSOAPBinding()
   {
   }

   public MessageFactory getMessageFactory()
   {
      return new MessageFactoryImpl();
   }

   public SOAPFactory getSOAPFactory()
   {
      return new SOAPFactoryImpl();
   }

   public boolean isMTOMEnabled()
   {
      return this.mtomEnabled;
   }

   public void setMTOMEnabled(boolean flag)
   {
      this.mtomEnabled = flag;
   }

   /** Create the message */
   protected abstract MessageAbstraction createMessage(OperationMetaData opMetaData) throws SOAPException;

   /** On the client side, generate the payload from IN parameters. */
   public MessageAbstraction bindRequestMessage(OperationMetaData opMetaData, EndpointInvocation epInv, Map<QName, UnboundHeader> unboundHeaders)
         throws BindingException
   {
      if (log.isDebugEnabled())
         log.debug("bindRequestMessage: " + opMetaData.getQName());

      try
      {
         CommonMessageContext msgContext = MessageContextAssociation.peekMessageContext();
         if (msgContext == null)
            throw new WSException("MessageContext not available");

         // Disable MTOM for rpc/encoded
         if (opMetaData.isRPCEncoded())
            XOPContext.setMTOMEnabled(false);
         else XOPContext.setMTOMEnabled(isMTOMEnabled());

         // Associate current message with message context
         SOAPMessageImpl reqMessage = (SOAPMessageImpl)createMessage(opMetaData);
         msgContext.setSOAPMessage(reqMessage);

         SOAPEnvelope soapEnvelope = reqMessage.getSOAPPart().getEnvelope();
         SOAPBody soapBody = soapEnvelope.getBody();
         SOAPHeader soapHeader = soapEnvelope.getHeader();

         // Get the namespace registry
         NamespaceRegistry namespaceRegistry = msgContext.getNamespaceRegistry();

         Style style = opMetaData.getStyle();
         SOAPElement soapBodyElement = soapBody;
         if (style == Style.RPC)
         {
            boolean serialize = true;
            
            if (opMetaData.getEndpointMetaData().getConfig().getRMMetaData() != null)
            {
               // RM hack to JAX-RPC serialization
               if (RMHelper.isRMOperation(opMetaData.getQName()))
               {
                  serialize = false;
               }
            }
            
            if (serialize)
            {
               QName opQName = opMetaData.getQName();
               Name opName = new NameImpl(namespaceRegistry.registerQName(opQName));

               if (log.isDebugEnabled())
                  log.debug("Create RPC body element: " + opName);

               soapBodyElement = new SOAPBodyElementRpc(opName);
               soapBodyElement = (SOAPBodyElement)soapBody.addChildElement(soapBodyElement);

               // Add soap encodingStyle
               if (opMetaData.getUse() == Use.ENCODED)
               {
                  String envURI = soapEnvelope.getNamespaceURI();
                  String envPrefix = soapEnvelope.getPrefix();
                  soapBodyElement.setAttributeNS(envURI, envPrefix + ":encodingStyle", Constants.URI_SOAP11_ENC);
               }
            }
         }

         for (ParameterMetaData paramMetaData : opMetaData.getInputParameters())
         {
            QName xmlName = paramMetaData.getXmlName();
            Object value = epInv.getRequestParamValue(xmlName);

            if (paramMetaData.isSwA())
            {
               // NOTE: swa:ref is handled by the AttachmentMarshaller callback
               CIDGenerator cidGenerator = reqMessage.getCidGenerator();
               AttachmentPart part = createAttachmentPart(paramMetaData, value, cidGenerator);
               reqMessage.addAttachmentPart(part);

               // Add the attachment to the standard property
               if (value instanceof DataHandler && msgContext instanceof MessageContextJAXWS)
               {
                  DataHandler dataHandler = (DataHandler)value;
                  Map<String, DataHandler> attachments = (Map<String, DataHandler>)msgContext.get(MessageContext.OUTBOUND_MESSAGE_ATTACHMENTS);
                  attachments.put(dataHandler.getContentType(), dataHandler);
               }
            }
            else
            {
               SOAPElement soapElement = paramMetaData.isInHeader() ? (SOAPElement)soapHeader : soapBodyElement;
               addParameterToMessage(paramMetaData, value, soapElement);
            }
         }

         // Add unbound headers
         if (unboundHeaders != null)
         {
            Iterator it = unboundHeaders.values().iterator();
            while (it.hasNext())
            {
               UnboundHeader unboundHeader = (UnboundHeader)it.next();
               if (unboundHeader.getMode() != ParameterMode.OUT)
               {
                  QName xmlName = unboundHeader.getXmlName();
                  Object value = unboundHeader.getHeaderValue();

                  xmlName = namespaceRegistry.registerQName(xmlName);
                  Name soapName = new NameImpl(xmlName.getLocalPart(), xmlName.getPrefix(), xmlName.getNamespaceURI());

                  log.debug("Add unboundHeader element: " + soapName);
                  SOAPContentElement contentElement = new SOAPHeaderElementImpl(soapName);
                  contentElement.setParamMetaData(unboundHeader.toParameterMetaData(opMetaData));
                  
                  if (soapHeader == null)
                     soapHeader = soapEnvelope.addHeader();
                  
                  soapHeader.addChildElement(contentElement);
                  contentElement.setObjectValue(value);
               }
            }
         }

         // Set the SOAPAction
         setSOAPActionHeader(opMetaData, reqMessage);

         return reqMessage;
      }
      catch (Exception e)
      {
         handleException(e);
         return null;
      }
   }

   /** Override to set the SOAPAction header */
   public abstract void setSOAPActionHeader(OperationMetaData opMetaData, SOAPMessage reqMessage);

   /** On the server side, extract the IN parameters from the payload and populate an Invocation object */
   public EndpointInvocation unbindRequestMessage(OperationMetaData opMetaData, MessageAbstraction payload) throws BindingException
   {
      if (log.isDebugEnabled())
         log.debug("unbindRequestMessage: " + opMetaData.getQName());

      try
      {
         // Read the SOAPEnvelope from the reqMessage
         SOAPMessageImpl reqMessage = (SOAPMessageImpl)payload;
         SOAPEnvelope soapEnvelope = reqMessage.getSOAPPart().getEnvelope();
         SOAPHeader soapHeader = soapEnvelope.getHeader();
         SOAPBody soapBody = soapEnvelope.getBody();

         // Verify the SOAP version
         verifySOAPVersion(opMetaData, soapEnvelope);

         // Construct the endpoint invocation object
         EndpointInvocation epInv = new EndpointInvocation(opMetaData);

         CommonMessageContext msgContext = MessageContextAssociation.peekMessageContext();
         if (msgContext == null)
            throw new WSException("MessageContext not available");

         // Disable MTOM for rpc/encoded
         if (opMetaData.isRPCEncoded())
            msgContext.put(StubExt.PROPERTY_MTOM_ENABLED, Boolean.FALSE);

         // Get the namespace registry
         NamespaceRegistry namespaceRegistry = msgContext.getNamespaceRegistry();

         if (opMetaData.isMessageEndpoint() == false)
         {
            Style style = opMetaData.getStyle();
            SOAPElement payloadParent = soapBody;
            if (style == Style.RPC)
            {
               payloadParent = null;
               Iterator it = soapBody.getChildElements();
               while (payloadParent == null && it.hasNext())
               {
                  Object childNode = it.next();
                  if (childNode instanceof SOAPElement)
                  {
                     payloadParent = (SOAPElement)childNode;
                  }
               }
               
               if (RMHelper.isRMOperation(opMetaData.getQName()) == false) // RM hack
               {
                  if (payloadParent == null)
                     throw new SOAPException("Cannot find RPC element in");

                  QName elName = payloadParent.getElementQName();
                  elName = namespaceRegistry.registerQName(elName);
               }
            }

            int numParameters = 0;
            for (ParameterMetaData paramMetaData : opMetaData.getParameters())
            {
               QName xmlName = paramMetaData.getXmlName();
               if (paramMetaData.getMode() == ParameterMode.OUT)
               {
                  epInv.setRequestParamValue(xmlName, null);
               }
               else
               {
                  if (paramMetaData.isSwA())
                  {
                     AttachmentPart part = getAttachmentFromMessage(paramMetaData, reqMessage);
                     epInv.setRequestParamValue(xmlName, part);

                     // Add the attachment to the standard property
                     if (part.getDataHandler() != null && msgContext instanceof MessageContextJAXWS)
                     {
                        DataHandler dataHandler = part.getDataHandler();
                        Map<String, DataHandler> attachments = (Map<String, DataHandler>)msgContext.get(MessageContext.INBOUND_MESSAGE_ATTACHMENTS);
                        attachments.put(part.getContentId(), dataHandler);
                     }
                  }
                  else
                  {
                     boolean isHeader = paramMetaData.isInHeader();
                     SOAPElement element = isHeader ? soapHeader : payloadParent;
                     if (!isHeader)
                        numParameters++;

                     SOAPContentElement value = getParameterFromMessage(paramMetaData, element, false);
                     epInv.setRequestParamValue(xmlName, value);
                  }
               }
            }

            if (RMHelper.isRMOperation(opMetaData.getQName()) == false)
            {
               // Verify the numer of parameters matches the actual message payload
               int numChildElements = 0;
               Iterator itElements = payloadParent.getChildElements();
               while (itElements.hasNext())
               {
                  Node node = (Node)itElements.next();
                  if (node instanceof SOAPElement)
                     numChildElements++;
               }
               if (numChildElements != numParameters)
                  throw new WSException("Invalid number of payload elements: " + numChildElements);
            }
         }

         // Generic message endpoint
         else
         {
            for (ParameterMetaData paramMetaData : opMetaData.getParameters())
            {
               QName xmlName = paramMetaData.getXmlName();
               Object value = soapBody.getChildElements().next();
               epInv.setRequestParamValue(xmlName, value);
            }
         }

         return epInv;
      }
      catch (Exception e)
      {
         handleException(e);
         return null;
      }
   }

   /** On the server side, generate the payload from OUT parameters. */
   public MessageAbstraction bindResponseMessage(OperationMetaData opMetaData, EndpointInvocation epInv) throws BindingException
   {
      if (log.isDebugEnabled())
         log.debug("bindResponseMessage: " + opMetaData.getQName());

      try
      {
         CommonMessageContext msgContext = MessageContextAssociation.peekMessageContext();
         if (msgContext == null)
            throw new WSException("MessageContext not available");

         // Disable MTOM for rpc/encoded
         if (opMetaData.isRPCEncoded())
            XOPContext.setMTOMEnabled(false);
         else XOPContext.setMTOMEnabled(isMTOMEnabled());

         // Associate current message with message context
         SOAPMessageImpl resMessage = (SOAPMessageImpl)createMessage(opMetaData);
         msgContext.setSOAPMessage(resMessage);

         // R2714 For one-way operations, an INSTANCE MUST NOT return a HTTP response that contains a SOAP envelope.
         // Specifically, the HTTP response entity-body must be empty.
         boolean isWsrmMessage = msgContext.get(RMConstant.RESPONSE_CONTEXT) != null;
         if (opMetaData.isOneWay() && (false == isWsrmMessage))
         {
            resMessage.getSOAPPart().setContent(null);
            return resMessage;
         }

         SOAPEnvelope soapEnvelope = resMessage.getSOAPPart().getEnvelope();
         SOAPHeader soapHeader = soapEnvelope.getHeader();
         SOAPBody soapBody = soapEnvelope.getBody();

         // Get the namespace registry
         NamespaceRegistry namespaceRegistry = msgContext.getNamespaceRegistry();

         Style style = opMetaData.getStyle();
         SOAPElement soapBodyElement = soapBody;
         if (style == Style.RPC)
         {
            QName opQName = opMetaData.getResponseName();

            if (false == RMHelper.isRMOperation(opQName)) // RM hack
            {
               Name opName = new NameImpl(namespaceRegistry.registerQName(opQName));
               soapBodyElement = new SOAPBodyElementRpc(opName);
               soapBodyElement = (SOAPBodyElement)soapBody.addChildElement(soapBodyElement);

               // Add soap encodingStyle
               if (opMetaData.getUse() == Use.ENCODED)
               {
                  String envURI = soapEnvelope.getNamespaceURI();
                  String envPrefix = soapEnvelope.getPrefix();
                  soapBodyElement.setAttributeNS(envURI, envPrefix + ":encodingStyle", Constants.URI_SOAP11_ENC);
               }
            }
         }

         // Add the return to the message
         ParameterMetaData retMetaData = opMetaData.getReturnParameter();
         if (retMetaData != null)
         {
            Object value = epInv.getReturnValue();

            // TODO calls to ParameterWrapping should be elsewhere
            if (opMetaData.isDocumentWrapped())
               value = ParameterWrapping.wrapResponseParameters(retMetaData, value, epInv.getOutParameters());

            if (retMetaData.isSwA())
            {
               CIDGenerator cidGenerator = resMessage.getCidGenerator();
               AttachmentPart part = createAttachmentPart(retMetaData, value, cidGenerator);
               resMessage.addAttachmentPart(part);
               epInv.setReturnValue(part);

               // Add the attachment to the standard property
               if (part.getDataHandler() != null && msgContext instanceof MessageContextJAXWS)
               {
                  DataHandler dataHandler = part.getDataHandler();
                  Map<String, DataHandler> attachments = (Map<String, DataHandler>)msgContext.get(MessageContext.OUTBOUND_MESSAGE_ATTACHMENTS);
                  attachments.put(part.getContentId(), dataHandler);
               }
            }
            else
            {
               SOAPContentElement soapElement = addParameterToMessage(retMetaData, value, soapBodyElement);
               epInv.setReturnValue(soapElement);
               soapElement.setObjectValue(value);
            }
         }

         // Add the out parameters to the message
         for (ParameterMetaData paramMetaData : opMetaData.getOutputParameters())
         {
            QName xmlName = paramMetaData.getXmlName();
            Object value = epInv.getResponseParamValue(xmlName);
            if (paramMetaData.isSwA())
            {
               CIDGenerator cidGenerator = resMessage.getCidGenerator();
               AttachmentPart part = createAttachmentPart(paramMetaData, value, cidGenerator);
               resMessage.addAttachmentPart(part);

               // Add the attachment to the standard property
               if (value instanceof DataHandler && msgContext instanceof MessageContextJAXWS)
               {
                  DataHandler dataHandler = (DataHandler)value;
                  Map<String, DataHandler> attachments = (Map<String, DataHandler>)msgContext.get(MessageContext.OUTBOUND_MESSAGE_ATTACHMENTS);
                  attachments.put(dataHandler.getContentType(), dataHandler);
               }
            }
            else
            {
               if (paramMetaData.isInHeader())
               {
                  addParameterToMessage(paramMetaData, value, soapHeader);
               }
               else
               {
                  addParameterToMessage(paramMetaData, value, soapBodyElement);
               }
            }
         }

         return resMessage;
      }
      catch (Exception e)
      {
         handleException(e);
         return null;
      }
   }

   /** On the client side, extract the OUT parameters from the payload and return them to the client. */
   public void unbindResponseMessage(OperationMetaData opMetaData, MessageAbstraction payload, EndpointInvocation epInv, Map<QName, UnboundHeader> unboundHeaders)
         throws BindingException
   {
      if (log.isDebugEnabled())
         log.debug("unbindResponseMessage: " + opMetaData.getQName());

      try
      {
         // R2714 For one-way operations, an INSTANCE MUST NOT return a HTTP response that contains a SOAP envelope.
         // Specifically, the HTTP response entity-body must be empty.
         if (opMetaData.isOneWay() == true)
         {
            return;
         }

         // WS-Addressing might redirect the response, which results in an empty envelope
         SOAPMessageImpl resMessage = (SOAPMessageImpl)payload;
         SOAPEnvelope soapEnvelope = resMessage.getSOAPPart().getEnvelope();
         if (soapEnvelope == null)
         {
            return;
         }

         // Verify the SOAP version
         verifySOAPVersion(opMetaData, soapEnvelope);

         // Get the SOAP message context that is associated with the current thread
         CommonMessageContext msgContext = MessageContextAssociation.peekMessageContext();
         if (msgContext == null)
            throw new WSException("MessageContext not available");

         // Disable MTOM for rpc/encoded
         if (opMetaData.isRPCEncoded())
            msgContext.put(StubExt.PROPERTY_MTOM_ENABLED, Boolean.FALSE);

         SOAPHeader soapHeader = soapEnvelope.getHeader();
         SOAPBodyImpl soapBody = (SOAPBodyImpl)soapEnvelope.getBody();
         SOAPBodyElement soapBodyElement = soapBody.getBodyElement();

         // Translate the SOAPFault to an exception and throw it
         if (soapBodyElement instanceof SOAPFaultImpl)
            throwFaultException((SOAPFaultImpl)soapBodyElement);

         // Extract unbound OUT headers
         if (unboundHeaders != null && soapHeader != null)
         {
            Map<QName, UnboundHeader> outHeaders = new HashMap<QName, UnboundHeader>();
            Iterator itHeaderElements = soapHeader.getChildElements();
            while (itHeaderElements.hasNext())
            {
               SOAPContentElement soapHeaderElement = (SOAPHeaderElementImpl)itHeaderElements.next();
               Name elName = soapHeaderElement.getElementName();
               QName xmlName = new QName(elName.getURI(), elName.getLocalName());

               UnboundHeader unboundHeader = (UnboundHeader)unboundHeaders.get(xmlName);
               if (unboundHeader != null)
               {
                  soapHeaderElement.setParamMetaData(unboundHeader.toParameterMetaData(opMetaData));

                  // Do the unmarshalling
                  Object value = soapHeaderElement.getObjectValue();
                  unboundHeader.setHeaderValue(value);
                  outHeaders.put(xmlName, unboundHeader);
               }
            }
            unboundHeaders.clear();
            unboundHeaders.putAll(outHeaders);
         }

         Style style = opMetaData.getStyle();
         SOAPElement soapElement = soapBody;
         if (style == Style.RPC)
         {
            if (soapBodyElement == null)
               throw new WSException("Cannot unbind response message with empty soap body");
            soapElement = soapBodyElement;
         }

         ParameterMetaData retMetaData = opMetaData.getReturnParameter();
         if (retMetaData != null)
         {
            if (retMetaData.isSwA())
            {
               AttachmentPart part = getAttachmentFromMessage(retMetaData, resMessage);
               epInv.setReturnValue(part);

               // Add the attachment to the standard property
               if (part.getDataHandler() != null && msgContext instanceof MessageContextJAXWS)
               {
                  DataHandler dataHandler = part.getDataHandler();
                  Map<String, DataHandler> attachments = (Map<String, DataHandler>)msgContext.get(MessageContext.INBOUND_MESSAGE_ATTACHMENTS);
                  attachments.put(part.getContentId(), dataHandler);
               }
            }
            else
            {
               SOAPContentElement value = getParameterFromMessage(retMetaData, soapElement, false);
               epInv.setReturnValue(value);
            }
         }

         for (ParameterMetaData paramMetaData : opMetaData.getOutputParameters())
         {
            QName xmlName = paramMetaData.getXmlName();
            if (paramMetaData.isSwA())
            {
               AttachmentPart part = getAttachmentFromMessage(paramMetaData, resMessage);
               epInv.setResponseParamValue(xmlName, part);

               if (part != null) { // m.
                   // Add the attachment to the standard property
                   if (part.getDataHandler() != null && msgContext instanceof MessageContextJAXWS)
                   {
                      DataHandler dataHandler = part.getDataHandler();
                      Map<String, DataHandler> attachments = (Map<String, DataHandler>)msgContext.get(MessageContext.INBOUND_MESSAGE_ATTACHMENTS);
                      attachments.put(part.getContentId(), dataHandler);
                   }
               } // m.
            }
            else
            {
               SOAPElement element = paramMetaData.isInHeader() ? soapHeader : soapElement;
               SOAPContentElement value = getParameterFromMessage(paramMetaData, element, false);
               epInv.setResponseParamValue(xmlName, value);
            }
         }
      }
      catch (Exception e)
      {
         handleException(e);
      }
   }

   public MessageAbstraction bindFaultMessage(Exception ex)
   {
      SOAPMessageImpl faultMessage = (SOAPMessageImpl)createFaultMessageFromException(ex);
      CommonMessageContext msgContext = MessageContextAssociation.peekMessageContext();
      if (msgContext != null)
      {
         msgContext.setSOAPMessage(faultMessage);
      }
      else
      {
         log.warn("Cannot set fault message in message context");
      }
      return faultMessage;
   }

   protected abstract SOAPMessage createFaultMessageFromException(Exception ex);

   private void verifySOAPVersion(OperationMetaData opMetaData, SOAPEnvelope soapEnvelope)
   {
      String envNS = soapEnvelope.getNamespaceURI();
      String bindingId = opMetaData.getEndpointMetaData().getBindingId();
      if (CommonSOAPBinding.SOAP11HTTP_BINDING.equals(bindingId) && Constants.NS_SOAP11_ENV.equals(envNS) == false)
         log.warn("Expected SOAP-1.1 envelope, but got: " + envNS);

      if (CommonSOAPBinding.SOAP12HTTP_BINDING.equals(bindingId) && Constants.NS_SOAP12_ENV.equals(envNS) == false)
         log.warn("Expected SOAP-1.2 envelope, but got: " + envNS);
   }

   private AttachmentPart createAttachmentPart(ParameterMetaData paramMetaData, Object value, CIDGenerator cidGenerator) throws SOAPException, BindingException
   {
      String partName = paramMetaData.getXmlName().getLocalPart();
      Set mimeTypes = paramMetaData.getMimeTypes();

      AttachmentPart part = new AttachmentPartImpl();
      if (value instanceof DataHandler)
      {
         DataHandler handler = (DataHandler)value;
         String mimeType = MimeUtils.getBaseMimeType(handler.getContentType());

         // JAX-WS 2.0, 2.6.3.1 MIME Content
         // Conformance (MIME type mismatch): On receipt of a message where the MIME type of a part does not
         // match that described in the WSDL an implementation SHOULD throw a WebServiceException.
         if (mimeTypes != null && !MimeUtils.isMemberOf(mimeType, mimeTypes))
            log.warn("Mime type " + mimeType + " not allowed for parameter " + partName + " allowed types are " + mimeTypes);

         part.setDataHandler((DataHandler)value);
      }
      else
      {
         String mimeType = null;
         if (mimeTypes != null && mimeTypes.size() > 0)
         {
            mimeType = (String)mimeTypes.iterator().next();
         }
         else
         {
            mimeType = MimeUtils.resolveMimeType(value);
         }

         if (mimeType == null)
            throw new BindingException("Could not determine mime type for attachment parameter: " + partName);

         part.setContent(value, mimeType);
      }

      if (paramMetaData.isSwA())
      {
         String swaCID = '<' + partName + "=" + cidGenerator.generateFromCount() + '>';
         part.setContentId(swaCID);
      }
      if (paramMetaData.isXOP())
      {
         String xopCID = '<' + cidGenerator.generateFromName(partName) + '>';
         part.setContentId(xopCID);
      }

      return part;
   }

   private AttachmentPart getAttachmentFromMessage(ParameterMetaData paramMetaData, SOAPMessage message) throws SOAPException, BindingException
   {
      QName xmlName = paramMetaData.getXmlName();

      AttachmentPart part = ((SOAPMessageImpl)message).getAttachmentByPartName(xmlName.getLocalPart());
      if (part == null) { // m.
          Iterator<SOAPElement> it = message.getSOAPBody().getChildElements(xmlName); // m.
          if (it.hasNext()) { // m.
              SOAPElement e = it.next(); // m.
              part = ((SOAPMessageImpl)message).getAttachment(e); // m.
          }
      }
// m.      if (part == null)
// m.        throw new BindingException("Could not locate attachment for parameter: " + paramMetaData.getXmlName());
      return part;
   }

   /** Marshall the given parameter and add it to the SOAPMessage */
   private SOAPContentElement addParameterToMessage(ParameterMetaData paramMetaData, Object value, SOAPElement soapElement) throws SOAPException, BindingException
   {
      QName xmlName = paramMetaData.getXmlName();
      Class javaType = paramMetaData.getJavaType();

      if (value != null && paramMetaData.isXOP() == false)
      {
         Class valueType = value.getClass();
         if (JavaUtils.isAssignableFrom(javaType, valueType) == false)
            throw new BindingException("javaType " + javaType.getName() + " is not assignable from: " + valueType.getName());
      }

      // Make sure we have a prefix on qualified names
      if (xmlName.getNamespaceURI().length() > 0)
      {
         CommonMessageContext msgContext = MessageContextAssociation.peekMessageContext();
         NamespaceRegistry namespaceRegistry = msgContext.getNamespaceRegistry();
         xmlName = namespaceRegistry.registerQName(xmlName);
      }

      Name soapName = new NameImpl(xmlName.getLocalPart(), xmlName.getPrefix(), xmlName.getNamespaceURI());

      SOAPContentElement contentElement;
      if (soapElement instanceof SOAPHeader)
      {
         contentElement = new SOAPHeaderElementImpl(soapName);
         soapElement.addChildElement(contentElement);
      }
      else
      {
         Style style = paramMetaData.getOperationMetaData().getStyle();
         if (style == Style.DOCUMENT)
         {
            contentElement = new SOAPBodyElementDoc(soapName);
            soapElement.addChildElement(contentElement);
         }
         else
         {
            contentElement = new SOAPContentElement(soapName);
            soapElement.addChildElement(contentElement);
         }
      }

      contentElement.setParamMetaData(paramMetaData);

      if (paramMetaData.isSOAPArrayParam())
      {
         log.trace("Add parameter as SOAP encoded Array");
         contentElement.addNamespaceDeclaration(Constants.PREFIX_SOAP11_ENC, Constants.URI_SOAP11_ENC);
      }

      // When a potential xop parameter is detected and MTOM is enabled
      // we flag the SOAP message as a XOP package
      if (paramMetaData.isXOP() && XOPContext.isMTOMEnabled())
      {
         log.trace("Add parameter as XOP");
         CommonMessageContext msgContext = MessageContextAssociation.peekMessageContext();
         SOAPMessageImpl soapMessage = (SOAPMessageImpl)msgContext.getSOAPMessage();
         soapMessage.setXOPMessage(true);
      }
      else if (paramMetaData.isSwaRef())
      {
         CommonMessageContext msgContext = MessageContextAssociation.peekMessageContext();
         SOAPMessageImpl soapMessage = (SOAPMessageImpl)msgContext.getSOAPMessage();
         soapMessage.setSWARefMessage(true);
      }

      contentElement.setObjectValue(value);

      return contentElement;
   }

   /** Unmarshall a message element and add it to the parameter list */
   private SOAPContentElement getParameterFromMessage(ParameterMetaData paramMetaData, SOAPElement soapElement, boolean optional) throws BindingException
   {
      Name xmlName = new NameImpl(paramMetaData.getXmlName());

      SOAPContentElement soapContentElement = null;
      Iterator childElements = soapElement.getChildElements();
      while (childElements.hasNext())
      {
         Object childNode = childElements.next();
         if (childNode instanceof SOAPElement)
         {
            SOAPElementImpl childElement = (SOAPElementImpl)childNode;
            // If this message was manipulated by a handler the child may not be a content element
            if (!(childElement instanceof SOAPContentElement))
               childElement = (SOAPContentElement)soapElement.replaceChild(new SOAPContentElement(childElement), childElement);

            // The parameters are expected to be lazy
            SOAPContentElement aux = (SOAPContentElement)childElement;
            Name elName = aux.getElementName();

            if (xmlName.equals(elName))
            {
               soapContentElement = aux;
               soapContentElement.setParamMetaData(paramMetaData);
               break;
            }

            if (SOAP_ARRAY_NAME.equals(elName))
            {
               CommonMessageContext msgContext = MessageContextAssociation.peekMessageContext();
               msgContext.put(CommonMessageContext.ALLOW_EXPAND_TO_DOM, Boolean.TRUE);
               try
               {
                  QName compXMLName = paramMetaData.getXmlName();
                  Element compElement = DOMUtils.getFirstChildElement(aux);
                  // NPE when the soap encoded array size is 0 on the return path
                  // http://jira.jboss.org/jira/browse/JBWS-1285
                  if (compElement == null || compElement.getNodeName().equals(compXMLName.getLocalPart()))
                  {
                     soapContentElement = aux;
                     soapContentElement.setParamMetaData(paramMetaData);
                     break;
                  }
               }
               finally
               {
                  msgContext.remove(CommonMessageContext.ALLOW_EXPAND_TO_DOM);
               }
            }
         }
      }

      // If matching by name fails, try to match by xmlType
      // This maybe necessary when wsa:Action dispatches to the operation
      if (soapContentElement == null)
      {
         childElements = soapElement.getChildElements();
         OperationMetaData opMetaData = paramMetaData.getOperationMetaData();
         TypesMetaData typesMetaData = opMetaData.getEndpointMetaData().getServiceMetaData().getTypesMetaData();
         if (childElements.hasNext() && opMetaData.getStyle() == Style.DOCUMENT)
         {
            SOAPElementImpl childElement = (SOAPElementImpl)childElements.next();

            // The parameters are expected to be lazy
            SOAPContentElement aux = (SOAPContentElement)childElement;
            Name elName = aux.getElementName();
            QName elType = null;

            XSElementDeclaration xsdElement = typesMetaData.getSchemaModel().getElementDeclaration(elName.getLocalName(), elName.getURI());
            if (xsdElement != null && xsdElement.getTypeDefinition() != null)
            {
               XSTypeDefinition xsdType = xsdElement.getTypeDefinition();
               elType = new QName(xsdType.getNamespace(), xsdType.getName());
            }

            if (paramMetaData.getXmlType().equals(elType))
            {
               soapContentElement = aux;
               soapContentElement.setParamMetaData(paramMetaData);
            }
         }
      }

      if (soapContentElement == null && optional == false)
         throw new WSException("Cannot find child element: " + xmlName);

      // When a potential XOP parameter is detected and
      // the incomming request is actuall XOP encoded we flag
      // the SOAP message a XOP packaged.
      if (paramMetaData.isXOP() && XOPContext.isXOPEncodedRequest())
      {
         SOAPMessageImpl soapMessage = (SOAPMessageImpl)MessageContextAssociation.peekMessageContext().getSOAPMessage();
         soapMessage.setXOPMessage(true);
      }
      else if (paramMetaData.isSwaRef())
      {
         SOAPMessageImpl soapMessage = (SOAPMessageImpl)MessageContextAssociation.peekMessageContext().getSOAPMessage();
         soapMessage.setSWARefMessage(true);
      }

      return soapContentElement;
   }

   abstract protected void throwFaultException(SOAPFaultImpl fault) throws Exception;

   abstract protected void verifyUnderstoodHeader(SOAPHeaderElement element) throws Exception;

   public void checkMustUnderstand(OperationMetaData opMetaData) throws Exception
   {
      CommonMessageContext msgContext = MessageContextAssociation.peekMessageContext();
      if (msgContext == null)
         throw new WSException("MessageContext not available");

      SOAPMessageImpl soapMessage = (SOAPMessageImpl)msgContext.getSOAPMessage();
      SOAPEnvelope soapEnvelope = soapMessage.getSOAPPart().getEnvelope();
      if (soapEnvelope == null || soapEnvelope.getHeader() == null)
         return;

      Iterator it = soapEnvelope.getHeader().examineAllHeaderElements();
      while (it.hasNext())
      {
         SOAPHeaderElement soapHeaderElement = (SOAPHeaderElement)it.next();
         Name name = soapHeaderElement.getElementName();
         QName xmlName = new QName(name.getURI(), name.getLocalName());

         ParameterMetaData paramMetaData = (opMetaData != null ? opMetaData.getParameter(xmlName) : null);
         boolean isBoundHeader = (paramMetaData != null && paramMetaData.isInHeader());

         if (!isBoundHeader && soapHeaderElement.getMustUnderstand())
            verifyUnderstoodHeader(soapHeaderElement);
      }
   }

   public void setHeaderSource(HeaderSource source)
   {
      headerSource = source;
   }

   private void handleException(Exception ex) throws BindingException
   {
      if (ex instanceof RuntimeException)
         throw (RuntimeException)ex;

      if (ex instanceof BindingException)
         throw (BindingException)ex;

      throw new BindingException(ex);
   }
}




> jax-ws and saaj attachments
> ---------------------------
>
>                 Key: JBWS-2026
>                 URL: http://jira.jboss.com/jira/browse/JBWS-2026
>             Project: JBoss Web Services
>          Issue Type: Bug
>      Security Level: Public(Everyone can see) 
>          Components: jbossws-native
>    Affects Versions:  jbossws-native-2.0.3
>         Environment: jboss as 4.2.2 + jbossws 2.0.3
>            Reporter: Mirko Ravagnan
>             Fix For:  jbossws-native-2.0.5
>
>         Attachments: CommonSOAPBinding.java
>
>
> Dear jbossws developers.
> I'm having an issue dealing with a webservice provided from a third party.
> The problem I encountered is as follows:
>    1: the service returns as output some data in the response
>       and an optional attachment sent with saaj.
>    2: the attachment comes along and is stored
>       in a temporary directory by jboss.
>    3: jbossws fails to identify the attachment and does not bind it
>       to the response of the service.
>    4: jboss throws the unused attachment away.
> I've tracked the issue on the class CommonSoapBinding
> in class org.jboss.ws.core.
> This issue is related to the inability of the mentioned class
> to deal with attachment by id, but only by part name.
> The fact is the service provider sends attachment
> without sending the content id complete with the part name
> let me explain:
> The http headers sent with the response message do not contain
> a header [Content-Id="MyAttachment=<blablabla...>"]
> but      [Content-Id="<blablabla...>"]
> Hence, method "getAttachmentFromMessage" fails to find the attachment.
> As a solution, I made some modification to that method in order
> it to deal with the requirement to handle attachments by id.
> I also modified the behaviour of the method not to throw an exception
> when it does not find the attachment it expects to find, because otherwise
> you might loose the chance to get fault text messages embedded in the service
> response, as this exception breaks the process of rebuilding the java
> representation of the soap message.
> This is reflected also in method "unbindResponseMessage".
> I'm attaching the modified java source
> so that you can have evidence of the changes
> (which have a comment "\\ m." on the right side of the line)
> Many thanks for your time

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.jboss.com/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        



More information about the jbossws-issues mailing list