0 Replies Latest reply on Feb 18, 2009 9:31 AM by Andrew Dinn

    Using Lists of String in WSDL-first JaxWS service

    Andrew Dinn Master

      I am developing a JaxWS-based test service which supports scripting of various actions in the server. So, the service provides a single WebMethod which takes a list of command strings and returns a list of result strings. I defined this using the following WSDL:

      <definitions
       targetNamespace="http://jbossts.jboss.org/xts/servicetests/generated"
       xmlns:s="http://www.w3.org/2001/XMLSchema"
       xmlns:tns="http://jbossts.jboss.org/xts/servicetests/generated"
       xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
       xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
       xmlns:wsaw="http://www.w3.org/2006/02/addressing/wsdl"
       xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
       xmlns="http://schemas.xmlsoap.org/wsdl/" >
       <types>
       <s:schema
       targetNamespace="http://jbossts.jboss.org/xts/servicetests/generated"
       xmlns="http://jbossts.jboss.org/xts/servicetests/generated">
       <s:simpleType name="commandList">
       <s:list itemType="s:string"/>
       </s:simpleType>
      
       <s:simpleType name="resultList">
       <s:list itemType="s:string">
       </s:list>
       </s:simpleType>
      
       <s:complexType name="commandsType">
       <s:sequence>
       <s:element name="commandList" type="commandList"/>
       </s:sequence>
       </s:complexType>
      
       <s:complexType name="resultsType">
       <s:sequence>
       <s:element name="resultList" type="resultList"/>
       </s:sequence>
       </s:complexType>
      
       <s:element name="commands" type="commandsType"/>
      
       <s:element name="results" type="resultsType"/>
      
       <!--
       <s:import namespace="http://schemas.xmlsoap.org/soap/envelope/"
       schemaLocation="http://schemas.xmlsoap.org/soap/envelope"/>
       -->
       </s:schema>
       </types>
      
       <message name="Commands">
       <part name="commands" element="tns:commands" />
       </message>
      
       <message name="Results">
       <part name="results" element="tns:results" />
       </message>
      
       <portType name="XTSServiceTestPortType">
       <operation name="serve">
       <input name="Commands" message="tns:Commands" />
       <output name="Results" message="tns:Results" />
       </operation>
       </portType>
      
       <binding name="XTSServiceTestPort_SOAPBinding" type="tns:XTSServiceTestPortType">
       <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
       <operation name="serve">
       <input message="tns:Commands">
       <soap:body use="literal"/>
       </input>
       <output message="tns:Commands">
       <soap:body use="literal"/>
       </output>
       </operation>
       </binding>
      
       <service name="XTSServiceTestService">
       <port binding="tns:XTSServiceTestPort_SOAPBinding" name="XTSServiceTestPortType">
       <wsaw:UsingAddressing required="true"/>
       <soap:address location="http://localhost:9000/xtsservicetests/XTSServiceTestService"/>
       </port>
       </service>
      </definitions>
      


      I generated the Java code from this and obtained the following types

      @XmlAccessorType(XmlAccessType.FIELD)
      @XmlType(name = "commandsType", propOrder = {
       "commandList"
      })
      public class CommandsType {
       @XmlList
       @XmlElement(required = true)
       protected List<String> commandList;
       public List<String> getCommandList() {
       if (commandList == null) {
       commandList = new ArrayList<String>();
       }
       return this.commandList;
       }
      }
      
      @XmlAccessorType(XmlAccessType.FIELD)
      @XmlType(name = "resultsType", propOrder = {
       "resultList"
      })
      public class ResultsType {
       @XmlList
       @XmlElement(required = true)
       protected List<String> resultList;
       public List<String> getResultList() {
       if (resultList == null) {
       resultList = new ArrayList<String>();
       }
       return this.resultList;
       }
      }
      


      The server interface was generated as follows:

      @WebService(name = "XTSServiceTestPortType", targetNamespace = "http://jbossts.jboss.org/xts/servicetests/generated")
      @SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
      public interface XTSServiceTestPortType {
       @WebMethod
       @WebResult(name = "results", targetNamespace = "http://jbossts.jboss.org/xts/servicetests/generated", partName = "results")
       public ResultsType serve(
       @WebParam(name = "commands", targetNamespace = "http://jbossts.jboss.org/xts/servicetests/generated", partName = "commands")
       CommandsType commands);
      }
      


      This works fine for a whole load of cases where I supply a series of string commands and get one or more strings as a reply. However, I found to my surprise a case where the results gets mangled. One of my command types contains the single command "subtransaction" which causes the server to start a subordinate transaction and return a transaction id. I call it as follows

       commands = new CommandsType();
       commands.getCommandList().add("subtransaction");
      
       try {
       results = port.serve(commands);
       } catch (Exception e) {
       exception = e;
       }
      


      On the server side I obtain a CommandsType containing a list with the single String element "subtransaction" as expected. The code which returns the result is as follows:


       result = new ResultsType();
       resultsList = result.getResultsList()
       String id = newTx.toString();
       resultsList.add(id);
       return resultsList;
      


      When I look at resultsList it contains a single String soemthing like this "AtomicTransactionIdentifier: urn:-53ef7d93:9592:499c0a00:ec".

      Now, going back to the client the value returned from the call to port.serve(commands) is a ResultsType whose ResultsList contains two strings,
      "AtomicTransactionIdentifier:" and "urn:-53ef7d93:9592".

      In my debugger I stepped up out of the server implementation method from the return statement up to the point where the returned value was encoded into a SOAPMessage and printed the message to System.out. The result was as follows:

      13:54:22,795 INFO [STDOUT] <env:Envelope xmlns:env='http://schemas.xmlsoap.org/soap/envelope/'><env:Header></env:Header><env:Body><ns1:results xmlns:ns1="http://jbossts.jboss.org/xts/servicetests/generated"><resultList>AtomicTransactionIdentifier: urn:-53ef7d93:9592:499c0a00:ec</resultList></ns1:results></env:Body></env:Envelope>
      


      It looks like the message text representation of the SOAP element corresponding to field resultsList of type List is encoded by printing the text of each String separated by spaces inside tags and decoded by reading back in the space-separated character sequences found between these tags. This appears to ignore the presence of spaces embedded in any of the Strings. That's not exactly what I would call robust :-).

      Firstly, is this a WS issue or a JaxB issue?

      Secondly, can I get round this by using a more sophiisticated schema or do I have to resort to registering my own encode/decode callbacks with JaxB?

      I realise I could avoid the problem by making sure my Strings don't contain spaces but I'd prefer to implement a more usable service.