The ColdFusion OverSeer Application
In the comments on my rant about the new and improved unhelpful help systems companies like these days, Mark writes:
Speaking of needing help, the thought occurred to me the other night that it would be really cool if one of my CF apps could at least read the Application variables of another of my CF apps on the same server. Essentially, the first CF app is an administrative one needing to check the status of one of the apps it helps administer. Where I need help is that I have no idea how to let one app see another's application scope. Is there a method way to do this? And, if not, could we ask for it in CF9? Very best regards....
I hate it when I forget a critical piece of information when asking for help... Neither of the applications are using onTap. Just plain jane CF8 standard.
Well I'm certainly not one to begrudge a little help to others in the industry, whether they're using my applications or not. :) And this question actually came up at my new job in Boston recently, my new boss (the technical one) was facing the same issue and asking me if I had any new info... and actually after thinking about it for a few minutes, I did come up with a solution, although he didn't seem to like it. (Or possibly didn't understand my explanation, it's tough to tell.)
So here I'll give explaining it another shot, with the caveat that, I haven't tested any of this, it's pure theory. :)
If you dig into the ColdFusion ServiceFactory you can find an object somewhere that gives you access to session scopes across the server. The problem is, there doesn't appear to be any similar functionality for applications. To make things even stranger, the SessionManager (not sure if that's what it's called) lets you filter those sessions by application name! :P So although it's no solution for applications that have unknown names at run-time, if you know the name of the applications you want to monitor you can loop over those names and fetch the sessions for each and that way you know based on the active sessions whether or not that application is currently running.
What follows is pseudocode, not actual working code.
<cfset sess = SessionManager.getSessions(appname) />
<cfif arraylen(sess)>... it's running! ...</cfif>
</cfloop>
Then inside that if where you've checked to see if it's running, you can drop into that application briefly using the cfapplication tag.
<cfif apprunning>
<cfapplication name="#appname#" sessionmanagement="true"
sessiontimeout="#blah#" applicationtimeout="#other#" />
</cfif>
</cfloop>
The annoying bit here is that you'll have to make sure you know all those application settings for that application, so you don't overwrite them with horribly inappropriate attributes, like for example suddenly disabling client management in a running application would be horrid. So you will need some kind of central repository to store the information about each app's settings.
When you're done, make sure you remember to return to your original application (the manager app) by using the cfapplication tag one more time after the loop.
Anyway, I hope this is helpful. :)
p.s. One other alternative to knowing if the application is running in advance is to store the application settings actually in the application scope, then declare a really short application timeout (1 second for example) in the cfapplication tag and if those settings aren't declared when you drop into the application, you'll know it wasn't running and you can let the application expire after the 1 second. It also won't run the application's onApplicationStart and set up any code it needs for that application, which is why you need it to expire after 1 second to reduce the chances of someone else touching the real application at the same time and it dying because it doesn't have its application variables. (That's a race condition.) But there's not really any way to resolve that race condition, you just have to set the timeout short and hope that 1 second is brief enough for it to expire before anyone else hits it. If the application is running however, then you have to execute the cfapplication tag again right away with the appropriate settings to keep it from dying and I make no guarantees that it won't crap out someone else's page if it changes settings like client management.
<cfapplication name="#appname#" applicationtimeout="#CreateTimeSpan(0,0,0,1)#" ... />
<cfif isDefined("application.timeout")>
<!--- it's running! --->
<cfapplication name="#appname#" applicationtimeout="#application.timeout#" ... />
</cfif>
</cfloop>
p.p.s. There's a planned feature for ColdFusion 9 (Centaur) to have a Server.cfc that would be able to do some things like the Application.cfc, for example code that executes when the server starts or stops. If they implement this (which I suspect they will), then that would give us some additional (and more bulletproof) options for monitoring applications. Each application could then register itself with the a server object when it starts and the whole thing becomes pretty simple. ... Well... you can actually use the server object technique now, but it's a little trickier because each application has to instantiate it and you have to have to lock its creation to make sure there's only one of them... But here's a code snippet anyway...
<cffunction name="init" access="public" output="false">
<cfargument name="appname" type="string" required="true" />
<cfargument name="appscope" type="struct" required="true" />
<cflock name="server.appmonitor" type="exclusive">
<cfif not isdefined("server.appmonitor")>
<cfset server.appmonitor = CreateObject("component","my.appmonitor.class") />
</cfif>
<cfset server.appmonitor.addApplication(appname,appscope) />
</cflock>
</cffunction>
</cfcomponent>
And then in each of your Applications you've got to do this in the Applicatin.cfc.
<cffunction name="onApplicationStart" access="public" output="false">
<cfset CreateObject("component","bootstrap").init(this.name,application) />
</cffunction>
<cffunction name="onApplicationEnd" access="public" output="false">
<cfset server.appmanager.removeApplication(this.name) />
</cffunction>
</cfcomponent>
Okay... that's 3 separate methods and I spent twice as long writing this as I planned... :P I think I'm done. :)
