Thanks for the Reply. If we are accessing the session variable via URL/FORM whether we can extend the session timeout. My doubt is how can we extend the timeout of the session variable that declared in the first application from second application (After redirecting to the second application, if the user taking more time to come back to the first application session will timeout).
This is part of a back-and-forth I have going on, so sorry if you are not getting the full picture, sorry. Basically, what is going on is that this person has two ColdFusion applications - AppA and AppB - both of which have their own Application files and separate session management. A user is jumping from one application to the other and then back and the concern here is, how can we keep both sessions running in parallel, ensuring that one doesn't time out while the user is in the other.
In ColdFusion, you can't really reach into the sessions or memory space of other ColdFusion applications. There's probably ways of doing it via session pointers and what not, but that all feels hacky to me. I have a solution, that is also a tad big hacky, but just feels safer. What we have to do is create a "heart beat" URL for one of the applications (or both if need-be). The "heart beat" URL is a URL that can be pinged regularly for the specific purpose of making sure a given session does not die (aka timeout).
One of the awesome features of ColdFusion is that we can associate a page request to a given session by passing along CFID and CFTOKEN values in the query string. Once we embrace this, all we really need to do is be able to access the "heart beat" URL of one application from another AND send along the original application's CFID and CFTOKEN values. While this might sound complicated, it's actually pretty easy.
To demonstrate this idea, we are going to create two ColdFusion applications in parallel directories - AppA and AppB. AppA has the following Application.cfc ColdFusion component:
<cfcomponent output="false" hint="Handle the application level events."> <!--- Set up the application. ---> <cfset THIS.Name = "CrossPing - AppA" /> <cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 0, 10, 0 ) /> <cfset THIS.SessionManagement = true /> <cfset THIS.SessionTimeout = CreateTimeSpan( 0, 0, 0, 10 ) /> <cfset THIS.SetClientCookies = true /> <!--- Set up the page request. ---> <cfsetting requesttimeout="10" showdebugoutput="false" enablecfoutputonly="true" /> <cffunction name="OnSessionStart" access="public" returntype="void" output="false" hint="Fires when the session is first created."> <!--- When the session first starts, we are going to create a time stamp for the session initiation. That way, when we pop back into the app, we can see what the time difference is. ---> <cfset SESSION.DateInitialized = Now() /> <!--- Return out. ---> <cfreturn /> </cffunction> </cfcomponent>
Not a whole lot going on here. Notice that the SESSION TIMEOUT is only 10 seconds. This is going to be important when we are playing around inside of AppB in parallel. Also notice that when the session starts, we are storing the date and time for the session initialization. This way, we will be able to tell if the current page request started a new session or merely continued an existing session.
AppA's index.cfm just outputs the intialization timestamp as well as the current timestamp so we can see what is going on:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Cross Application Ping - Application A</title> </head> <body> <cfoutput> <p> Application A </p> <p> Session Started: #TimeFormat( SESSION.DateInitialized, "hh:mm:ss TT" )# </p> <p> Current Time: #TimeFormat( Now(), "hh:mm:ss TT" )# </p> <!--- Provide a link to the App B. We need to send along the current session information for the "heart beat" bing action. We have to be careful NOT to send CFID/CFTOKEN as names otherwise that will try to hook into the AppB session management, which is not relevant to us. Therefore, we are going to send them as AID and ATOKEN. ---> <cfset strAppBURL = ( "../AppB/index.cfm?" & "AID=#SESSION.CFID#&" & "ATOKEN=#SESSION.CFTOKEN#" ) /> <p> <a href="#strAppBURL#">Jump To App B</a> </p> </cfoutput> </body> </html>
The important thing to see here is that in the index file of AppA, we are providing a link to the index file of AppB. That link will take us to the new application with a new session. As part of that link, we are passing the CFID and CFTOKEN values of the current app's session (AppA). BE CAREFUL! Do not try to pass it as CFID/CFTOKEN named values, otherwise, AppB will think that you are trying to hook into an existing AppB session. In order to avoid this session conflict, we are going to pass over the CFID and CFTOKEN values a AID and ATOKEN respectively.
Then finally, there is AppA's "heart beat" URL, which we are putting at ping.cfm:
Here, we are just returning a 1x1 transparent GIF as the response body. While I would rather do less work since this is going to be a high-traffic URL, I am comfortable with this as a demo concept. The ping.cfm must be a ColdFusion template so that we can use it to hook into AppA's application and session management.
Ok, so that takes care of AppA. Once we click on the cross-over link in AppA's index page, we will be taken to AppB. AppB is a bit more simple, consisting of just an Application.cfc and an index.cfm page. Here is AppB's Application.cfc ColdFusion component:
<cfcomponent output="false" hint="Handle the application level events."> <!--- Set up the application. ---> <cfset THIS.Name = "CrossPing - AppB" /> <cfset THIS.ApplicationTimeout = CreateTimeSpan( 0, 0, 10, 0 ) /> <cfset THIS.SessionManagement = true /> <cfset THIS.SessionTimeout = CreateTimeSpan( 0, 0, 5, 0 ) /> <cfset THIS.SetClientCookies = true /> <!--- Set up the page request. ---> <cfsetting requesttimeout="10" showdebugoutput="false" enablecfoutputonly="false" /> <cffunction name="OnSessionStart" access="public" returntype="void" output="false" hint="Fires when the session is first created."> <!--- When this session starts, we need to store the CFID and CFTOKEN that come from the other application. We don't know that they exist, so param them first in the URL. ---> <cftry> <cfparam name="URL.AID" type="numeric" default="0" /> <cfparam name="URL.ATOKEN" type="numeric" default="0" /> <!--- Catch any numeric param erros. ---> <cfcatch> <cfset URL.AID = 0 /> <cfset URL.ATOKEN = 0 /> </cfcatch> </cftry> <!--- ASSERT: Whether or not we were passed any ID or TOKEN values from the other application, we will definitely have numeric valus for AID and ATOKEN in our URL scope. ---> <!--- Store the external ID/Token values. ---> <cfset SESSION.AID = URL.AID /> <cfset SESSION.ATOKEN = URL.ATOKEN /> <!--- Return out. ---> <cfreturn /> </cffunction> </cfcomponent>
Notice here that AppB's session timeout is much longer than AppA's session timeout. This is just to drive home the point that we will be able to hang out in AppB for a while as we test the ability to keep AppA's session living in parallel. The other important point to notice is that when AppB's sessions start, we param the URL values for AppA's incoming session IDs: AID and ATOKEN. We then store these values into the current session so that we can use them in future page requests.
Now, let's take a look at AppB's index.cfm - this is where the magic happens:
Once AppB's index.cfm page has refreshed sufficient number of times, it them forwards itself back over to AppA's index.cfm page where we can see the updated timestamps. If AppB was able to keep AppA's session running via the "heart beat" URL, then these time stamps should be different. If it was not able to keep the session running, then this page request would create a new session and the two timestamps would be just about the same.
Moment, of truth! When we click on the cross-over link (the link from AppA to AppB), we end up getting this output:
Session Started: 02:49:06 PM
Current Time: 02:49:30 PM
It worked! As you can see, the current timestamp is 24 seconds beyond that of the session intialization. And, since AppA's session timeout is a mere 10 seconds, this could only have been done if the session was being successfully pinged from AppB.
Technically, we don't really need to pass the CFID and CFTOKEN values into the ping.cfm URL. The reason for this is that the browser will send along AppA's cookies as part of the ping.cfm request. However, I like to pass along the CFID and CFTOKEN values explicitly to drive home the point that we are really hooking back into AppA's session management via the ping.cfm "heart beat" URL.
Want to use code from this post? Check out the license.
Of course, a better question to ask is why to use session variables in the first place? They pretty much suck from both a usability and a scalability standpoint.
Just so we are on the same page here, are you talking about SESSION variables in general?
Yep. From a usability standpoint keeping any significant amount of state information in SESSION precludes multiple windows/tabs/workflows into your site.
Also putting a large amount of information in session means that you're in trouble the first time you're digged or slashdotted. Plus you're in trouble from day one if you need to go multi-server as you now need to do a rewrite.
Client var's, OTOH, let you go multiserver at any time w/o needing a LB that allows sticky sessions.
I would argue with you on some of the points:
Yep. From a usability standpoint keeping any significant amount of state information in SESSION precludes multiple windows / tabs / workflows into your site.
I would say that poor application architecture breaks with multiple windows/tabs. This has nothing to do with SESSION scoped variables. I don't see how this has anything to do with SESSION.
Also putting a large amount of information in session means that you're in trouble the first time you're digged or slashdotted
Again, I don't see the connection. What does being Digged have to do with anything. That's like saying you are scewed the second anyone bookmarks your site. I think you are confusing the ideas of HAVING SESSIONS and passing CFID and CFTOKEN values in the URL. The only way I can ever see Digg being a problem is if they link to a URL that has your session information in the URL.... this is VERY different than the idea of having sessions.
Plus you're in trouble from day one if you need to go multi-server as you now need to do a rewrite.
I have never done anything multi-server so I cannot speak one way or the other on this. However, I think you can use Jsessionid or something rather than CFID / CFTOKEN to go across servers... but again, that is not something I know anything about.
And, as far as client variables, those might be easier to work cross-server, but I think they can lead to pseudo memory leaks, especially on high-spider-traffic sites (from what I have heard).
I've gone multi-server using SESSION variables and have had no problems whatsoever (with 20,000 hits a day). There is no rewrite necessary as long as you do it correctly the first time. I don't try to keep a great deal of data in the SESSION variable either (I just feel it's not best practice), but if I had to, it never seemed to impact site usability.
Are there caching issues that you'd have to worry about? Wouldn't you need to send the no cache, etc. headers to ensure the page wasn't pulled from the cache?
Would you also recommend the 1px gif method outlined above to keep a session alive on a page with a form that may take a while to fill out?
For instance, right now if a user is creating a discussion post and needs more time, I let them know via setTimeout about 5 minutes before the end of the session. That should give them enough time to finish their sentence and go to a preview page for more editing. Could one instead use an Ajax request to the 1px image to keep the session alive automatically if they hit a button that says need more time?
Hope that makes sense,
However, even with an Image() object, you don't have to return an object - the image just won't be valid. We don't really care about this, so the GIF return was probably overkill.
If you are on a long page, you could certainly do this on a regular basis:
// Every 5 minuites
(1000 * 60 * 5),
// Call this function.
var objImg = new image();
objImg.src = "YOUR_PING_URL";
This will ping the YOUR_PING_URL every 5 minutes.
I use the Image object over the AJAX methodology because we don't really care about any response value (at least not for any sort of validation). If you don't care about the return value, AJAX is just overkill. It has the overhead of creating the XMLHttpObject, which raises cross browser issues). The Image, IMO, is just easy and safe.
I am looking to do something similar but I need ad ADMIN user to have the ability to login in a a regular user but the admin's session remains.