Thursday, April 21, 2011

Debugging ColdFusion Webservices

Debugging webservice calls in ColdFusion is almost always a painful experience. Recently I spent some time debugging a CF call to a .Net webservice that was expecting a ArrayOfInt as well as some custom data types. Here are some steps I went throuogh:

  • The first step is to find out if the call is getting to the webservice or is it being stopped by CF itself. You can use a network sniffing tool (like Wireshark) for this. Or you can enable request/response logging in ColdFusion. Here's how: Open client-config.wsdd (its usually at  C:\ColdFusion8\wwwroot\WEB-INF). You will see a requestFlow and a responseFlow section. Uncomment out this section. Restart CF. Now when you make a webservice call from CF, the raw request/response will be logged in cfusion-out.log (usually located at C:\ColdFusion8\runtime\logs). See if your call generates an entry in this file. If it does, you know that your call is getting through to the webservice endpoint. If you dont see any entry here, that would imply that the request is being killed by CF itself.

    NB: Do remember to comment out the requestFlow and responseFlow lines once you are done debugging.
  • If your request is not getting to the API webservice, chances are you are getting one of 2 errors:
Web service operation methodName with parameters ....... cannot be found. OR
java.lang.IllegalArgumentException: argument type mismatch
  • If you are getting "Web service operation sendMessageToGroup with parameters ....... cannot be found.", then you probably have a mismatch of the arguments you are passing to the service and the arguments that it is expecting. Depending on how you are calling the webservice (CFINVOKE, cfobject, named parameters versus non-named parameters, etc), ensure that the signatures match exactly. If using named parameters, ensure spellings are correct. If using non-named parameters, ensure the order of parameters is correct.
  • If you are instead getting "java.lang.IllegalArgumentException: argument type mismatch", there is probably a mismatch between the argument type that the webservice is expecting and the argument type you are passing to it. This is mostly the case when complex data types are involved. To troubleshoot this problem, here are a few useful steps:
  1. If you have a number of complex data types in the method arguments, your first step is to find out which argument is causing the mismatch. ColdFusion itself will not tell you which argument is causing the "mismatch". This is how I do it: Browse to the web service wsdl file in a browser. Save the file locally to your dev. environment (a place that is web accessible and where you can edit it). This is now your local copy of the wsdl. Open the wsdl file and locate the method thats causing you grief. If you have more than one complex data type (including arrays, custom data type, enums), remove the one you think is causing the problem; now in your CF code to call the service change the endpoint (wsdl location to your local wsdl) as well as adjust the method call accordingly.
    Run the CF page to see if the error changes. If it does you now know that this is the parameter that is causing the "argument type mismatch" error. If the error is still the same you know that the parameter you removed is not the one that is causing the error.
  2. Once you have identified the argument that is causing the error, next task is to find out what exactly is wrong. The best way is to look at the webservice stubs that Java is creating. There are 2 ways of doing this:
    a. Using WSDL2Java: This method has been documented fairly well. See Tom Jordahl' post or Adobe KB article.
    b. There's a simpler way. When you call a web service for the first time, ColdFusion automatically creates the stub files. These are usually located at: C:\ColdFusion8\stubs\
    Depending on how many webservice calls you have made there may be multiple folders here. You can browse thro' the folders to see which one belongs to the service you are troubleshooting. Or you can simply delete all folders, make your call and see which folder was created. Now dig thro' the folder and you will find a number of java class files. Each class file corresponds to something in your wsdl. Now if you try to open the class file, you will see garbage because class files are compiled java file. So to get the source behind the class file, you can use a java decompiler. There are quote a few out there. But I like one one here: It has a GUI version as well as an Eclipse plugin. Once you open a class file you will be able to see the interface definitions as Java internally sees it. 
  • If you are getting a "java.lang.IllegalArgumentException: argument type mismatch" while calling a .Net webservice with ArrayOfInt or ArrayOfString as the argument type, then most probably you are not defining the datatypes correctly. The ArrayOfInt and ArrayOfString will lead you to believe that .Net is expecting an Array. But remember ColdFusion arrays are not the same as .Net arrays. If you look at the wsdl carefully, you will notice that the "ArrayOfInt" is defined as a complexType name.
    <s:complexType name="ArrayOfInt">
    <s:element minOccurs="0" maxOccurs="unbounded" name="int" type="s:int" />

    And complex types in CF are mapped to Structures. Further, you will notice that this complex type has an element with name="int". Now when java looks at the wsdl and creates the stub class files, its renaming this element to "_int". My guess is that its doing this  because "int" is a reserved word in Java and also a native data type. So what this means is to successfully call the .Net webservice, you need:

    <cfset objGroupIds = StructNew()>
    <cfset objGroupIds._int = ListToArray("627303")>
    <cfset callResult = myObj.getUser(objGroupIds)>
Another great resource for understanding/ debugging ColdFusion webservices: