Twitter: raymondcamden


Address: Lafayette, LA, USA

Unexpected behavior with Axis2 web services in ColdFusion

07-10-2014 2,275 views ColdFusion 26 Comments

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.

26 Comments

  • Commented on 07-10-2014 at 10:21 AM
    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.
  • Commented on 07-10-2014 at 10:23 AM
    Oh yeah - I didn't even account for the possibility it may just be a bug in CF. I'll file a report now.
  • Commented on 07-10-2014 at 10:24 AM
    Bug report: https://bugbase.adobe.com/index.cfm?event=bug&...
  • Toby Reiter #
    Commented on 07-10-2014 at 10:31 AM
    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.
  • Miguel_F #
    Commented on 07-10-2014 at 10:36 AM
    [subscribe]
  • Commented on 07-10-2014 at 10:40 AM
    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.
  • Steve Seaquist #
    Commented on 07-10-2014 at 10:54 AM
    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?
  • Commented on 07-10-2014 at 10:56 AM
    I refreshed it in cfinvoke. I'm pretty darn sure that fixed it. Will confirm in a bit.
  • Steve Seaquist #
    Commented on 07-10-2014 at 11:03 AM
    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.
  • Steve Seaquist #
    Commented on 07-10-2014 at 11:05 AM
    Are you sure you didn't leave wsversion="1" in the cfcomponent tag?

    That would do it.
  • Toby Reiter #
    Commented on 07-10-2014 at 12: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.
  • Commented on 07-10-2014 at 12: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.
  • serializing structs #
    Commented on 07-10-2014 at 1:28 PM
    http://stackoverflow.com/questions/11059219/coldfu...
  • Steve Seaquist #
    Commented on 07-10-2014 at 1: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/axis2performancetestinground1

    Too bad it imposes such a recoding burden (so far) to prevent this stateful behavior.
  • Steve Seaquist #
    Commented on 07-10-2014 at 1:57 PM
    That URL got trashed by the underscore = italics mechanism. Trying again with %5f for underscore:

    http://www.wso2.net/2006/05/axis2%5fperformance%5f...
  • Commented on 07-10-2014 at 2: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.
  • Steve Seaquist #
    Commented on 07-10-2014 at 3:09 PM
    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.
  • Commented on 07-12-2014 at 1:53 AM
    http://stackoverflow.com/questions/24313528/cf10-c...
  • Melvin T #
    Commented on 07-17-2014 at 11:21 AM
    Hi Ray,
    On the 'serviceaddress' property of a cfc, can you make the name dynamic? Say, I have an application.wsdlsrvaddr = 'https://mydevsite/mycomp.cfc"; in my DEV environment, then in my QA, I have application.wsdlsrvaddr = '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
  • Commented on 07-21-2014 at 9:48 AM
    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?
  • Steve Seaquist #
    Commented on 07-22-2014 at 1:42 PM
    Hi Melvin!

    Try doing it outside of the cfcomponent tag:

    this.serviceAddress = Application.wsdlsrvaddr; // 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!
  • Steve Seaquist #
    Commented on 07-22-2014 at 1:43 PM
    Stupid underscores again:

    this.serviceAddress = Application.wsdl%5fsrvaddr; // doesn't work.
    getMetaData(this).serviceAddress = Application.wsdl%5fsrvaddr; // does work.
  • Steve Seaquist #
    Commented on 07-22-2014 at 1: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.
  • Steve Seaquist #
    Commented on 07-22-2014 at 1:48 PM
    Well, %5f used to work to get underscores into the code here. I guess that changed.
  • Commented on 07-22-2014 at 2:00 PM
    Sorry for the issues. This is why I suggest a Pastebin or Gist for code. :)
  • Steve Seaquist #
    Commented on 07-22-2014 at 3:52 PM
    No problem. For the benefit of those who couldn't read it, Melvin had an underscore between Application.wsdl and srvaddr.

Post Reply

Please refrain from posting large blocks of code as a comment. Use Pastebin or Gists instead. Text wrapped in asterisks (*) will be bold and text wrapped in underscores (_) will be italicized.

Leave this field empty