Friday, November 2, 2012

Initiating web service objects using class files

If you want to call a webservice which uses more complex arguments then it's easier (or even best practice) to use class files in stead of recreating the objects by yourself. Here is how:

  1. Open your ColdFusion9/stubs/ folder.
  2. Make a call to the webservice using createObject:
    someWs = createObject("webservice", "http://addressToWsdlFile/?wsdl");
  3. Inspect the stubs folder and find the subfolder which is newly created, open it.
  4. There should be a whole subfolder structure which contains eventually some .class files. Copy the root (normaly this is something like a top level domain: com or net or such)
  5. Paste the folder structure to folder which is noted as a class path folder, or make a new folder and put this in the ColdFusion administrator (Server Settings > Java and JVM > ColdFusion Class Path) or in your server.cfc class path settings
  6. Restart ColdFusion (do not forget this!)
  7. Now you can create one of the objects needed in the webservice using:
    someObject = createObject("java", "com.test.testapp.testObj);
    (the dot notated path is corresponding with the copied folder structure to the class files)
  8. The objects should have all the getters and setters. (Use writeDump() (or cfdump) to see all the properties of the classes). So use someObject.setSomeValue("xxx"); to populate.
  9. Call the method with the generated object:
    result = someWs.someMethod(someObject);
  10. Done!


Using SSL with ColdFusion howto

Here is a writeup about how to setup ColdFusion 9 to use SSL. I will describe the steps I needed to take to connect to a site that uses https.

1: inspect the site and download the certificate

Go to the site and inspect the certificate. I use Google Chrome, so I clicked on the lock icon next to the address in the adress bar. Then click on the certificate details.
Click on the button "copy to file" to export the certificate. Choose "Base-64 encoded X.509 (.CER) and choose the filename and location.

2: enable SSL in Jrun.xml

Open the Jrun.xml and search for "SSLService". Here you will see a commented section where all the setting about SSL are. Set the port to the right value (443 is the standard port for ssl), set the right location for keyStore and trustStore. My values are "{jrun.rootdir}/lib/security/cacerts" and "{jrun.rootdir}/lib/security/trustStore". Not sure where the jrun.rootdir points to? Look in the ColdFusion Administrator in "system information" > "Java Home".

3: import the certificate

Here you'll have two options: use the gui in the ColdFusion administrator http://certman.riaforge.org/ (which is the easiest), or import the certificate using the JRE keytool.
You have two keystore files: cacerts and trustStore, the cacerts should only contain the root certificates from the Certificate Authorities (CA) and the trustStore should contain the trusted certificates in which you should import the certificate of the server you wish to connect.

If you use the CFadmin plugin, open the KeystoreManager.cfc and set the KeyStorePath if it is not the default setting and the password (the default settings should be fine). In the init function you can change the file of the keystore which you specified in the jrun.xml in step 2.

If you use the keytool file, open a command prompt and go to the bin folder of your CF jre. There you can find the keytool.exe file.
Make a keystore named trustStore (if it does not exist) by generating a server certificate:

keytool -genkey -v -alias cf -dname "cn=localhost" -keystore trustStore -keypass changeit -storepass changeit
Alias (here set to "cf") is the short name for the certificate, trustStore is the name of the keystore (you should use an absolute path to be shure where the keystore file is written to, the keypass and storepass are the passwords to secure the certificate and the keystore (changeit is default, so it's advised to change this, but be shure it is the same as what is set in the jrun.xml file in step 2).
Import the certificate of the remote server to the trustStore:

keytool -import -v -alias savedCertAlias -file savedCert.cer -keystore trustStore
savedCertAlias is the short name of the to be imported certificate, savedCert.cer is the file of the certificate you saved in step 1 (use an absolute path to be sure you will import the right file), strustStore is the file of the keystore you will save the certificate to (use an absolute path). Consider the -trustcacerts option if you want to trust the whole tree.
List the contents of the keystore or truststore:
keytool -list -keystore keystorefile

4: Enable jvm debug output (optional)

This step is optional, but great to see if everything is working. Add the folowing line to the JVM arguments in the CF administrator:
-Xdebug -Djavax.net.debug=all

This will save all sorts of handy debug output to runtime\logs folder coldfusion-out which comes in handy to see how the SSL connection works (or not).

5: Restart ColdFusion

For all the indivdual steps a restart of the ColdFusion instance is needed so this is a good moment to do so. (changed jvm.xml, added keys to keystore, changed jvm arguments).

6: Check the SSL connection

Make a call to the server which you want to connect to:
<cfhttp 
url="https://someSSLEnabledServer/index.php?someargs=1"
charset="utf-8" 
method="post"
port="443"
result="test" >
If you enabled jvm debug output (step 4) you can see the SSL connection results in the -out log file.

7: Debug!

If everything is working, congratulations! Be sure to disable the debugging because one ssl connection can generate a few Kb of logging information in one call.
If the call fails you should get some hints in the log file, do not trust the coldfusion exceptions you get in your browser, they are not detailed enough to see what is going on. Here are some pointers to what might go wrong:

Keystore or trustStore not found: be sure the files exist and the paths are correct in the jvm.xml. Change the paths and/or generate the keyStore

Not all the CA's of the certificate are trusted by the keystore: Open the imported certificate file and check the certification path. Check all the CA's in the certification path and be shure that all the certificates are either in the keystore or the trustStore. If not then import them to the trustStore (repeat the steps 1-3) and/or consider the -trustcacerts option (see keytool manual).

No CA's are in the trustStore: On one CF server I found a keyStore with only three CA records. You can import all the CA's, but it might be easier to see if your JRE has an updated file.

Certificate or one of the CA's are expired: check if all the certificates in the chain are still valid and updated in your keystores.

Note: This description assumes that only one way ssl signing is needed, if the SSL server requires a certificate from the client you should generate a valid certificate and import it in the keystore. The keytool is limited to X509 v1 generation.If you need X509 v3 then you should use openSSL or another capable tool. Use a tool that is capable of importing private keys, the keytool is not capable to do this.

Here are some references I used:
Tool to import certificates: http://certman.riaforge.org/
Jrun Help / Import certificates | Certificate stores | ColdFusion: http://helpx.adobe.com/coldfusion/kb/import-certificates-certificate-stores-coldfusion.html
Debugging SSL/TLS Connections:  http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/ReadDebug.html
Search for SSL in: "Configuring and administering ColdFusion"


Wednesday, June 27, 2012

Weird thing in for loop in ColdFusion

I was making a simple XML iteration in script using a for loop, but it only returned one element in stead of all the elements. In a further inspection I discovered a typo which was not reported by ColdFusion.
Here is the for loop:

for (sCount=1; sCount<+arrayLen(xmlSomething["anotherElement"]); sCount++){

}

The problem is the <+ operator which should be <=
But no error report, just the first element that is returned. I wonder why!

Monday, June 18, 2012

Using different axis types in webservices (like unsigned int)

This is just a reminder for myself:
I read a great article about using the different datatypes that are not supported in ColdFusion (and not through JavaCast()). The way to go is to use the underlying axis object:
duration = CreateObject("java", "org.apache.axis.types.UnsignedInt").init(60);
In the example the variable "duration" is of type "UnsignedInt" and has the value 60. You can also do this in XML (see my previous post), but this is more elegant in my opinion.

Friday, June 15, 2012

Question: how to throw an error in a ColdFusion web service?

I've been trying to solve this promblem for some time now, but it seems there is no simple solution for this:
When you look at your webservice WSDL you have three operation parts: input, output and fault. The fault type of a generated ColdFusion WSDL is "CFCInvocationException". This is fine for most purposes, but not in my case. For example: I want to validate the input of the web service call to see if everything is correct before executing the method. Let's say you have a service to order something, then you want to authenticate the user, check the amount, and see if the product is available. If something is not correct then I want to return a fault.
In the service you can set the HTTP status code to 500 by using getPageContext().getResponse().setstatus(500);
But I still want to return a fault code and a fault message. If I generate this and return it in the body it will be returned in the SOAP output part, and not in de fault part. If I use "throw" to return the error, then it will be added to the "CFCInvocationException" and not my own fault type.
throw (message="some",type="urn:errorType",errorCode="999");

Does anyone have a solution to return a valid custom web service error (that is defined in the service WSDL) that will not be overrided by the ColdFusion fault handler?

UPDATE:
I haven't yet looked into CF11 so I don't know if this is fixed in that version, but I read a solution which isn't very elegant, but works. In short: you have to give the webservices their own application.cfc and use the onError function to catch the ColdFusion generated exception and replace the content by soap XML. A sample is described here: http://laksmatee.blogspot.nl/2014/02/half-baked-soap-support-in-coldfusion-9.html

Returning a complex object with specfic data types in a ColdFusion web service

Here is a folow-up on my previous post about returing an integer in a webservice. Most webservices return more then one value. In this example I will return a structure with a firstname and lastname:

<cfcomponent output="false">
   <cffunction name="giveMeAPerson" output="false" access="remote" returnType="person">
      <cfset var stReturn = createObject("component","person") />
      <cfset stReturn.firstName = "John" />
      <cfset stReturn.lastName = "Doe" />
      <cfset stReturn.age = "30" />
      <cfreturn stReturn />
   </cffunction>
</cfcomponent>

The returnType="person" points to person.cfc which is a value object with three properties: firstName, lastName and age with the according data type (string and numeric). (If the person.cfc is in a different folder than the web service cfc, then you must use the dot delimited path in the returnType field.)

If you want to use a specific data type for every struct key you will have to use XML. If you want the web service consumers to be able to fully use the different data types you have to specify the data types in the WSDL file. Save the generated WSDL to a file and point to this file in your component (see my previous post for the details on this). Next, edit the WSDL where the returnType is specified. Here I will change the data type "double" of the property "age" to "int". Next I will set the returntype of the function "giveMeAPerson" to "any".
Now it is time to construct the XML that has to be returned:

var XmlResponse = XmlNew();
XmlResponse.xmlRoot = XmlElemNew(XmlResponse,"urn:pointerToURN","giveMeAPersonResponse");
XmlResponse.xmlRoot.XmlAttributes['xsi:type'] = "giveMeAPersonResponse";
XmlResponse.xmlRoot.XmlChildren[1] = XmlElemNew(XmlResponse,"urn:customUrn","firstName");
XmlResponse.xmlRoot.XmlChildren[2] = XmlElemNew(XmlResponse,"lastName");
XmlResponse.xmlRoot.XmlChildren[3] = XmlElemNew(XmlResponse,"age");
XmlResponse.xmlRoot.firstName.XmlAttributes['xsi:type'] = "firstname";
XmlResponse.xmlRoot.lastname.XmlAttributes['xsi:type'] = "xsi:string";
XmlResponse.xmlRoot.age.XmlAttributes['xsi:type'] = "xsi:int";
XmlResponse.xmlRoot. firstName .XmlText = "John";
XmlResponse.xmlRoot.lastname.XmlText = "Doe"; 
XmlResponse.xmlRoot.age = JavaCast("int",30);

Then return the XmlResponse.

Note: In this example I used a custom data type for firstName. I did this to show you what to change to implement this. The custom data type must be defined in the WSDL under the given URN.
The conversion to int and string is not necessary, if you describe the type in the WSDL then ColdFusion will do the conversion.


Returning an integer (or other data types) with a ColdFusion web service

ColdFusion is a webserver language which is easy to learn and makes it easy to quickly achieve complex operations. This is all very nice, but can get in the way if you want to do something in a specific way. This problem arises when you have to deliver a webservice that returns a specific data type.
Here is a webservice which returns a number:
<cfcomponent output="false">
   <cffunction name="giveMeANumber" output="false" access="remote" returnType="numeric">
      <cfreturn RandRange(0,10) />
   </cffunction>
</cfcomponent>

If you call the web service in a different language than ColdFusion, or in a specific web service tool (like SoapUI) you will see that the returned value will be something like "5.0". This will be no problem for ColdFusion, but this may come as a problem for PHP or .Net. That's why I want the service to return "5" in stead of "5.0".
If you look at the webservice WSDL you will see that the webservice returns a double:

<wsdl:message name="giveMeANumberResponse">

<wsdl:part name="giveMeANumberReturn" type="xsd:double"/>
</wsdl:message>

Save the whole WSDL content to a text file and be sure that it is XML compliant (ie. no leading spaces before the XML tag). Now you can change the data type of the return value:

<wsdl:message name="giveMeANumberResponse">

<wsdl:part name="giveMeANumberReturn" type="xsd:int"/>
</wsdl:message>

Next, change the component tag to use the external wsdlfile:
<cfcomponent wsdlfile="myExternalwsdlfile.wsdl" style="document" output="false">
If you point to an external WSDL file you have to set style="document".

Next, set the returntype of the function to "any", this is not required for int (double or int are both numeric), but it is best practice because it will be required for other data type.
<cffunction name="giveMeANumber" output="false" access="remote" returnType="any">

Next, use Javacast around the return value to set the right data type, in this case: <cfreturn JavaCast("int",RandRange(0,10)) />  

To see which data type in ColdFusion will be returned with which data type (and if it may cause problems, see the ColdFusion reference "Data conversions between ColdFusion and WSDL data types".

Wednesday, May 30, 2012

Coldfusion, SOAP headers and custom namespaces

Some webservices require you to add specific headers to your request (i.e. for authentication). In the CFML reference you can find a function AddSOAPRequestHeader which can do this for you. The problem is that the documentation is not always very clear about HOW you should use this.
My problem was that the header should use some custom namespaces and some default namespaces, here is how you should do this.
My SOAP header should look like this:

<soapenv:header>
<ws:authenticationheader>
<username>[username here]</username>
<password>[password here]</password>
</ws:authenticationheader>
</soapenv:header>

There is a namespace "ws" defined in the soapenv:Envelope pointing to a custom namespace. It is still (as far as I could research) impossible to define this in the soapenv. Second best is to define this in the header itself.

XmlHeader = XmlNew();
XmlHeader.xmlRoot = XmlElemNew(XmlHeader,"http://www.url-to-ns","ws:AuthenticationHeader");
XmlHeader.xmlRoot.XmlChildren[1] = XmlElemNew(XmlHeader,"","username");
XmlHeader.xmlRoot.XmlChildren[2] = XmlElemNew(XmlHeader,"","password");
XmlHeader.xmlRoot.username.XmlText = "[username here];
XmlHeader.xmlRoot. username .XmlAttributes['xsi:type'] = "xsd:string";
XmlHeader.xmlRoot.Password.XmlText = "[password here]";
XmlHeader.xmlRoot.Password.XmlAttributes['xsi:type'] = "xsd:string";
AddSOAPRequestHeader(varnameOfWS," http://www.url-to-ns ","ws:AuthenticationHeader",XmlHeader,false);
result = varnameOfWS.doSomething();

From the CF documentation:
XmlElemNew(xmlObj[, namespace], childName)
The trick is to add the namespace alias to childName parameter using the format "[alias]:childname", the namespace alias then is automatically added.

Here is the soap header using soap_req = getSOAPRequest(varnameOfWS);


<soapenv:Header>
<ws:AuthenticationHeader 
soapenv:actor="" 
soapenv:mustUnderstand="0" 
xmlns:ws="http://www.url-to-ns">
<username xmlns="" xsi:type="xsd:string">[username here]</username>
<password xmlns="" xsi:type="xsd:string">[password here]</password>
</ws:AuthenticationHeader>
</soapenv:Header>


The namespace definition is valid for the web service which was my goal. I would rather see the definition in the soapenv:Envelope so if anyone knows how to do this please let me know! :)

Wednesday, April 4, 2012

A pinout database of any plug

This is a great initiative: http://pinouts.ru/
I have an old computer power supply lying around which I want to repurpose for a DIY electronics project. But which pin is what? I can try my volt meter on every pin, but using this site is much easier!

This site contains a database with more than 1400 descriptions of hardware pinouts, cables, connectors and even some schemes. Everything is nicely categorized and searchable. You can even submit your own pinouts if you think something is missing!