Unexpected behavior with Axis2 web services in ColdFusion

This post is more than 2 years old.

Credit for this find goes to Steve Seaquist. He and I have been discussing this over email the last few days. Ok, quiz time, look at the following CFC:

component  {
variables.weird = 0;
remote numeric function testWeird() {
	variables.weird++;
	return variables.weird;
}

}

Imagine you are calling this CFC directly from JavaScript code. As you know, each time you call a CFC remotely, it will be created on the fly. So calling this CFC's testWeird method N times will always return 1. However, what happens if you call it as a web service?

If you said 1, you would be wrong. Under Axis2, the CFC is now persistent. I have no idea why. But if you make calls to the CFC as a webservice, you will see that the result increments by one every time you do so. I've got no idea what scope this is living in (my first test was early this morning, hours ago), and the only way to clear this is to edit the CFC itself. (Refreshing the WSDL also does it.) If you add wsversion="1" to the component tag it will use the earlier Axis library and act the right way.

Perhaps this is some known "feature" of Axis2. I did a bit of Googling but nothing really struck me as relevant. Whether good or bad, this is one of those things I did not expect (neither did Steve) so I'm blogging to warn others.

Also, don't use web services. Seriously. Unless someone is holding a gun to your head, just use simple JSON services and don't overcomplicate stuff.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Joe Rinehart posted on 7/10/2014 at 7:21 PM

That sounds more like a difference in behavior in the ColdFusion adapter that talks to Axis 2 - Axis itself wouldn't know anything about CFCs.

Anyhow...

> Also, don't use web services. Seriously. Unless
> someone is holding a gun to your head, just use
> simple JSON services and don't overcomplicate stuff.

...amen.

Comment 2 by Raymond Camden posted on 7/10/2014 at 7:23 PM

Oh yeah - I didn't even account for the possibility it may just be a bug in CF. I'll file a report now.

Comment 3 by Raymond Camden posted on 7/10/2014 at 7:24 PM
Comment 4 by Toby Reiter posted on 7/10/2014 at 7:31 PM

So, If we're trading data back and forth between servers behind the scenes, how do we do a "simple JSON service"? Wouldn't that be a _cfhttp_ call? To me, a web service seems a much more direct approach than serializing all of my values and submitting them as a post request.

Comment 5 by Miguel_F posted on 7/10/2014 at 7:36 PM

[subscribe]

Comment 6 by Raymond Camden posted on 7/10/2014 at 7:40 PM

Toby, I can go deeper if you want, but for a while now CF has had an easy way to output JSON since CF6.

Imagine the CFC above. I can call it from JS using this URL:

some.cfc?method=testWeird&returnformat=json

The URL argument, returnformat, tells CF to auto turn the result into JSON.

It really is easy to use.

In terms of calling the CFC with data, if you add crap to the url, like &name=ray, then it is passed like an argument to the function. So arguments.name works. Ditto for FORM posts.

Comment 7 by Steve Seaquist posted on 7/10/2014 at 7:54 PM

In my experience, refreshing the WSDL made no difference. But I used cfinvoke's refreshWSDL="Yes". Are you saying refreshing the WSDL in ColdFusion Administrator resets Variables.weird?

Comment 8 by Raymond Camden posted on 7/10/2014 at 7:56 PM

I refreshed it in cfinvoke. I'm pretty darn sure that fixed it. Will confirm in a bit.

Comment 9 by Steve Seaquist posted on 7/10/2014 at 8:03 PM

It could have been that you did a some.cfc?method=testWeird test and that's what actually reset it. Then on the next WS call with refreshWSDL="Yes", it masqueraded as a fix.

Or maybe it was something else you did amid many tests. Whatever it was, it would be great to have an Axis2 solution less painful than restarting CF Server.

Comment 10 by Steve Seaquist posted on 7/10/2014 at 8:05 PM

Are you sure you didn't leave wsversion="1" in the cfcomponent tag?

That would do it.

Comment 11 by Toby Reiter posted on 7/10/2014 at 9:01 PM

@Ray - I'm familiar with getting data returned as JSON from AJAX requests, but in this case, I have to submit a serialized object with dozens of its own fields as well as one or more child objects that also have dozens of fields (basically, to perform data transfer between servers). While a CFHTTP post can handle that much data (I think), it was more straightforward to submit it as a struct with nested arrays as opposed to a serialized JSON string.

Comment 12 by Raymond Camden posted on 7/10/2014 at 9:56 PM

@Steve: I only used that arg in a copy of my WS file so I could easily test.

@Toby: I'm not quite sure I get you. If you have a complex struct, you can serialize it very easily.

Comment 13 by serializing structs posted on 7/10/2014 at 10:28 PM
Comment 14 by Steve Seaquist posted on 7/10/2014 at 10:54 PM

The benefit of using web services is performance. By precompiling the arguments negotiation, it becomes a much faster remote invocation mechanism. That's why you have to refresh WSDLs when they change.

My site is a very heavy user of web services, so it's not just syntactic sugar to us. We were actually hoping to gain a 4x to 5x performance improvement in the move to Axis2: http://www.wso2.net/2006/05...

Too bad it imposes such a recoding burden (so far) to prevent this stateful behavior.

Comment 15 by Steve Seaquist posted on 7/10/2014 at 10:57 PM

That URL got trashed by the underscore = italics mechanism. Trying again with %5f for underscore:

http://www.wso2.net/2006/05...

Comment 16 by Raymond Camden posted on 7/10/2014 at 11:03 PM

Woah now, while I will not argue that Axis2 is more performant, I'm not going to agree that related network calls to web service X versus the same CFC doing JSON is going to be faster, especially with how verbose SOAP is. CFC instantiation is *incredibly* fast, especially since CF8 and onwards. Also, if you have any particularly complex CFCs you can cache them in RAM and call them via your remote CFC as a proxy.

Comment 17 by Steve Seaquist posted on 7/11/2014 at 12:09 AM

It's not our code calling our code. It's customers calling our code with all manner of platforms and differing client software. Sometimes they have no choice but to use web services because they're passing through 3rd party software.

Perhaps I should've been more specific, sorry. I'm sure, if the caller has no need for WSDL, you can save a ton of processing.

Comment 18 by Dana K posted on 7/12/2014 at 10:53 AM
Comment 19 by Melvin T posted on 7/17/2014 at 8:21 PM

Hi Ray,
On the 'serviceaddress' property of a cfc, can you make the name dynamic? Say, I have an application.wsdl_srvaddr = 'https://mydevsite/mycomp.cfc" in my DEV environment, then in my QA, I have application.wsdl_srvaddr = 'https://myQAsite/mycomp.cfc". I know that if you remove the serviceaddress on the cfcomponent tag, it will just defaults to whatever protocol+location the cfc resides. Thing is, our SSL certs are installed in F5, so if I take it out, the protocal will always show SSL. I was hoping to just do -

<cfcomponent output="false" serviceaddress="#application.wsdl_srvaddr#">

... but this always breaks with

Fault - Error attempting to create Java skeleton for CFC web service.; nested exception is:
coldfusion.xml.rpc.CFCInvocationException: [java.lang.ExceptionInInitializerError : null][java.lang.ClassCastException : [Ljava.lang.Object; cannot be cast to java.lang.String]

Any suggestions? (- and shoutout to my homie Steve S!)

Thanks! -M

Comment 20 by Raymond Camden posted on 7/21/2014 at 6:48 PM

Um, you got me. It *may* be that when CF generates the skeleton, there is no Application scope. If you change that variable to foo, and before cfcomponent do

cfset foo = something

Does it work then?

Comment 21 by Steve Seaquist posted on 7/22/2014 at 10:42 PM

Hi Melvin!

Try doing it outside of the cfcomponent tag:

this.serviceAddress = Application.wsdl_srvaddr; // doesn't work.
getMetaData(this).serviceAddress = Application.wsdl_srvaddr; // does work.

The getMetaData moves the "this" reference up to the cfcomponent level. Pretty cool, huh!

Comment 22 by Steve Seaquist posted on 7/22/2014 at 10:43 PM

Stupid underscores again:

this.serviceAddress = Application.wsdl%5fsrvaddr; // doesn't work.
getMetaData(this).serviceAddress = Application.wsdl%5fsrvaddr; // does work.

Comment 23 by Steve Seaquist posted on 7/22/2014 at 10:47 PM

Why did it do THAT??? Trying again without copy/paste:

this.serviceAddress = Application.wsdl%5fsrvaddr; // doesn't work.
getMetaData(this).serviceAddress = Application.wsdl%5fsrvaddr; // does work.

That's in the pseudo-constructor, so I'm not sure about the Application reference, but I'm dead certain about the getMetaData(this) allowing code in the pseudo-constructor to modify the serviceAddress.

Comment 24 by Steve Seaquist posted on 7/22/2014 at 10:48 PM

Well, %5f used to work to get underscores into the code here. I guess that changed.

Comment 25 by Raymond Camden posted on 7/22/2014 at 11:00 PM

Sorry for the issues. This is why I suggest a Pastebin or Gist for code. :)

Comment 26 by Steve Seaquist posted on 7/23/2014 at 12:52 AM

No problem. For the benefit of those who couldn't read it, Melvin had an underscore between Application.wsdl and srvaddr.

Comment 27 by Raymond Camden posted on 8/21/2014 at 6:14 PM