I was under the impression that apps written in RS2012R2.1 should work if compiled under Xojo. My first attempt failed. After looking up my user name and password in the database, it failed with a nil object exception at this line.
dim rs As RecordSet = Session.MainDB.SQLSelect("SELECT * FROM Setups WHERE Name = 'WizDone'")
In the Messages area it says [quote]Warning: No Session object in context[/quote].
It appears Xojo handles sessions differently than RS did.
The program starts with a login screen that verifies the email address and password against the database. If successful, a new webpage is shown. Daniel Taylor’s jQueryTabs panel is in a container on this page. The first line of code in the Shown event is
dim rs As RecordSet = Session.MainDB.SQLSelect("SELECT * FROM Setups WHERE Name = 'WizDone'")
which cured the problem. It didn’t cure it for long. The next time I tried to get a recordset, I got another nil object exception. Perhaps I should have been checking Session.Available in my apps written in RS, but the only time I needed it was when I looked for cookies when the app first started. It looks like I will have to do it every time I access the database now.
It just occurred to me that my database is a REALSQLDatabase and not an SQLiteDatabase. Could that be the difference?
WebSessionContext is broken in Xojo. Note that my custom controls keep the session and the context in properties, so you can get to the session in any custom control event with: Me.EventSession.
Unfortunately this means changing your code. Worse, this affects any of your own code which relies on WebSessionContext. If you use WebSessionContext in a server side Timer, Thread, or socket event (for example), Session will not work.
Agreed. I’m going to continue testing and working with Xojo to try and root out bugs and make sure that Web Custom Controls works. But my own projects will remain in RB2012r2.1 until there are some bug fixes, both framework and IDE.
(I know I should have spent more time with the beta, but I have zero time at the moment.)
First of all, this is not a regression in Xojo. This change was made in 2012r2 last fall.
The issue that you are running into has to do with the way Threads work, and what happens when they switch.
Each request coming from a browser runs in a thread and is “connected” to a particular session. When code runs from within that thread or as a direct result of that thread, the current Session is accessed by searching a lookup table.
In the case of code that is not connected to a Session (i.e. server side thread) there is no reliable way to “look up” which session you are connected to besides keeping a reference. What we had been doing prior to 2012r2 was storing a hard reference to the Sessions within every thread to make sure that Session would never be nil. This lead to rampant memory leaks which our users had no possibility of fixing.
Every time a loop cycles (for, while, do loops, etc) there is the possibility of a thread context switch, and the chances increase with the number of concurrent users. When running code in a control which is not connected to a session, the session could change which would probably lead to sending data to the wrong session.
There is a simple solution to this problem though. Add a Constructor to your Thread subclass with the following code:
MySession = Session()
Because the constructor will be run as a result of code running from one of the Session connected threads, the framework will be able to reliably return the correct Session object.
I’m sorry I chose to use a Thread as an example as that seems to have clouded the issue. The documentation does not discuss the change you refer to, so I didn’t distinguish between Threads and App events. (It needs to because MySession = Session in the constructor is not a sufficient work around. It’s easy to end up with code in a call chain that relies on Session being valid, including framework code that we can’t edit. If Session is useless in threads that needs to be noted in the docs.)
Note that Web Custom Controls does not use Threads. But it does use HandleSpecialURL. Is WebSessionContext still supported for the App events? If not then there’s no reason to even keep the WebSessionContext class around, is there?
I don’t understand. When a call is made to WebSessionContext, wouldn’t you just add the thread and the session reference to your lookup table? Then remove it when either one stops and/or falls out of scope? How would that be different from the threads handling browser connections and their use of the lookup table, including cleanup?
This won’t work for many use scenarios. Judging from the code and forum posts I’ve seen, the design pattern of looping through and working with the open sessions in response to a WebApplication, Timer (not WebTimer!), Thread, or Socket event is more widespread then you might think. I haven’t checked the last two to see if WebSessionContext still works there or not.
You absolutely cannot call the Session() method in any event on the App class and expect the correct Session object to be returned. App events fire on the main event loop, and there is no way to tell which Session is currently running.
Prior to Xojo, we had a secondary mechanism for figuring out which session was current. At best it was an uneducated guess. At worst, it could have been considered a security hole.
If you had two or more users hit your app at nearly the same moment, the threaded nature of a web application means that the code for those users could easily be executed in an interleaved fashion. I can almost guarantee that if you had a loop of some kind, the current Session context would never be the same when calling methods in the web framework. Heck, because there’s so much framework code under the hood before App.HandleSpecialURL fires, I don’t see that you could depend on the current Session being accurate there without giving it a hint.
Just add what thread? The current thread? And associate it with what? You’re looking up a session based on the current thread. You can’t just add one and then turn around and say, now what are you connected to… you’d get back Nil.
But you don’t have to. Look at my previous post. We provide a mechanism for looking up the Session based on the ControlID. That’ll probably get you where you need to be.
Dim sc As WebSessionContext
sc = New WebSessionContext(App.SessionForControl(Self))[/quote]
How would this make any difference? Does WebSessionContext all of a sudden work in the Feedback project if I get the session from a control first?
What is WebSessionContext doing if it’s not linking a thread and a session under the hood so that the Session method gets the intended session for the calling thread? And what is its intended use if it’s useless in both user level Threads and app events? The context is already set everywhere else.
[quote]1. Browser 1 sends a command to the app and The code in App.HandleSpecialURL gets called.
2. The first line runs and MySession gets set to something. Pre 2013, that was the most recently created session.
3. Browser 2 sends a command which comes in on another socket and is queued for processing by the ServerSocket.
4. The second line of HandleSpecialURL runs and the Session() method is called. Guess what, that code contains a loop, so the context switches…
5. The command from browser 2 gets processed and the current Session changes… EVEN THOUGH YOU SET IT ON THE PREVIOUS LINE.[/quote]
For this to be true Session would have to retrieve a simple global variable. But you’ve said it performs a table lookup.
If Session performs a table lookup, then the session associated with the Main Thread via WebSessionContext should not change just because there was a context switch to another thread associated with another session.
If Session is nothing more then a global variable then…I’m speechless. Such a design should have never been imagined much less implemented.
Then what is WebSessionContext for? When does it actually work?
I’ve found that best way to get the Session right all the time is to use and cache app.SessionForControl. I set up a bunch of mechanisms for doing that with a common “mySession” way to access them. This has eliminated problems associated with using Session. As has been discussed often before, the whole Session global thing is nice on accessibility for new web developers, not terribly nice when projects start getting complicated.
I’m already getting the session based on the control ID and passing that to WebSessionContext. But user and framework code called after that rely on the Session function. And the Session function returns NIL in Xojo where it returned the correct session in RB as documented.
IF the Session function does a table lookup to find the session associated with the calling thread…
AND IF WebSessionContext lets us explicitly set that association when we need to…
THEN we shouldn’t need any other mechanism to manage sessions.
I’m hearing that a lookup table is used, but then hearing about bugs and leaks and context switches changing the “current” session, and that suggests some other (incorrect) design pattern.
I would like to get to the bottom of this because WebSessionContext is either broken, or it’s completely useless and needs to be removed from the framework. And Session is either safe to call, or a train wreck. And that affects a lot more code in projects I’ve touched then just my custom controls.
[quote=10889:@Daniel Taylor]I would like to get to the bottom of this because WebSessionContext is either broken, or it’s completely useless and needs to be removed from the framework. And Session is either safe to call, or a train wreck. And that affects a lot more code in projects I’ve touched then just my custom controls.
I’ll just restate… It’s a really useful mechanisms for beginners who don’t trip on its issues. It’s not so good when projects get more complicated.
At the pace Greg has been fixing some of these deeper issues, I’m pretty sure things will get right eventually. I don’t think removing WebSessionContext is a prudent move on its own.