Archive for the ‘Web Services’ Category

Web Services Atomic Transactions in ColdFusion

June 20, 2014

In order to be able to perform web services atomic transactions, we need to run the ColdFusion code
on a Java Application Server that supports WS-AT. Obviously the Tomcat that ships with ColdFusion does not
support it (out of the box). One could try to plugin the Atomikos transaction manager, but for the purpose of
this test, we decided to deploy ColdFusion on WebLogic and benefit from the WS-AT support that is offered by WebLogic.

In the example below, we will wrap 2 web service invocations in a transaction managed by WebLogic’s transaction manager.
The methods persistDetachedContact in WebServiceA and removeDetachedContactDetail in WebServiceB both require WSAT10,
which means that they can not be invoked if there is no transaction context.

// load webservice classes
wsclassA = CreateObject(“java”,”eu.alrighty.marbie.WebServiceA”);
wsclassB = CreateObject(java”,”eu.alrighty.marbie.WebServiceB”);

// load web service wsdl’s
wsdlUrlA = wsclassA.getClass().getClassLoader().getResource(“META-INF/wsdls/WebServiceAService.wsdl”);
wsdlUrlB = wsclassB.getClass().getClassLoader().getResource(“META-INF/wsdls/WebServiceBService.wsdl”);

// prepare transactional feature
feature = CreateObject(“java”,”weblogic.wsee.wstx.wsat.TransactionalFeature”);
ta = CreateObject(“java”,”weblogic.wsee.wstx.wsat.Transactional”);
flowtype = CreateObject(“java”,”weblogic.wsee.wstx.wsat.Transactional$TransactionFlowType”).SUPPORTS;
version = CreateObject(“java”,”weblogic.wsee.wstx.wsat.Transactional$Version”).WSAT10;
feature.setFlowType(flowtype);
feature.setVersion(version);

// get webLogic Transaction Manager
ctx = CreateObject(“java”,”javax.naming.InitialContext”);
tx = ctx.lookup(“java:comp/UserTransaction”);

// instantiate webService stubs through wsdl’s
wsa = CreateObject(“java”,”eu.alrighty.marbie.WebServiceAService”).init(wsdlUrlA);
wsb = CreateObject(“java”,”eu.alrighty.marbie.WebServiceBService”).init(wsdlUrlB);

// start the transaction
tx.getTM().begin();

// find a contact person from webService A, and create a new contact person with it (increase Id);
contacts = wsa.getWebServiceAPort([feature]).findAllContacts();
contact = contacts[ArrayLen(contacts)];
contact.setId(IncrementValue(contact.getId()));
writeDump(contact.getId())

// find a contact detail from webService B, and create a new contactdetail with it (increase Id);
contactdetails = wsb.getWebServiceBPort([feature]).findAllContactdetails();
contactdetail = contactdetails[ArrayLen(contactdetails)];
contactdetail.setId(IncrementValue(contactdetail.getId()));
writeDump(contactdetail.getId());

// add contact to webService A
writeDump(wsa.getWebServiceAPort([feature]).persistDetachedContact(contact));
// add the contact detail to webService B
writeDump(wsb.getWebServiceBPort([feature]).removeDetachedContactDetail(contactdetail));

// (optionally) dump some information on the status of the transaction
writeDump(tx.getTM().getTransaction().getStatus());

// commit the transaction
tx.getTM().commit();

In the code above, a contact person is added to a database through web service A, running on a
WebLogic A. Secondly, a (non existing) contact detail is removed from a database through web service B, running on a
WebLogic B. Since the second command can not be executed, the first one is properly rolled back, and the test
ends leaving the data in the database to its’ original state.

(Thanks to Petar Banicevic for helping me out with the weblogic transaction manager).

Retrieving a MIME-encoded binary attachment through MTOM with ColdFusion 10

March 6, 2013

Setup:
A Oracle Weblogic (BEA) Web Service returning mime-encoded binary documents (word or pdf) in attachment through MTOM, matching the metadata specified in the request.
A client running on ColdFusion 10, using the automatically generated stub from the WSDL of the Web Service using axis2.

To start, we have ColdFusion generate the stub by invoking the WSDL:

variables.ws=createObject("webservice","http://testserver/CarsDocContentServiceWS/CarsDocContentServiceWSPort?WSDL",{wsversion="2",refreshwsdl="false",timeout=500});

Iomportant: we need to specify wsversion = 2 to force ColdFusion to use axis2. Refreshwdl can be false, we only want the stub to be generated once. Regenerating it each time is time-consuming.
To see what we get back from the Web Service it is always a good idea to dump it:

writeDump(variables.ws);

dump of the webservice and its methods

The method we are looking for is getmanifestationcontent. The first 8 variables are the metadata we need to provide, the last 5 are returned by the Web Service. I create a placeholder for them.


variables.result=StructNew();
variables.result.filenameOut='';
variables.result.errorMessage='';
variables.result.errorCode='';

All set up to invoke the method on the webservice:


variables.response=variables.ws.getmanifestationcontent('MYACC','ST',JavaCast("bigdecimal",5007),JavaCast("bigdecimal",2012),'INIT','EN','INTRA','PDF',JavaCast("bigdecimal",1),variables.result.filenameOut,dhl,variables.result.errorMessage,variables.result.errorCode);

Some of the metadata we passed in had to be casted to the expected datatype. The one oddity in this particular webservice was the ‘dhl’ variable that matches the expected javax.activation.DataHandler data type. Since this is a returned argument, I expected it would be enough to pass just the name of an empty variable and the stub would automatically return a DataHandler object. But the webservice refused to accept any null valued arguments, so I had to build a empty DataHandler object in which even the DataSource had to be specified, even if it would never be used on the Web Service. So I added these 2 lines of code prior to the invocation of the Web Service.


fds=CreateObject("Java", "javax.activation.FileDataSource").init('c:\temp.pdf');
dhl=CreateObject("Java", "javax.activation.DataHandler").init(fds);

Time to look at the response of our request. I would have expected the response to be in the DataHandler variable dhl. But this was not the case. The attachent was in the response of the invocation of the getmanifestation method itself.
So, again, I dumped it to see what I was dealing with.

writeDump(variables.response);

manifestationcontent response

Clearly, the filename was in the get_FileName_out() method:

saveToFileName="c:\"&variables.response.getFileName_out();

Now the only thing left is to get the binary attachment and I saved it locally to the disk. The getFileContent_out() contained the getInputStream() i was looking for. So I could loop through that until the end-of-file and stream it to the local disk:


inputStream=variables.response.getFileContent_out().getInputStream();
outStream=createObject("Java","java.io.ByteArrayOutputStream").init();

byteClass=createObject("Java","java.lang.Byte").TYPE;
byteArray=createObject("Java","java.lang.reflect.Array").newInstance(byteClass, javacast("int", 1024));
length=inputStream.read(byteArray);
offset=0;
while (length GT 0) {
outStream.write(byteArray,offset,length);
length=inputStream.read(byteArray);
}
outStream.close();
inputStream.close();
FileWrite(saveToFileName,outStream.toByteArray());

While dumping the objects returned from the Web Sevice, I noticed that the Oracle WebLogic WebService used the Apache James Mime4J library. Since it was not by default in my list of libs that ColdFusion is using, I had to download it and put it in the cfusion/lib directory. I didn’t need to reference it anywhere in my code, but it would not run without it, specifically throwing an error telling me that ColdFusion could not find a specific class within this library.