Axis
  1. Axis
  2. AXIS-2586

Wrapped/Literal encoding and abstract parameters with arrays won't work.

    Details

    • Type: Bug Bug
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 1.4
    • Fix Version/s: None
    • Labels:
      None
    • Environment:
      Tomcat 5.5
      Sun JRE 1.5.0.07
      Windows XP SP2

      Description

      I found a problem when passing abstract parameters with array fields to a method in Wrapped/Literal encoding:

      Steps to reproduce
      1 - create a base class that has an array field of itself.
      2 - create a sub class that inherits from the base class
      3 - create a service class to publish as a web service that uses the base class as a paramter for a public method
      4 - use Java2WSDL to publish the service class using wrapped/literal encoding
      5 - run the web services on a web server
      6 - use WSDL2Java to create stubs for a test client
      7 - use the test client to call the method on the service class with a Sub Class instance.
      8 - notice that the call to the service fails with the error : org.xml.sax.SAXException: Invalid element in BaseClass - item

      Here are the code samples:

      class BaseClass{
      private BaseClass[] childArray;
      public BaseClass[] getChildArray()

      { return childArray; }

      public void setChildArray(BaseClass[] childArray)

      { this.childArray = childArray; }


      }

      class SubClass extends BaseClass{
      private BaseClass[] otherChildArray;
      public BaseClass[] getOtherChildArray()

      { return otherChildArray; }

      public void setOtherChildArray(BaseClass[] otherChildArray)

      { this.otherChildArray = otherChildArray; }


      }

      // Use Java2WSDL to publish this as a web service
      class MyTestInterface{
      // send and return the same base class parameter
      public BaseClass sendBase(BaseClass base)

      { return base; }

      // this method just declares sub to make sure it finds its
      // way in the WSDL
      public void declareSub(SubClass sub){};
      }

      // client unit test code
      class MyTest extends junit.framework.TestCase{
      public void testSendBase(){
      // Insert code to resolve URL and locate the web services here
      MyTestInterface x = null;
      SubClass sendSubClass = new SubClass();
      sendSubClass.setChildArray(new BaseClass[]

      {new SubClass()}

      );
      // this call will fail with the message
      // org.xml.sax.SAXException: Invalid element in BaseClass - item
      BaseClass retVal = x.sendBase(sendSubClass);
      assertEquals(sendSubClass, retVal);
      }
      }

      Note:

      • This problem is reproducible in Document/Literal
      • This problem is not reproducible in RPC/Encoded

        Activity

        Hide
        Steve McDuff added a comment -

        Possible solution identified:

        In the class : org.apache.axis.encoding.ser.BeanDeserializer.
        Problem: getDeserializer wasn't getting the right deserializer for array types when the xmlType is set.
        Solution: apply the default XML Type when we are getting an array deserializer

        I modified the following method (see method comments to know where changes were applied).

        protected Deserializer getDeserializer(QName xmlType,
        Class javaType,
        String href,
        DeserializationContext context) {
        if (javaType.isArray())

        { context.setDestinationClass(javaType); }


        // See if we have a cached deserializer
        if (cacheStringDSer != null) {
        if (String.class.equals(javaType) &&
        href == null &&
        (cacheXMLType == null && xmlType == null ||
        cacheXMLType != null && cacheXMLType.equals(xmlType)))

        { cacheStringDSer.reset(); return cacheStringDSer; }

        }

        Deserializer dSer = null;

        // MODIFICATION DONE HERE : Added the "&& ! javatype.isArray()" part in the line below
        if (xmlType != null && href == null && ! javatype.isArray() )

        { // Use the xmlType to get the deserializer. dSer = context.getDeserializerForType(xmlType); }

        else {
        // If the xmlType is not set, get a default xmlType
        TypeMapping tm = context.getTypeMapping();
        QName defaultXMLType = tm.getTypeQName(javaType);
        // If there is not href, then get the deserializer
        // using the javaType and default XMLType,
        // If there is an href, the create the generic
        // DeserializerImpl and set its default type (the
        // default type is used if the href'd element does
        // not have an xsi:type.
        if (href == null)

        { dSer = context.getDeserializer(javaType, defaultXMLType); }

        else

        { dSer = new DeserializerImpl(); context.setDestinationClass(javaType); dSer.setDefaultType(defaultXMLType); }

        }
        if (javaType.equals(String.class) &&
        dSer instanceof SimpleDeserializer)

        { cacheStringDSer = (SimpleDeserializer) dSer; cacheXMLType = xmlType; }

        return dSer;
        }

        Note: The source problem seems to be that the client sends its arrays identified with types when special conditions are met.

        Here are 2 SOAP messages sent. One uses a BaseClass as a root parameter, the other uses a SubClass.

        SOAP using baseclass:

        <?xml version="1.0" encoding="UTF-8"?>
        <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <soapenv:Body>
        <sendParent xmlns="http://text.com">
        <x>
        <childs>
        <item xmlns:ns1="http://text.com" xsi:type="ns1:ChildInherit">
        <ns1:childs>
        <ns1:item xsi:type="ns1:ChildInherit">
        <ns1:childs xsi:nil="true"/>
        </ns1:item>
        </ns1:childs>
        </item>
        </childs>
        </x>
        </sendParent>
        </soapenv:Body>
        </soapenv:Envelope>

        SOAP using subclass:

        <?xml version="1.0" encoding="UTF-8"?>
        <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <soapenv:Body>
        <sendParent xmlns="http://text.com">
        <x xmlns:ns1="http://text.com" xsi:type="ns1:ParentInherit">
        <ns1:childs xsi:type="ns1:Child">
        <ns1:item xsi:type="ns1:ChildInherit">
        <ns1:childs xsi:type="ns1:Child">
        <ns1:item xsi:type="ns1:ChildInherit">
        <ns1:childs xsi:nil="true" xsi:type="ns1:Child"/>
        </ns1:item>
        </ns1:childs>
        </ns1:item>
        </ns1:childs>
        </x>
        </sendParent>
        </soapenv:Body>
        </soapenv:Envelope>

        Notice that by using a subclass, all the arrays in the XML get tagged with "xsi:type". It is even applied to arrays that are deeper down the object hierarchy.

        Conclusion:

        • The fix I applied above is not the problem source, but it works as a failsafe for the server side.
        • The client serializer should be fixed not to tag "xsi:type" on arrays.
        Show
        Steve McDuff added a comment - Possible solution identified: In the class : org.apache.axis.encoding.ser.BeanDeserializer. Problem: getDeserializer wasn't getting the right deserializer for array types when the xmlType is set. Solution: apply the default XML Type when we are getting an array deserializer I modified the following method (see method comments to know where changes were applied). protected Deserializer getDeserializer(QName xmlType, Class javaType, String href, DeserializationContext context) { if (javaType.isArray()) { context.setDestinationClass(javaType); } // See if we have a cached deserializer if (cacheStringDSer != null) { if (String.class.equals(javaType) && href == null && (cacheXMLType == null && xmlType == null || cacheXMLType != null && cacheXMLType.equals(xmlType))) { cacheStringDSer.reset(); return cacheStringDSer; } } Deserializer dSer = null; // MODIFICATION DONE HERE : Added the "&& ! javatype.isArray()" part in the line below if (xmlType != null && href == null && ! javatype.isArray() ) { // Use the xmlType to get the deserializer. dSer = context.getDeserializerForType(xmlType); } else { // If the xmlType is not set, get a default xmlType TypeMapping tm = context.getTypeMapping(); QName defaultXMLType = tm.getTypeQName(javaType); // If there is not href, then get the deserializer // using the javaType and default XMLType, // If there is an href, the create the generic // DeserializerImpl and set its default type (the // default type is used if the href'd element does // not have an xsi:type. if (href == null) { dSer = context.getDeserializer(javaType, defaultXMLType); } else { dSer = new DeserializerImpl(); context.setDestinationClass(javaType); dSer.setDefaultType(defaultXMLType); } } if (javaType.equals(String.class) && dSer instanceof SimpleDeserializer) { cacheStringDSer = (SimpleDeserializer) dSer; cacheXMLType = xmlType; } return dSer; } Note: The source problem seems to be that the client sends its arrays identified with types when special conditions are met. Here are 2 SOAP messages sent. One uses a BaseClass as a root parameter, the other uses a SubClass. SOAP using baseclass: <?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Body> <sendParent xmlns="http://text.com"> <x> <childs> <item xmlns:ns1="http://text.com" xsi:type="ns1:ChildInherit"> <ns1:childs> <ns1:item xsi:type="ns1:ChildInherit"> <ns1:childs xsi:nil="true"/> </ns1:item> </ns1:childs> </item> </childs> </x> </sendParent> </soapenv:Body> </soapenv:Envelope> SOAP using subclass: <?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <soapenv:Body> <sendParent xmlns="http://text.com"> <x xmlns:ns1="http://text.com" xsi:type="ns1:ParentInherit"> <ns1:childs xsi:type="ns1:Child"> <ns1:item xsi:type="ns1:ChildInherit"> <ns1:childs xsi:type="ns1:Child"> <ns1:item xsi:type="ns1:ChildInherit"> <ns1:childs xsi:nil="true" xsi:type="ns1:Child"/> </ns1:item> </ns1:childs> </ns1:item> </ns1:childs> </x> </sendParent> </soapenv:Body> </soapenv:Envelope> Notice that by using a subclass, all the arrays in the XML get tagged with "xsi:type". It is even applied to arrays that are deeper down the object hierarchy. Conclusion: The fix I applied above is not the problem source, but it works as a failsafe for the server side. The client serializer should be fixed not to tag "xsi:type" on arrays.

          People

          • Assignee:
            Unassigned
            Reporter:
            Steve McDuff
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:

              Development