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.
Archived Comments
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.
Oh yeah - I didn't even account for the possibility it may just be a bug in CF. I'll file a report now.
Bug report: https://bugbase.adobe.com/i...
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.
[subscribe]
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.
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?
I refreshed it in cfinvoke. I'm pretty darn sure that fixed it. Will confirm in a bit.
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.
Are you sure you didn't leave wsversion="1" in the cfcomponent tag?
That would do it.
@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.
@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.
http://stackoverflow.com/qu...
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.
That URL got trashed by the underscore = italics mechanism. Trying again with %5f for underscore:
http://www.wso2.net/2006/05...
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.
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.
http://stackoverflow.com/qu...
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
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?
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!
Stupid underscores again:
this.serviceAddress = Application.wsdl%5fsrvaddr; // doesn't work.
getMetaData(this).serviceAddress = Application.wsdl%5fsrvaddr; // does work.
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.
Well, %5f used to work to get underscores into the code here. I guess that changed.
Sorry for the issues. This is why I suggest a Pastebin or Gist for code. :)
No problem. For the benefit of those who couldn't read it, Melvin had an underscore between Application.wsdl and srvaddr.
Follow up: http://www.raymondcamden.co...