Yes, I'm calling a bug delicious. Not that I like bugs of course - but sometimes there is no better feeling than working with someone on a problem, going through numerous rounds of tests, and then finally finding the solution. Shoot - I think I probably enjoy that feeling more than actually completing a project. It's like a mystery. Why is my application spewing out X when it should return Y? I don't know - maybe I'm just too much of a dork. Either way - sit back and let me tell you a little story.
The user came to me with the following description of his problem: He wanted to make a web service call within his Application.cfc file. Whenever he did so, though, his application would throw an error. He could take the same code though, put it in an index.cfm file, and it ran perfectly!
So with that being said, I began to work with him to simplify and laser in on the possible issue. I first asked to see his Application.cfc file - and while not directly related to the bug, there was some interesting (and incorrect) things he was doing there.
Check out how his Application.cfc began:
<cfcomponent>
<cfscript>
this.applicationTimeout = createTimeSpan(0,10,0,0);
this.clientmanagement= "yes";
this.loginstorage = "session";
this.sessionmanagement = "yes";
this.sessiontimeout = createTimeSpan(0,10,0,0);
this.setClientCookies = "yes";
this.setDomainCookies = "no";
this.scriptProtect = "all";
this.application.title = "APPTITLE";
this.application.datasource = "MYDSN";
this.datasource = "MYDS";
this.configManager = CreateObject("webservice", "Config");
return this;
</cfscript>
Now this surprised me. First - he was the This scope to store variables related to his Application that were not settings. You can put anything you want in the This scope really, but in this case it really wans't a good idea to do so. I immediately suggested moving those custom values to the Application scope and the onApplicationStart method.
The real crazy part though was the return statement in the constructor. I've done some testing with a statement in the CFC and it doesn't quite seem to actually do anything. But certainly I had him remove that as well.
Ok - so now he had this This statements cleaned up. He had a set of Application variables being created in onApplicationStart. The last thing he had was his web service call. He was using the "alias" format:
application.configManager = CreateObject("webserivce", "Config");
Where "Config" was defined in the ColdFusion Administrator's Web Services panel. When debugging I strip things down as much as possible and simplify everywhere. So I had him replace the alias with the URL. Same issue.
So next I had him build a new CFC with a "HelloWorld" method only. I figured it must be something wrong with his other CFC. Things still broke. So now that his application was torn down to the bare essentials, I told him to zip it up and send it to me.
It's at that point I saw this in his onApplicationStart:
application.configManager = CreateObject("webservice", "http://localhost:80/testingzone/badws/common/components/PropertyManager.cfc?wsdl");
Do you see it? Obviously the URL was modified to fit my machine - and even I missed it at first when I fixed the URL.
If you don't see it - here is what essentially happened.
Request A came in - the first request - to the application.
CF said, this app isn't started, let me run onApplicationStart.
While Request A was waiting, onApplicationStart made the web service call.
Request B, the web service call, called the same application.
CF told Request B, "Hey man, I'm still starting up, please stand by."
Request A was still waiting for the web service call to end.
Request B, the web service, is still waiting for the app to start up.
And there we go - dead lock. You can see the same issue if you just use cfhttp to hit a CFM in the same application.
Totally - completely - obvious. Isn't it? But both he and I were more focused on the web service side then anything else. Web services can be tricky so I kind of expect them to fail randomly sometimes.
Anyway - I thought I'd share this. As I said - it was pure joy figuring it out.
Archived Comments
It's not because you're a dork; it's because you're a geek. I think we do what we do because we like to solve puzzles. They are also less frustrating when they belong to someone else.
"both he and I were more focused on the web service side then anything else"...I spend so much time investigating the wrong thing. I spent hours last week on this css file. CSS is pretty new to me, so when I couldn't get one area to work, I was researching the syntax, the cascading rules, etc. When I got someone else to look at it, he found that the style in question was *commented out!* I had the new stuff perfect except that it *wasn't being read.* Duh.
Very good points, but can you explain me why an application has to invoke itself as webservice at start? i'm not able to figure this case.
regards
salvatore
Sal - the web service provided config info. It was built like that because he planned for _other_ sites to also use the config info.
This guy must have been the most innovative CF developer in the world!! :-).
Thanks again for the assist Ray. Good way to end the week.
Nice catch. So I guess when you create a web service object, it hits the WSDL file immediately in order to gather information about the proxy it has to create. Very interesting.
Hmm. Actually, he had made the object and the WSDL was cached. The issue came when he made the call to the method. If the WSDL had not been cached, I'm not convinced it would happen there even. So that's an interesting (new) question, does the creation of a WS object execute App.cfm? Going to test.
Interesting. So I added logging to App.cfc. I made a test.cfm that ONLY made an instance of the web service. On hitting test.cfm, App.cfc IS run for the WSDL creation, both onRequestStart, and onSessionStart. So hit 1 gives you 4 logs (2 for test.cfm, 2 for my web service). On the next hit you only get ONE log for the onRequestStart for test.cfm. CF doesn't do another network hit since it has cached the WSDL.
I guess this isn't too surprising. Your web services may use application variables so it would (obviously) need to run App.cfc, but still, interesting to check.
Oh, I also logged CGI.HTTP_USER_AGENT. For CF's hit to get the WSDL it is blank.