Another entry in my new CF10 Backport series, taking a new feature from ColdFusion 10 and showing that it’s possible in older versions. This time the function is
SessionStartTime() which can be pretty useful to know in certain applications.
Can’t we already do this…
Yes, just open up your
Application.cfc and create (or update if it’s already there) the onSessionStart method. Then just slap the current date and time into a session variable.
<!--- Runs when your session starts ---> <cffunction name="onSessionStart" returnType="void" output="false"> <cfset session.startTime = Now() /> </cffunction>
That was easy :) Although that’s not what this series is about, we want to just mimic the functionality of CF10 as closely as possible. So if CF10 can get that information without forcing you to alter your
Application.cfc, then neither will I.
Again, I’ve already delved into this sort of area with CFTracker but here’s how I’d have gotten there. We’ll start with the session scope, that’s what we’d like to know the start time of, so it makes sense to start there. To see what’s available, get the class name of it, create an object and dump it.
<cfdump var="#CreateObject("java", session.getClass().getName())#" />
Looking at the dump,
getElapsedTime() looks like a prime candidate for our need. Using it will return the number of milliseconds since the session was first created. All we then need to do is a little date maths to find out that actual date and time.
<cfscript> elapsed = session.getElapsedTime(); startTime = DateAdd("s", -elsaped / 1000, Now()); WriteOutput(DateFormat(startTime, "yyyy-mm-dd ") & TimeFormat(startTime, "HH:mm:ss")); </cfscript>
All done? Not quite. I’ve noticed a small issue with this when I keep refreshing the page. Sometimes the creation date and time will change by about a single second. My guess is the tiny delay between getting the elapsed time and doing the date maths is enough to cause the change. Putting
session.getElapsedtime() straight into the DateAdd function doesn’t help.
Time to disect
Obviously somewhere inside the session, it’s storing the time of creation in order to return the “elapsed time”. But how do we get at it? Well Java is nice enough to provide us with a few handy tools to dig into things like this. Meet session.getClass().
.getClass() method is available pretty much everywhere (I think) in the world of Java / ColdFusion. You’ll always see the same methods no matter what you used it on, like a structure or array for example. But what you get back refers to the object you used it on, which is identified by the
.getName() method. To poke inside the session, we’ll grab a list of “Declared Fields”, which means stuff inside the session that isn’t exposed. Just like the “variables.” scope inside a ColdFusion Component. I’ll leave that part for you to try out, but from the list you get back the one named mStartTime is the target. Since it’s private,
session.mStartTime is not going to work. We’ll have to grab that field, make it accessible and then call it against the session in question.
<cfscript> class = session.getClass(); field = class.getDeclaredField("mStartTime"); field.setAccessible(true); startTime = field.get(session); WriteOutput(startTime); </cfscript>
What you should now see is the number of seconds since the Unix Epoch, in other words 1970-1-1, to the date and time that the session was created. Thanks to the mighty Rob Brooks-Bilson, there’s an easy way to convert this into a normal date (in “server time”).
<cfscript> class = session.getClass(); field = class.getDeclaredField("mStartTime"); field.setAccessible(true); startTime = field.get(session) / 1000; epoch = DateConvert("utc2Local", "January 1 1970 00:00:00"); startDate = DateAdd("s", startTime, epoch); WriteOutput(DateFormat(startDate , "yyyy-mm-dd ") & TimeFormat(startDate , "HH:mm:ss")); </cfscript>
Much better! Refreshing the page with this code, doesn’t appear to suffer from any deviation in the time. I think we’ve got what we needed.
<cfif ListFirst(server.coldfusion.productVersion) Lt 10> <cffunction name="SessionStartTime" output-="false" returntype="date"> <cfscript> var lc = StructNew(); lc.mirror = ArrayNew(1); lc.class = lc.mirror.getClass().forName("coldfusion.runtime.SessionScope"); lc.start = lc.class.getDeclaredField("mStartTime"); lc.start.setAccessible(true); return DateAdd("s", lc.start.get(session) / 1000, DateConvert("utc2Local", "January 1 1970 00:00:00")); </cfscript> </cffunction> </cfif>
Above is the code wrapped nicely in function, wrapped in code to prevent it being present in CF10, since that’ll support it natively ;)
GitHub - CFBackport
Like before, I’ve added this code into my CFBackport project on Github (along with some others that I haven’t blogged about yet). Feel free to try it out or fork to create your own backports of functions.
Same as the previous backport, this was tested in ColdFusion 8 and should work in 9. Feel free to try it out in MX7 and let me know. Railo and OpenBD engines would require different code due to the internal gubbins.