Connecting and using a Salesforce web service from Coldfusion

You need to use Coldfusion and Salesforce together via web services? That is crazy...but it works.

Salesforce.com and Coldfusion. It’s an interesting pairing, but one nonetheless that probably happens more often then you would think. But how does one connect to a Force.com web service?

Connecting to Salesforce from Coldfusion isn’t terribly hard. What you need is the Partner WSDL file from your particular instance of Salesforce. Since I’m not one for duplicating work, the Force.com web services API developers guide covers the topic and the steps needed to retrieve the WSDL.

Needless to say, lets look at an example. You can’t take this and run wild; you would need to make the struct work with your own target, as well as point it to proper WSDL’s and offer login details. Some things to note:

  1. The struct that you sent to your Force.com call needs to be perfect, otherwise you’ll be left shaking your head at a completely unhelpful “Web service operation could not be found” error.
  2. If you ever see “Unable to create web service argument class” this is generally because the struct is also wrong. Check your data and check the target types you should be sending.
  3. Coldfusion MX 7 tends to fight more then Coldfusion 8.
<!--- A pretend struct to send to our web service ---> <cfscript> mySentStruct = structNew(); mySentStruct.somethingF1 = "StringTest-01"; mySentStruct.somethingF2 = "StringTest-02"; mySentStruct.ohcomplex[1] = structNew(); mySentStruct.ohcomplex[1].somethingagain = "herewego"; mySentStruct.ohcomplex[1].ohcomplex02 = arrayNew(1); mySentStruct.ohcomplex[1].ohcomplex02[1] = "something,something"; </cfscript> <CFSET Application.isError = "false" /> <!--- Only one session should login at a time. ---> <CFLOCK timeout="10" scope="Application" throwOnTimeout="no" type="exclusive"> <!--- The SFDC login session should remain active for 30 minutes ---> <CFIF NOT IsDefined("Application.sfdc") OR Application.sfdc EQ "" OR NOT IsDefined("Application.lastLogin") OR (IsDefined("Application.lastLogin") AND Abs(DateDiff("n", Application.lastLogin, Now())) GT 30)> <!--- Create web service objects ---> <CFSET Application.sfdc = createObject("webservice","https://someurl.to.partner.wsdl/") /> <CFSET Application.sfdcyourservice = createObject("webservice","https://someurl.to.yourservice.wsdl") /> <CFSET loginResult = Application.sfdc.login("username","password") /> <!--- Create the SOAP Header that will contain the Session ID ---> <CFSET authHeader = createObject("java","org.apache.axis.message.SOAPHeaderElement").init("YourServicesService", "SessionHeader") /> <CFSET Application.sfdcyourservice.setHeader(authHeader) /> <!--- Add (and populate) a text node called sessionId: ---> <CFSET authHeader.addChildElement("sessionId").addTextNode(loginResult.getSessionId()) /> <!--- Change the endpoint URL to what was returned by the login method: ---> <CFSET Application.lastLogin = Now() /> </CFIF> </CFLOCK> <CFTRY> <!--- Only one session at a time should call this method ---> <CFLOCK timeout="10" scope="Application" throwOnTimeout="no" type="readOnly"> <CFSET sfdcResponse = Application.sfdcyourservice.createSomething(mySentStruct) /> </CFLOCK> <cfoutput>My SF Response: #sfdcResponse#</cfoutput> <!--- Catch any errors ---> <CFCATCH type="ANY"> <!--- Reset the sfdc session ---> <CFSET Application.sfdc = "" /> <CFIF NOT IsDefined("Application.isError") OR Application.isError NEQ "true"> <h1>An error occurred</h1> <h2>Error Details</h2> <CFDUMP VAR="#mySentStruct#" /> <CFDUMP VAR="#CFCATCH#" /> <CFSET Application.isError = "true" /> </CFIF> </CFCATCH> </CFTRY>

But just you say, what about “Cannot generate stubs errors”? Truly a pain in the neck. What I found that works the best in this situation is to use the CF admin API and reload the web service, which in turn usually regenerates the stubs without issue:

<cfset createObject("component","cfide.adminapi.administrator").login("yourCFadminpassword")> <cfset ws = createobject("component","CFIDE.adminapi.extensions")> <cfset ws.reloadWebService(name="https://someurl.to.yourservice.wsdl",path="https://someurl.to.yourservice.wsdl")> <cfdump var="#ws.getwebservices("https://someurl.to.yourservice.wsdl")#">

See, now that wasn’t so bad was it? Coldfusion may not be your first choice, but when it’s there and you can wrangle it, you can get it to connect to pretty much anything.