5 Replies Latest reply on Jan 4, 2006 9:04 AM by Thomas Diesler

    JAXRPC Handler to modify incoming SOAP

    shin tai Newbie

      Hi all,

      Has anyone out there managed to successfully use a JAXRPC handler to modify an incoming SOAP message?

      My incoming SOAP message does not have the xsi type attribute (dodgy client?) so I'm writing a handler to insert it for me if it's not present.

      I've had numerous problems already and I've copied over fixes from JBWS-464
      http://jira.jboss.org/jira/browse/JBWS-464.

      Spefically I've patched my JBoss to use

      src/main/org/jboss/axis/Attic/MessagePart.java 1.1.2.4
      src/main/org/jboss/axis/message/Attic/SOAPElementAxisImpl.java 1.1.2.8


      But not JavaProvider.java rev 1.1.2.3 because the changes got reverted soon after


      And here's the handler code I'm using:

      public boolean handleRequest(MessageContext msgContext) {
       SOAPMessageContext smc = (SOAPMessageContext)msgContext;
       SOAPMessage msg = smc.getMessage();
      
       try {
       SOAPBody sb = msg.getSOAPBody();
      
       NodeList list = sb.getElementsByTagName("multipart");
      
       if (list == null || list.getLength() == 0)
       return true;
       else {
       for (int i = 0; i < list.getLength(); i++) {
       Node node = list.item(i);
       String ns = "http://www.w3.org/2001/XMLSchema-instance";
      
       NamedNodeMap attributes = node.getAttributes();
      
       if (attributes == null)
       continue;
      
       Node type = attributes.getNamedItem("type");
      
       if (type == null) {
       Document doc = list.item(i).getOwnerDocument();
       Attr attr = doc.createAttribute("xsi:type");
       attr.setValue("n1:Array");
      
       Node nn = attr.cloneNode(false);
      
       Node n = attributes.setNamedItem(nn);
       }
       }
       }
      
       } catch (SOAPException e) {
       e.printStackTrace();
       }
      
       return true;
       }




      I can see my attribute being added to the NamedNodeMap object (and the NamedNodeMap is live in the DOM so any changes should be reflected) but if I do a subsequent check on the SOAP message. My changes are not there. For brevity I haven't included above the code to check the SOAP message but here it is below:

      // Create transformer
       try {
       TransformerFactory tff = TransformerFactory.newInstance();
       Transformer tf = tff.newTransformer();
      
       // Get reply content
       Source sc = msg.getSOAPPart().getContent();
      
       // Set output transformation
       StreamResult result = new StreamResult(System.out);
       tf.transform(sc, result);
       System.out.println();
       } catch (Exception e) {
       throw new JAXRPCException(e);
       }



      Thanks,
      Shin

        • 1. Re: JAXRPC Handler to modify incoming SOAP
          Jason Greene Master

          Have you tried using Element.setAttribute(String, String)/getAttribute(String) instead?

          -Jason

          • 2. Re: JAXRPC Handler to modify incoming SOAP
            shin tai Newbie

            Thanks Jason. I think that did the trick!

            But it looks like the attribute has no effect and I think it's because the XML has been deserialised once already. So I think when RPCInvocation does

            Vector args = body.getParams();


            It returns the attributes before the XML was modified. I don't understand this because the code comments talk as though the fact that the handlers may have changed the XML has been considered.

            Thanks

            • 3. Re: JAXRPC Handler to modify incoming SOAP
              Jason Greene Master

              I looked at your code, and i see that you are modifying xsi:type, the setAttribute is probably working, the problem is that it is improperly ignored, which would most likely be this bug:

              http://jira.jboss.com/jira/browse/JBWS-349

              -Jason

              • 4. Re: JAXRPC Handler to modify incoming SOAP
                shin tai Newbie

                Thanks for your reply Jason.

                I'm not following jbws-349 too well but it doesn't look related. I should have explained that I have two clients; one that works because it includes the attribute and one that doesn't.

                Bad message

                <multipart n1:arrayType="n0:MultimediaMessagePart[1]" xmlns:n1="http://schemas.xmlsoap.org/soap/encoding/">
                 <item i:type="n0:MultimediaMessagePart">
                 <type i:type="d:string">image/gif</type>
                 <contentID i:nil="1"/>
                 <location i:nil="1"/>
                 <data i:type="n1:base64">somedata</data>
                 <multipart i:nil="1"/>
                 </item>
                </multipart>


                Good message

                <multipart i:type="n1:Array" n1:arrayType="n0:MultimediaMessagePart[1]" xmlns:n1="http://schemas.xmlsoap.org/soap/encoding/">
                 <item i:type="n0:MultimediaMessagePart">
                 <type i:type="d:string">image/gif</type>
                 <contentID i:nil="1"/>
                 <location i:nil="1"/>
                 <data i:type="n1:base64">somedata</data>
                 <multipart i:nil="1"/>
                 </item>
                </multipart>


                As you can see the only difference is that the good message contains
                <multipart i:type="n1:Array" .....


                Because I have no control over the client I decided to write a handler that would insert the attribute if it wasn't there. I'm at the stage where the handler inserts the attribute (so the XML changes) and the endpoint is invoked but I can see that the deserialised object (i.e. multipart) doesn't contain the data part.


                My attention has come to handleRequest() in org.jboss.webservice.handler.ServerHandlerChain :
                public boolean handleRequest(MessageContext msgContext)
                 {
                 RPCInvocation invBefore = (RPCInvocation)msgContext.getProperty(RPCProvider.RPC_INVOCATION);
                 if (invBefore == null)
                 throw new IllegalStateException("Cannot obtain RPCInvocation from message context");
                
                 String xmlBefore = invBefore.getRequestEnvelope().getAsStringFromInternal();
                 if (log.isTraceEnabled())
                 log.trace("RequestEnvelope before request handlers: " + xmlBefore);
                
                 boolean doNext = super.handleRequest(msgContext);
                
                 checkMustUnderstand(msgContext);
                
                 RPCInvocation invAfter = new RPCInvocation(invBefore);
                 invAfter.prepareFromRequestEnvelope();
                
                 String xmlAfter = invAfter.getRequestEnvelope().getAsStringFromInternal();
                 if (xmlBefore.equals(xmlAfter) == false)
                 {
                 if (log.isTraceEnabled())
                 log.trace("RequestEnvelope after request handlers: " + xmlAfter);
                
                 msgContext.setProperty(RPCProvider.RPC_INVOCATION, invAfter);
                 }
                
                 return doNext;
                 }


                xmlAfter contains the attribute so I know the handler has worked. But the RPCInvocation invAfter as part of its arguments does not contain the data part in the multipart object.

                Investigating further, it looks like invAfter.prepareFromRequestEnvelope() deserialises the XML. However (it looks to me that) it checks to see if it has deserialised it before. I believe that because invAfter is created using a copy constructor, it thinks that it does not have to deserialise the SOAP body again.

                Next thing I'll try is somehow force it to deserialise the body again.

                Thanks again,
                Shin



                • 5. Re: JAXRPC Handler to modify incoming SOAP
                  Thomas Diesler Master

                  The messages you show use SOAP encoded arrays. SOAP encoding is not allowed by the BasicProfile-1.0 and not well supported by WS4EE.

                  You probably want to migrate your application to rpc/literal or document/literal style.