Wednesday, July 02, 2008

Using WSDL2Java to figure out CFML arguments to a Web Service

For some reason I haven't actually even written up the procedure I use to figure out how to invoke a web service from ColdFusion when people ask me what CFML structures they need to pass in so CF can match the arguments to a function in the Apache Axis stub it generated from the WSDL.

I encourage you to read the reprint of Consuming Web Service complex types in ColdFusion which goes in to some great detail on how to figure this out. This posting is going to be the cheat sheet version of that.

First, if you can't figure out why CF gives you an error about not finding the right function to invoke, you probably aren't passing in the right arguments. How do you figure these out? You run the WSDL2Java command in Apache Axis, and take a look at the Java code CF is trying to invoke. Here's a batch script for Windows and ColdFusion 8:

set CFLIB=c:\ColdFusion8\lib
set CLASSPATH=%CFLIB%\axis.jar;%CFLIB%\wsdl4j-1.5.1.jar;%CFLIB%\log4j-1.2.12.jar;%CFLIB%\commons-logging.1.0.4.jar;%CFLIB%\commons-discovery-0.2.jar;%CFLIB%\xercesImpl.jar;%CFLIB%\xml-apis.jar;%CFLIB%\saaj.jar;%CFLIB%\jaxrpc.jar;%CFLIB\..\runtime\lib\jrun.jar

java org.apache.axis.wsdl.WSDL2Java %1 %2 %3 %4 %5 %6 %7 %8 %9


Save this as "wsdl2java.bat". This will also work with ColdFusion MX 7. You will need to change the log4j-1.2.12.jar file to be just log4j.jar.

Change to an empty directory. Wsdl2Java is going to spew directories and files all over the current directory, so I find its best to create something like c:\temp\x and run the command from there. Otherwise you can give the -o option and specify the output directory.

wsdl2java.bat -v -o c:\temp\x http://example.com/path/to/wsdl

The -v is the verbose argument. I like to see all the files that are being created.

Now you can start browsing through the Java files and see what we have to work with. Look for a file named the same as the "portType" name in your WSDL. For instance, if your WSDL contains a portType that looks like this:
<wsdl:portType name="CodexWS">

Then you would look for CodexWS.java. This will be the interface of the service, and you should be able to see each of the operations in your service, and the Java objects that ColdFusion will be trying to create to call them.

How do you create the CFML to pass to the operation? Simple. Most likely there will be JavaBeans created from the XML Schema in the WSDL. These aren't that big of a deal, basically they are structures with name/value pairs, which is why you will need to make a CFML structure with the same names in it.

Check out this Bean:

public class AddThingRequest implements java.io.Serializable {
private java.lang.String manifestfile;
private java.lang.String uri;
private java.lang.String certlevel;
private java.lang.String statusname;
private com.adobe.Ldapcredentials ldapcredentials;
...

There would be a getManifest() and setManifest() function in the bean also, along with a get and set function for each one of the other bean properties. These only matter because this is what CF will look for when it finds a "manifest" entry in your CFML structure. So what would we pass in to an operation that takes an "AddThingRequest" object as an argument? Something like this:

atr = StructNew();
atr.manifestfile = "foo";
atr.uri = "uri:gimme";
atr.certlevel = "high";
atr.statusname = "Alert";
art.ldapcredentials = mycreds;

But wait, you say, what is "mycreds"? Well, the process just repeats here - we would go find the com.adobe.Ldapcredentials Java object, look at the properties it contains and make a CFML structure (mycreds in this example above) that matches it.

So that's all (ha!) there is to it. You now have the magic to invoke almost any web service out there. This isn't exactly "making the hard stuff easy" (that would be that WSDL2CFML tool I haven't written yet), but once you understand how the process works, you should very rarely have to resort to grunging through the Java code generated from a WSDL to construct the right CFML inputs. My opinion is that Web Services should be designed to present an easy to use interface, simple inputs with only reasonably complex outputs. I know that isn't the case for many services out there that you are forced to used. But with this technique you should be able to easily figure out how to use the more complex ones.

4 comments:

Oliver Merk said...

Wouldn't the Eclipse ColdFusion plugin's Services Browser give you all you need?

jwhite1202 said...

If you are using eclipse Europa or you have the JEE perspective loaded on your eclipse you can use the Java2WSDL functionality without using the command line. The cmd line is OK but I prefer the GUIs.

Tom said...

@oliverm You can certainly use the Services browser that is included in the ColdFusion 8 plugins for Eclipse. That is an excellent idea. However, it might not help in cases where a Schema type turns in to an Axis Java object like UnsignedInt, see my next blog entry.

@jwhite1202 I am not sure if you meant the WSDL2Java functionality in the JEE perspective but what we are trying to see is the structure of the Java object that CF will be using, not the WSDL generated from existing Java classes.

StevenInc said...

Here is the updated batch file for CF9:
set CFLIB=C:\ColdFusion9\lib
set CLASSPATH=%CFLIB%\axis.jar;%CFLIB%\wsdl4j-1.5.1.jar;%CFLIB%\log4j-1.2.15.jar;%CFLIB%\commons-logging-1.1.1.jar;%CFLIB%\commons-discovery-0.4.jar;%CFLIB%\xercesImpl.jar;%CFLIB%\xml-apis.jar;%CFLIB%\saaj.jar;%CFLIB%\jaxrpc.jar;%CFLIB\..\runtime\lib\jrun.jar

java org.apache.axis.wsdl.WSDL2Java %1 %2 %3 %4 %5 %6 %7 %8 %9