Practical Perforce

We use Perforce for source control here at work. Perforce is a little different than other version control systems I have used in the past (CVS, Subversion, VSS) so I decided to read O'Reilly's Practical Perforce to help bring me up to speed.

It was a huge help. Much like Open Source Development With CVS, this book goes beyond just command documentation and tutorials to really explain the Perforce view of the software development process. As I said, Perforce operates a little differently than other version control systems and learning Perforce's approach to the software development process really helped me understand why Perforce works the way it does.

[More]

Another CF Debug SQL Explorer Extension Update

Here is another update for the CF Debug Extension to the SQL Explorer Plugin. The extension wasn't properly handling yes/no true/false values for cf_sql_bit queryparams. This update fixes the issue.

To get the latest version of the extension click the download link at the bottom of this entry, between the send and del.icio.us links. (You may need to right click and "Save Link As...")

Web Services and Nulls

With all of the talk about null values today I thought I'd throw my hat in the ring and talk about one other area where null values can be an issue: web services. Some web services may expect null values for empty arguments and others may expect the empty argument to be left out of the request completely. Fortunately ColdFusion has a way of handling this via omit attribute of the <cfinvokeargument> tag.

[More]

Disabled Form Controls Cannot be Successful

I'm sure I probably knew this at some point, but for some reason it was a surprise to me this past week. Apparently disable controls are not submitted with HTML forms. I ran into this in the context of some AJAX work I've been doing. I noticed one of my form fields was not being submitted and I thought it may have had something to do with my AJAX JavaScript, but it turned out to be something much more basic... You (re)learn something new everyday!

WSDL2Java Batch File

The other day I posted about the WSDL2Java utility. Well, here is the code for a Windows batch file which will set up the proper classpath to run the Axis WSDL2Java utility.

@echo off

SET TMPCLASSPATH=%CLASSPATH%

SET CFROOT=C:\JRun4\servers\cfusion\cfusion-ear\cfusion-war\WEB-INF\cfusion\lib\

SET CLASSPATH=.

SET CLASSPATH=%CLASSPATH%;%CFROOT%axis.jar

SET CLASSPATH=%CLASSPATH%;%CFROOT%jaxrpc.jar

SET CLASSPATH=%CLASSPATH%;%CFROOT%saaj.jar

SET CLASSPATH=%CLASSPATH%;%CFROOT%commons-logging-1.0.2.jar

SET CLASSPATH=%CLASSPATH%;%CFROOT%commons-discovery-0.2.jar

SET CLASSPATH=%CLASSPATH%;%CFROOT%wsdl4j- 1.5.1.jar

SET CLASSPATH=%CLASSPATH%;%CFROOT%xercesImpl.jar

SET CLASSPATH=%CLASSPATH%;%CFROOT%log4j.jar

cls

java org.apache.axis.wsdl.WSDL2Java -v -o %1 %2

SET CLASSPATH=%TMPCLASSPATH%

To use just pass in an output directory and the WSDL URL.

WSDL2Java C:\output http://www.somesite.com/service.asmx?wsdl

It is based on one I saw here. You may need to change the CFROOT variable depending on your ColdFusion installation.

Update

Adobe has a couple of TechNotes on this subject with specific instructions for various CFMX versions and installations:

ColdFusion and ASP.NET Web Service Interoperability, Part III

Yesterday I described some of the problems I was having getting ColdFusion to talk to an ASP.NET web service as well as a brute force solution to the issue. Well my former co-worker Phil Duba left a comment on that post pointing to the follwing article: Notes on Interfacing ColdFusion MX to External Web Services Requiring Complex-within-Complex XML Documents as Input. This article turned out to be the key to my getting this web service call to work using <cfinvoke>.

[More]

ColdFusion and ASP.NET Web Service Interoperability (or Lack Thereof), Part II

Yesterday I was talking about some of the problems I had getting a service provider's ASP.NET client to work with my company's ColdFusion web service. Once we had that issue resolved the next step was to get our CFMX 6.1 application working with their ASP.NET web services.

Here is a sample SOAP 1.1 request and response for one of their web service methods. (This is from the ASP.NET auto generated documentation):

POST /service.asmx HTTP/1.1
Host: their.service.com
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://tempuri.org/Company_Add"

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Company_Add xmlns="http://tempuri.org/">
<txtCompanyName>string</txtCompanyName>
<txtCompanyAddress1>string</txtCompanyAddress1>
<txtCompanyAddress2>string</txtCompanyAddress2>
<txtCompanyZip>string</txtCompanyZip>
<txtCompanyFax>string</txtCompanyFax>
</Company_Add>
</soap:Body>
</soap:Envelope>

----------------

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Company_AddResponse xmlns="http://tempuri.org/">
<Company_AddResult>int</Company_AddResult>
<txtErrorMessage>string</txtErrorMessage>
</Company_AddResponse>
</soap:Body>
</soap:Envelope>

I originally tried to call this service using <cfinvoke> however I kept getting the following error:

Web service operation "Company_Add" with parameters {txtCompanyName={Test Company}...} could not be found.

I got this error no matter how I tried passing in the parameters so what I ended up doing is taking the brute force approach. I created a cfc which just wrapped <cfhttp>calls to the webservice. Below is an example of what I did.

<cfcomponent>

<cffunction access="public" name="int" output="false" returntype="mywebservice">
<cfargument name="URL" type="string" required="true">
<cfset instance = StructNew() />
<cfset instance.URL = arguments.URL />
<cfreturn this />
</cffunction>

<cffunction access="public" name="companyAdd" output="false" returntype="struct">
<cfargument name="companyName" type="string" required="true" />
<cfargument name="companyAddress1" type="string" required="false" default="" />
<cfargument name="companyAddress2" type="string" required="false" default="" />
<cfargument name="companyZip" type="string" required="false" default="" />
<cfargument name="companyFax" type="string" required="false" default="" />

<cfset var requestXML = "" />

<cfsavecontent variable="requestXML">
<Company_Add xmlns="http://tempuri.org/">
<txtCompanyName>#arguments.companyName#</txtCompanyName>
<txtCompanyAddress1>#arguments.companyAddress1#</txtCompanyAddress1>
<txtCompanyAddress2>#arguments.companyAddress2#</txtCompanyAddress2>
<txtCompanyZip>#arguments.companyZip#</txtCompanyZip>
<txtCompanyFax>#arguments.companyFax#</txtCompanyFax>
</Company_Add>
</cfsavecontent>

<cfreturn makeRequest("Company_Add",requestXML) />

</cffunction>

<cffunction access="private" name="makeRequest" output="false" returntype="struct">
<cfargument name="requestType" type="string" required="true" />
<cfargument name="requestXML" type="string" required="true" />

<cfset var httpXML = "" />
<cfset var cfhttp = "" />
<cfset var resultXML = "" />
<cfset var resultStruct = StructNew() />

<cfsavecontent variable="httpXML">
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>#arguments.requestXML#</soap:Body>
</soap:Envelope>
</cfsavecontent>

<cfhttp method="post" url="#instance.URL#">
<cfhttpparam type="header" name="SOAPAction" value="http://tempuri.org/#arguments.requestType#">
<cfhttpparam type="XML" value="#httpXML#">
</cfhttp>

<cfset resultXML = XMLParse(cfhttp.FileContent) />
<cfset resultStruct.result = resultXML.Envelope.Body['#arguments.requestType#Response']['#arguments.requestType#Result']['XmlText'] />
<cfset resultStruct.errorMessage = resultXML.Envelope.Body['#arguments.requestType#Response']['txtErrorMessage']['XmlText'] />

<cfreturn resultStruct />

</cffunction>

</cfcomponent>

I'm definitely not a SOAP expert and I don't know a whole lot about ColdFusion's underlying Apache Axis web service engine, so if anyone has other suggestions on how to handle this, or any insight whatsoever as to what the issue is I'd be glad to hear it.

ColdFusion and ASP.NET Web Service Interoperability (or Lack Thereof), Part I

For the past few weeks I've been doing some work with a third party service provider trying to integrate their offerings into an in-house application via web services. Their application is written in ASP.NET and ours is written in ColdFusion which has lead to some interesting challenges.

The first issue we had was getting their .NET application to talk to our ColdFusion web service. At first the provider reported that they were seeing the following error when calling our web service:

The underlying connection was closed: An unexpected error occurred on a send.

They were attempting to call our web service every ten minutes, however after looking in our web server logs I was able to determine that they had successfully connected to the web service only once. After doing some research I found a MS Knowledge Base Article which lead me to believe this error was due to a connection timeout. Our server's keep-alive setting was set at 5 minutes. If they were only calling our web service every 10 minutes the connection the client originally opened was being closed by our server. When I pointed out this article and described the issue the provider agreed to have their developers rewrite the calling code in ASP.NET 2.0, which apparently handles the keep-alive properly.

However once the client had been re-written, they reported a new issue. They were now seeing the following error:

The request failed with an empty response.

Just to confirm that the issue wasn't on our end I wrote a quick ColdFusion client which was able to call the web service with no problem. I looked at our logs again and could see that they were not even getting to our web service. This lead me to believe the issue was still in the calling code, but unlike the previous error google didn't turn up anything that seemed to point directly to the issue. At this point I decided to download VB 2005 Express and build a .NET client myself. Having done zero .NET development it took me a few minutes to figure out how to connect to a web service via VB .NET, but once I did it was fairly easy.

In Visual Studio .NET you create a Web Reference to work with web services. Adding a Web Reference is a lot like adding a COM, DLL, or ActiveX reference to you project; basically you just click on Project -> Add Web Reference and type in the URL of the web service you want to work with in the Web Reference window and you are all set. Well almost. Our web service need to be called overSSL so the URL I entered for our Web Reference looked something like "https://our.service.com/component.cfc?wsdl." However whenever I tried to call the web service in my VB client I was getting the same "empty response" error our service provider was reporting. So, I fired up a network sniffer (Ethereal) to see if I could figure out what was going wrong. It turns out that even though I was typing in "https://our.service.com/component.cfc?wsdl" for my Web Reference the VB.NET client was calling "http://our.service.com/component.cfc?wsdl." Security in our application was then trying to redirect the client to the secure URL via <cflocation>, hence the empty response. Now that I knew what the issue was I just had to figure out how to fix it. Turns out it was pretty simple. The VB.NET project has an app.config file which holds, you guessed it, application configuration. One of the entries in this file was the web service URL and upon inspection I found that it was listed as "http://our.service.com/component.cfc?wsdl." I manually edited this entry to use https instead of http and that did the trick. I then notified the provider and they made the corresponding change on their end an we were in business.

This has turned into a bit of a .NET post, but I thought it was worth sharing in case you have others trying to consume your ColdFusion web services via .NET. Up next, what we had to do to get ColdFusion to talk to the service providers .NET web services.

Araxis Merge For OSX

Every since I started using source control one of my favorite utilities on Windows has been Araxis Merge. It is certainly one of the better diff/merging applications I have used, plus it does folder comparisons and synchronization which is very handy when trying to sync up files on different servers. Since switching to the Mac I've been looking for something comparable and today I was happy to find out that my search may soon come to an end. I've just downloaded the early-access test release of Araxis Merge for Mac OS X. I haven't really put it through its paces yet, and according to the download page it is still a little rough around the edges, but so far it looks pretty sweet. If it ends up being anything like the Windows version I'll be one happy camper.

If your looking for an awesome diff/merge application for Windows or Mac definitely check out Merge. It isn't free, but I've found it well worth the money. (You can download a Windows trial from the website. If your looking for the early-access test release for the Mac you best bet is to contact Araxis via their website.)

BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.