What causes a "org.xml.sax.SAXException: Bad types" exception when calling a ColdFusion Web Service?

Eric Belair picture Eric Belair · Jan 13, 2012 · Viewed 8.4k times · Source

I've written a ColdFusion Web Service for an external client to consume. It basically let's them send over some user information, and returns the contents of the user's shopping cart, and let's them add items to the user's shopping cart. For simplicity sake, we'll just cover the "getCart" method.

They need the Web Service to return Xml in a SOAP envelope, however, they also need to Xml to contain specific complex data types. For instance, every request needs to return a status code and message, as part of a ReturnInfo type. So, I have written a hard-coded WSDL for them to consume with the complex data types outlined within (I basically took the ColdFusion-generated WSDL and edited it):

/ws/Cart.wsdl:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://cart.ws" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="http://cart.ws" xmlns:intf="http://cart.ws" xmlns:tns1="http://model.cart.ws" xmlns:tns2="http://rpc.xml.coldfusion" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <wsdl:types>
    <schema elementFormDefault="qualified" targetNamespace="http://cart.ws" xmlns="http://www.w3.org/2001/XMLSchema">
      <import namespace="http://model.cart.ws"/>
      <import namespace="http://rpc.xml.coldfusion"/>
      <element name="addItemToCart">
        <complexType>
          <sequence>
            <element name="loginID" type="xsd:string"/>
            <element name="spikeId" type="xsd:int"/>
            <element name="cartItem" type="tns1:CartItem"/>
          </sequence>
        </complexType>
      </element>
      <element name="addItemToCartResponse">
        <complexType>
          <sequence>
            <element name="addItemToCartReturn" type="tns1:ReturnInfo"/>
          </sequence>
        </complexType>
      </element>
      <element name="fault" type="tns2:CFCInvocationException"/>
      <element name="getCart">
        <complexType>
          <sequence>
            <element name="loginID" type="xsd:string"/>
            <element name="spikeId" type="xsd:int"/>
          </sequence>
        </complexType>
      </element>
      <element name="getCartResponse">
        <complexType>
          <sequence>
            <element name="getCartReturn" type="tns1:CartResponse"/>
          </sequence>
        </complexType>
      </element>
    </schema>
    <schema elementFormDefault="qualified" targetNamespace="http://model.cart.ws" xmlns="http://www.w3.org/2001/XMLSchema">
      <import namespace="http://rpc.xml.coldfusion"/>
      <complexType name="CartItem">
        <sequence>
          <element name="quantity" nillable="true" type="xsd:int"/>
          <element name="sku" nillable="true" type="xsd:string"/>
        </sequence>
      </complexType>
      <complexType name="ReturnInfo">
        <sequence>
          <element name="code" nillable="true" type="xsd:int"/>
          <element name="message" nillable="true" type="xsd:string"/>
        </sequence>
      </complexType>
      <complexType name="CartResponse">
        <sequence>
          <element name="cartItems" nillable="true" type="tns1:ArrayOfCartItems"/>
          <element name="returnInfo" nillable="true" type="tns1:ReturnInfo"/>
        </sequence>
      </complexType>
      <complexType name="ArrayOfCartItems">
        <sequence>
          <element maxOccurs="unbounded" minOccurs="0" name="cartItems" type="tns1:CartItem"/>
        </sequence>
      </complexType>
    </schema>
    <schema elementFormDefault="qualified" targetNamespace="http://rpc.xml.coldfusion" xmlns="http://www.w3.org/2001/XMLSchema">
      <import namespace="http://model.cart.ws"/>
      <complexType name="CFCInvocationException">
        <sequence/>
      </complexType>
    </schema>
  </wsdl:types>
  <wsdl:message name="CFCInvocationException">
    <wsdl:part element="impl:fault" name="fault"/>
  </wsdl:message>
  <wsdl:message name="addItemToCartRequest">
    <wsdl:part element="impl:addItemToCart" name="parameters"/>
  </wsdl:message>
  <wsdl:message name="getCartRequest">
    <wsdl:part element="impl:getCart" name="parameters"/>
  </wsdl:message>
  <wsdl:message name="addItemToCartResponse">
    <wsdl:part element="impl:addItemToCartResponse" name="parameters"/>
  </wsdl:message>
  <wsdl:message name="getCartResponse">
    <wsdl:part element="impl:getCartResponse" name="parameters"/>
  </wsdl:message>
  <wsdl:portType name="Cart">
    <wsdl:operation name="addItemToCart">
      <wsdl:input message="impl:addItemToCartRequest" name="addItemToCartRequest"/>
      <wsdl:output message="impl:addItemToCartResponse" name="addItemToCartResponse"/>
      <wsdl:fault message="impl:CFCInvocationException" name="CFCInvocationException"/>
    </wsdl:operation>
    <wsdl:operation name="getCart">
      <wsdl:input message="impl:getCartRequest" name="getCartRequest"/>
      <wsdl:output message="impl:getCartResponse" name="getCartResponse"/>
      <wsdl:fault message="impl:CFCInvocationException" name="CFCInvocationException"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="cart.cfcSoapBinding" type="impl:Cart">
    <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="addItemToCart">
      <wsdlsoap:operation soapAction=""/>
      <wsdl:input name="addItemToCartRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="addItemToCartResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
      <wsdl:fault name="CFCInvocationException">
        <wsdlsoap:fault name="CFCInvocationException" use="literal"/>
      </wsdl:fault>
    </wsdl:operation>
    <wsdl:operation name="getCart">
      <wsdlsoap:operation soapAction=""/>
      <wsdl:input name="getCartRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="getCartResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
      <wsdl:fault name="CFCInvocationException">
        <wsdlsoap:fault name="CFCInvocationException" use="literal"/>
      </wsdl:fault>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="CartWebService">
    <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
      Contains WebService methods for retrieving and updating Cart data
    </wsdl:documentation>
    <wsdl:port binding="impl:cart.cfcSoapBinding" name="cart.cfc">
      <wsdlsoap:address location="http://mysite/ws/cart/cart.cfc"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

Every call they make to either Web Service give them the same error:

[1/13/12 12:26:19:557 EST] 00000018 SystemErr     R org.xml.sax.SAXException: Bad types (interface javax.xml.soap.SOAPElement -> class ws.cart.model.CartResponse) Message being parsed: <?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> 
  <getCartResponse xmlns="http://cart.ws"> 
   <getCartReturn xsi:type="ns1:Document" xmlns:ns1="http://xml.apache.org/xml-soap"> 
    <ns2:CartResponse xmlns="http://model.cart.ws" xmlns:ns2="http://model.cart.ws"> 
     <ns2:ReturnInfo xmlns="http://model.cart.ws"> 
      <code xmlns="">1</code> 
      <message xmlns="">Items are pending in cart</message> 
     </ns2:ReturnInfo> 
     <cartItems xmlns=""> 
      <cartItem> 
       <sku>ABC123</sku> 
       <quantity>2</quantity> 
      </cartItem> 
     </cartItems> 
    </ns2:CartResponse> 
   </getCartReturn> 
  </getCartResponse> 
 </soapenv:Body> 
</soapenv:Envelope> 

I'm not very familiar with SOAP, or Complex Data Types in CF, so I'm kinda stuck here. Here's my CF Code:

/ws/cart.cfc:

<cfcomponent
    displayname="CartWebService"
    hint="Contains WebService methods for retrieving and updating Cart data"
    style="document"
    wsdlfile="Cart.wsdl"
    namespace="http://cart.ws"
    >
    <cffunction
        name="getCart"
        access="remote"
        displayname="Get Cart"
        hint="Returns cart data (Skus, Quantities) for a given UserId"
        description="Returns cart data (Skus, Quantities) for a given UserId"
        output="false"
        returntype="xml">
        <cfargument name="loginID" type="String" default="" />

        <cfscript>
            var response =
                CreateObject("component", "ws.cart.model.GetCartResponse");
            var cart = CreateObject("component", "lib.Cart");
            var _loginId = UCase(Trim(ARGUMENTS.LoginID));
            var i = 1;
            var cartItem = CreateObject("component", "ws.cart.model.CartItem");

            /*
            Validation/processing....
            */

            // get the cart data
            cart = cart.init(_loginId);

            qCartItems = cart.getItems();

            if (qCartItems.RecordCount) {
                for (i=1; i<=qCartItems.RecordCount; i++) {
                    cartItem =
                        CreateObject("component", "ws.cart.model.CartItem");
                    cartItem.quantity = qCartItems["quantity"][i];
                    cartItem.sku = qCartItems["sku"][i];

                    ArrayAppend(response.cartItems, cartItem);
                }

                // responde with the Xml of status code, status message, and cart items
                return response.setCode(1).toXML();
            }

            // default response - no items in cart
            return response.setCode(0).toXML();
        </cfscript>
    </cffunction>
</cfcomponent>

/ws/model/CartItem.cfc:

<cfcomponent
    displayname="Cart Item"
    hint="Represents an item in the cart (existing or to be added/updated)"
    namespace="http://model.cart.ws"
    output="false">
    <cfproperty name="quantity" type="Numeric" />
    <cfproperty name="sku" type="String" />
</cfcomponent>

/ws/model/GetCartResponse.cfc:

<cfcomponent
    displayname="Cart Response"
    hint="Contains Cart Items and Return Info"
    namespace="http://model.cart.ws"
    extends="ReturnInfo"
    implements="IReturnInfo"
    output="false">
    <cfproperty
        name="cartItems"
        displayname="Array of Cart Items"
        hint="Array of Cart Items"
        type="CartItem[]" />

    <cfscript>
        THIS.cartItems = [];
    </cfscript>

    <cffunction name="getMessage" returntype="String" output="false">
        <cfscript>
            switch(THIS.code) {
                //  Success: no items in cart
                case 0:
                    return "No items in cart.";

                    break;
                //  Success: items are pending in cart
                case 1:
                    return "Items are pending in cart";

                    break;
            }

            return "";
        </cfscript>
    </cffunction>

    <cffunction name="toXML" returntype="XML" output="false">
        <cfscript>
            var cartResponseXML = XmlNew(true);
            var i = 1;
            var numCartItems = ArrayLen(THIS.cartItems);

            cartResponseXML["xmlRoot"] =
                XmlElemNew(
                    cartResponseXML,
                    "http://model.cart.ws",
                    "CartResponse"
                    );

            cartResponseXML["xmlRoot"]["ReturnInfo"] =
                XmlElemNew(
                    cartResponseXML,
                    "http://model.cart.ws",
                    "ReturnInfo"
                );
            cartResponseXML["xmlRoot"]["ReturnInfo"]["code"] =
                XmlElemNew(cartResponseXML, "code");
            cartResponseXML["xmlRoot"]["ReturnInfo"]["code"].XmlText =
                THIS.code;
            cartResponseXML["xmlRoot"]["ReturnInfo"]["message"] =
                XmlElemNew(cartResponseXML, "message");
            cartResponseXML["xmlRoot"]["ReturnInfo"]["message"].XmlText =
                THIS.message;

            cartResponseXML["xmlRoot"]["cartItems"] =
                XmlElemNew(cartResponseXML, "cartItems");

            for (i=1; i<=numCartItems; i++) {
                cartItem = THIS.cartItems[i];
                cartItemXML = XmlElemNew(cartResponseXML, "cartItem");
                cartItemXML["sku"] = XmlElemNew(cartResponseXML, "sku");
                cartItemXML["sku"].XmlText = cartItem.sku;
                cartItemXML["quantity"] =
                    XmlElemNew(cartResponseXML, "quantity");
                cartItemXML["quantity"].XmlText = cartItem.quantity;

                ArrayAppend(
                    cartResponseXML["xmlRoot"]["cartItems"].XmlChildren,
                    cartItemXML
                );
            }

            return cartResponseXML;
        </cfscript>
    </cffunction>
</cfcomponent>

Answer

Shawn Holmes picture Shawn Holmes · Jan 14, 2012

Could be a number of things, but the most commonly overlooked reason this is caused by is:

You've changed the signature (function parameters, types, number of arguments, return type etc.) of the method, but ColdFusion has cached the stub.

Solution

Go to ColdFusion Administrator, and under Data & Services -> Web Services, find the URL which includes the WSDL, and either refresh it, or remove it completely.

May not be your specific solution, but is a reasonable starting point.