2 Replies Latest reply on Dec 9, 2009 2:47 PM by shadowcreeper

    How to recieve RPC/encoded SOAP messages in JBoss5?

    shadowcreeper

      For legacy clients which refuse to drop rpc/encoded SOAP format, what can we use to get their messages in JBoss5?

      We would love for them to switch over to document/literal (or even rpc/literal) but big companies are extremely reluctant to (read: never will) change. :(

      Thanks.
      -Shadow

      [initially posted this in the sun metro forum on accident, it was meant to go here]

        • 1. Re: How to recieve RPC/encoded SOAP messages in JBoss5?
          pa12399

          Hi

          I am having the exact same issue as well. One of my clients is using RPC encoded in their WSDL, which I am having to call. So, I am unable to port the client into the latest version of JBoss. Any pointers would be greatly appreciated.

          • 2. Re: How to recieve RPC/encoded SOAP messages in JBoss5?
            shadowcreeper

            OK, here's what seems to be working for me... It's a horrible hack, but it works...

            First, take your WSDL file and turn it into RPC/literal (for me it was as easy as replacing the word "encoded" with "literal").

            Next, setup your JBossWS stuff as though you will be receiving RPC/literal documents (from its standpoint, you will).

            Now for the tricky part... You will have already setup a servlet (@WebService), which you now need to add an XSLT filter to...

            All I had to do to make my SOAP messages understandable to JBossWS was flatten the "multiRef" nodes.

            Original SOAP message:

            <myWebServiceMethod>
             <myArgumentObject href="#id0"/>
            </myWebServiceMethod>
            <multiRef id="id0" ...>
             <actualGutsOfArgument/>
             <referencedGutNum2 href="#id2"/>
            </multiRef>
            <multiRef id="id2" ...>
             <moreActualGuts/>
            </multiRef>
            


            Flattened SOAP message:
            <myWebServiceMethod>
             <myArgumentObject id="id0" ...>
             <actualGutsOfArgument/>
             <referencedGutNum2 id="id2" ...>
             <moreActualGuts/>
             </referencedGutNum2>
             </myArgumentObject>
            </myWebServiceMethod>
            


            Note: The only attribute I removed was "href", all of the attributes to the multiRef node became attributes to the node that referenced it (this includes xsi:type info which I believe gets ignored anyway since it is already specified in the XSD file that my WSDL references).

            The filter will look something like this:
            public class MultiRefFlattener
             implements Filter
            {
             private Transformer m_transformer = null;
            
             public void init ( FilterConfig filterConfig )
             throws ServletException
             {
             final String stylePath = filterConfig.getServletContext().getRealPath( "WEB-INF/multi-ref-flattener.xslt" );
             final Source styleSource = new StreamSource( stylePath );
             final TransformerFactory transformerFactory = TransformerFactory.newInstance();
             try
             {
             m_transformer = transformerFactory.newTransformer( styleSource );
             }
             catch( TransformerConfigurationException e )
             {
             throw new ServletException( "Error creating XSLT transformer: ", e );
             }
             }
            
             public void doFilter(
             final ServletRequest request,
             final ServletResponse response,
             final FilterChain chain )
             throws IOException, ServletException
             {
             final String requestText;
             try
             {
             final CharArrayWriter caw = new CharArrayWriter();
             final StreamResult result = new StreamResult(caw);
             m_transformer.transform(new StreamSource(request.getInputStream()), result);
             requestText = caw.toString();
             }
             catch( TransformerException e )
             {
             throw new ServletException( "Error filtering data" );
             }
            
             final ServletInputStream requestStream = new ServletInputStream()
             {
             private int m_column = 0;
             public int read ()
             throws IOException
             {
             if( m_column >= requestText.length() )
             return -1;
             final int character = requestText.charAt( m_column );
             ++m_column;
             return character;
             }
             };
            
             final HttpServletRequest httpServletRequest = (HttpServletRequest)request;
             final HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper( httpServletRequest )
             {
             @Override
             public int getContentLength ()
             {
             return requestText.length();
             }
            
             @Override
             public ServletInputStream getInputStream ()
             throws IOException
             {
             return requestStream;
             }
             };
             chain.doFilter( wrapper, response );
             }
            
             public void destroy ()
             {
             m_transformer = null;
             }
            }
            


            If anybody has a better way of faking an HttpServletRequest, please let me know.

            And here is my flattening XSLT file:
            <xsl:stylesheet version="1.0"
             xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
             xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
            
             <xsl:template match="/">
             <xsl:call-template name="CopyNode"/>
             </xsl:template>
            
             <xsl:template name="CopyNodeRef">
             <xsl:param name="elementName"/>
             <xsl:param name="hrefId"/>
             <xsl:copy>
             <xsl:element name="$elementName">
             <xsl:for-each select="//multiRef[@id = $hrefId]/@*">
             <xsl:copy>
             <xsl:apply-templates mode="keeping"/>
             </xsl:copy>
             </xsl:for-each>
             <xsl:for-each select="//multiRef[@id = $hrefId]/node()[not(@href)]">
             <xsl:call-template name="CopyNode"/>
             </xsl:for-each>
             <xsl:for-each select="//multiRef[@id = $hrefId]/node()[@href]">
             <xsl:call-template name="CopyNodeRef">
             <xsl:with-param name="elementName" select="name()"/>
             <xsl:with-param name="hrefId" select="substring(@href, 2)"/>
             </xsl:call-template>
             </xsl:for-each>
             </xsl:element>
             </xsl:copy>
             </xsl:template>
            
             <xsl:template name="CopyNode">
             <xsl:copy>
             <xsl:call-template name="CopyAttributes"/>
             <xsl:for-each select="node()[not(@href|@id)]">
             <xsl:call-template name="CopyNode"/>
             </xsl:for-each>
             <xsl:for-each select="node()[@href]">
             <xsl:call-template name="CopyNodeRef">
             <xsl:with-param name="elementName" select="name()"/>
             <xsl:with-param name="hrefId" select="substring(@href, 2)"/>
             </xsl:call-template>
             </xsl:for-each>
             </xsl:copy>
             </xsl:template>
            
             <xsl:template name="CopyAttributes">
             <xsl:for-each select="@*">
             <xsl:copy>
             <xsl:apply-templates mode="keeping"/>
             </xsl:copy>
             </xsl:for-each>
             </xsl:template>
            
            </xsl:stylesheet>
            


            If anybody has a better idea for a flattener, please let me know.

            I hope this helps.
            -Shadow