I have been a lot of work with SOAP recently, consuming 3rd party web services written in JAVA and .NET. And I have come to the conclusion that ColdFusion really does suck when it comes to this particular area.
why do I say this, well basically I feel that Adobe have tried to be too clever with the web services and rather than make it easier for CF to work with SOAP they have in fact made it more of a PITA.
- When you try to consume a web service, the first thing that CF does it download the WSDL and create JAVA stubs for all the methods. This can be the start of your nightmares. Any errors in the WSDL, or anything that JAVA doesn't like, and the stub files will not be created and you will not be able to consume that web service. You also will not get back any kind of remotely useful error message either. So unless the 3rd party is prepared to modify their web service (unlikely) then your screwed. Also the initial request will be very slow while the stubs are generated, and any changes to the WSDL will not be picked up unless you delete the web service definition from the CFADMIN.
Here is an example of the type of error you might get when the stubs cannot be created.coldfusion.jsp.CompilationFailedException: Errors reported by Java compiler: Found 3 semantic errors compiling "/opt/jrun4/servers/cfusion/cfusion-ear/cfusion-war/WEB-INF/cfusion/stubs/WS888823651/fi/atex/www/namespace/Atex/Web/Advertising/Logo/LogoServiceLocator.java": 53. return _stub; <---> *** Error: The type of this return expression, "fi/atex/www/namespace/Atex/Web/Advertising/Logo/LogoSoapBindingStub", does not match the return type of the method, "fi/atex/www/namespace/Atex/Web/Advertising/Logo/Logo". 74. return _stub; <---> *** Error: The type of this return expression, "fi/atex/www/namespace/Atex/Web/Advertising/Logo/LogoSoapBindingStub", does not match the return type of the method, "java/rmi/Remote". 94. return getLogo(); <-------> *** Error: The type of this return expression, "fi/atex/www/namespace/Atex/Web/Advertising/Logo/Logo", does not match the return type of the method, "java/rmi/Remote". Found 1 semantic error co....
- Creating a SOAP request in CF can also be a total nightmare as you do it with arrays and structures and CF then tries to convert it into SOAP when you invoke the service. Simple variables are no problem, but when you need to create a request comprising of complex nested variable types, the fun and games begins, as CF just wont produce the required SOAP request in the required format.
E.G. Here is part of a required SOAP request, generating this in CF as nested structures does not produce the desired result due to the fact it is a nested set of complex variables.<insertions>
<item xsi:type="ns2:insertion">
<adContent xsi:type="ns2:adContent"><color xsi:type="ns2:color" xsi:nil="true"/>
<id xsi:type="xsd:int">0</id>
<messages xsi:type="soapenc:Array" xsi:nil="true" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"/>
<overmatter xsi:type="xsd:boolean">false</overmatter>
<proofURL xsi:type="xsd:string" xsi:nil="true"/>
<size xsi:type="ns2:size">
<depth xsi:type="xsd:double">30.0</depth><depthUnit xsi:type="xsd:string">mm</depthUnit>
<width xsi:type="xsd:double">31.0</width><widthUnit xsi:type="xsd:string">mm</widthUnit>
</size>
<styleId xsi:type="xsd:int">509</styleId>
<text xsi:type="xsd:string">uploadFile26863.tmp</text>
</adContent>
</item>
<item xsi:type="ns2:insertion">
<adContent xsi:type="ns2:adContent"><color xsi:type="ns2:color" xsi:nil="true"/>
<id xsi:type="xsd:int">1</id>
<messages xsi:type="soapenc:Array" xsi:nil="true" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"/>
<overmatter xsi:type="xsd:boolean">false</overmatter>
<proofURL xsi:type="xsd:string" xsi:nil="true"/>
<size xsi:type="ns2:size">
<depth xsi:type="xsd:double">30.0</depth><depthUnit xsi:type="xsd:string">mm</depthUnit>
<width xsi:type="xsd:double">31.0</width><widthUnit xsi:type="xsd:string">mm</widthUnit>
</size>
<styleId xsi:type="xsd:int">509</styleId>
<text xsi:type="xsd:string">uploadFile26863.tmp</text>
</adContent>
</item>
</insertions> - When providing your own web service for others to consume, the SOAP response created by CF is not very developer friendly at all. CF tends to generate xml attributes instead of tag entities.
In all the above situations you are better of manually creating your own SOAP request/response and sending this off the old fashioned way via a cfhttp post, which is in fact also quicker as you are bypassing the java stub files.
Below is an example of manually consuming a web service with your own hand crafted SOAP request. To generate and test your SOAP requests I recommend a handy tool called SOAP UI which will read your WSDL and create a default request for each method saving you a lot of time. All you need to do is fill in the data and submit the request, wheny ou have it working, just paste the request into your CF code and off you go.
<cfsavecontent variable="localscope.soapRequest">
<cfoutput>
<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>
<MyMethod>
<userContext xsi:type="ns1:userContext">
<name xsi:type="xsd:string">USERNAME</name>
<password xsi:type="xsd:string">PASSWORD</password>
</userContext>
<MyItem>
<userGroup xsi:type="xsd:string" xmlns="">1</userGroup>
<transactionType xsi:type="xsd:string" xmlns="">New</transactionType>
<adType xsi:type="xsd:string" xmlns="">L</adType>
<channel xsi:type="xsd:string" xmlns="">WEB</channel>
</MyItem>
</MyMethod>
</soapenv:Body>
</soapenv:Envelope>
</cfoutput>
</cfsavecontent>
<cfhttp url="webServiceURL?wsdl" method="POST" resolveurl="NO" useragent="Axis/1.1">
<cfhttpparam type="header" name="SOAPAction" value="#WSURL#MyMethod">
<cfhttpparam type="xml" name="body" value="#localscope.soapRequest#">
</cfhttp>
<cfset localscope.soapresponse = XMLParse(cfhttp.FileContent) />
Recent Comments