SessionNotAvailableException issue when creating Container

Hey guys,

I am occasionally, but not always, getting a SessionNotAvailable Exception when creating dynamic container controls on one of my web pages. The exception is happening somewhere in Xojo’s internal framework code. So in my code CandCContainer is my container control class. So when I do:

dim c as New CandCContainer

The exception happens on that line. I have checked that there is a valid session and there is. I have created a WebSessionContext object but I don’t know what to do with it (the exception message says to create a WebSessionContext object). And it doesn’t seem to make a difference.

And this error doesn’t happen all the time - only some of the time. So I’m stumped with what to do and how to handle this since I need to create the container and whatever is throwing the exception is inside Xojo’s code…

Manipulation of controls that are attached to a session need to know which session they come from. The framework hooks up each request that comes in to a particular session by the thread it is running on. The only ways I’m aware of that would cause what you’re seeing is if:

  • you’re trying to create your container in response to an event that fires on the main thread (like a socket’s DataAvailable event)
  • You’ve got this code running in a Thread and you didn’t declare it as a WebThread.
  • Your code is running in response to HandleUrl or HandleSpecialUrl which have no knowledge of a session.

Greg,

Thank you. Yes, the code is running in response to a HandleSpecialURL. I am using Daniel Taylor’s WebCustomControls and have a jQueryTab control which when a new tab is added, is driving the creation of the container control.

So seeing as that is the problem, how do I assign the session so that the framework knows where to go. And it works some times which is odd. I would expect this to fail all the time then…

So I think I figured out a way to solve this…

I’ve added a web timer to the page. In the method that is launched ultimately from the HandleSpecialUrl call, I enable the timer. When the timer fires it launches the method to create my container controls. Now, I’m creating the controls from a web timer which resides on the webpage itself so all the session information should be good.

So far, it appears to work great and is actually much faster than what I had done previously…

Greg - is this the correct way to handle this then?

[quote=226148:@Jon Ogden]So I think I figured out a way to solve this…

I’ve added a web timer to the page. In the method that is launched ultimately from the HandleSpecialUrl call, I enable the timer. When the timer fires it launches the method to create my container controls. Now, I’m creating the controls from a web timer which resides on the webpage itself so all the session information should be good.

So far, it appears to work great and is actually much faster than what I had done previously…

Greg - is this the correct way to handle this then?[/quote]

How do you reconcile HandleURL which is Session agnostic with that particular web page within a particular session ? Or is the method in HandleURL updating all sessions at once ?

First of all, I’m using HandleSpecialURL which is what Daniel Taylor’s controls use. As he’s creating jQuery controls on the web page with his code, he must have the secret sauce figured out somehow.

The webpage knows it’s session. In the original method creating the container control that had the HandleSpecialURL call in its stack, if I called App.SessionForControl(self), I got back the session for the web page. That was always correct. The problem is the framework on the server sometimes loses track of the session. Like, I said originally, sometimes it worked great. Other times, it failed. If Xojo had a way to tell the server under which session to create a control, that would be great. I think that’s what the WebSessionContext object is supposed to do. However, I’m not sure how I can assign a WebSessionContext to the dynamic creation of a container control.

Now a web timer, put onto the web page in the IDE is a client side timer. So any calls from that client side timer are going to be tied to a session. It does work quite well. It has not had a crash yet in several tries.

Make sure this works with more than one session. It would not surprise me if it works beautifully with one, but has issues connecting to the right session when more than one user is connected.

So then let me ask again:

How would you recommend doing it?

[quote=226361:@Jon Ogden]So then let me ask again:

How would you recommend doing it?[/quote]
You should be creating a WebSessionContext object and then doing what you need to do asap. The context object is to help the framework know which session it’s dealing with, but it’s only good until the next WebSessionContext object is created. If a thread context switch occurs or another request comes in to HandleSpecialUrl while running your code, all bets are off.

For best results, I suggest creating the WebSessionContext and then immediately create a WebThread to do the actual work.

dim wsc as new WebSessionContext(app.SessionForControlID("xxxxx")) dim th as new WebThread AddHandler th.run, addressOf yourRunMethod th.run

Just make sure that if your code does a lot of work that you call thread.sleep periodically to yield time to the other threads. Also, if you use the AddHandler method, make sure you call RemoveHandler when you’re done with it:

Sub yourRunMethod(th as WebThread) RemoveHandler th.run, addressOf yourRunMethod ... Do Your Stuff Here .. End Sub

[quote=226367:@Greg O’Lone]You should be creating a WebSessionContext object and then doing what you need to do asap. The context object is to help the framework know which session it’s dealing with, but it’s only good until the next WebSessionContext object is created. If a thread context switch occurs or another request comes in to HandleSpecialUrl while running your code, all bets are off.

For best results, I suggest creating the WebSessionContext and then immediately create a WebThread to do the actual work.

dim wsc as new WebSessionContext(app.SessionForControlID("xxxxx")) dim th as new myWebThread th.run[/quote]

OK. So is there anything that you do with the WebSessionContext object? That’s the confusing thing about it. Or is creating it good enough and the framework takes it from there?

So far running the timer with multiple pages open seems to work fine but I do want to do it the correct way.

I’ll look at creating the webThread than too. But the problem is I need to create UI elements. Can a WebThread create UI elements?

It just needs to exist. A copy is stored down in the bowels of the framework when it’s created and removed when its destructor fires.

I’d still do it with a WebThread. Timers (assuming you’re not using WebTimers) run on the main thread and can confuse things further.

As long as you set up the context immediately before creating the thread. The difference between normal Threads and WebThreads is that we store a table of WebThreads and which sessions they belong to, so when a bit of framework code needs access to the current session, we have it. It works like this when getting the current session:

  1. Get the session associated with the current thread (WebThread or internal web framework threads)
  2. If #1 is nil then check to see if a WebSessionContext object is in play. If so, use the WebSession associated with it.
  3. If #1 and #2 are Nil, raise a SessionNotAvailableException.

Now as I said before, #2 is just a guess. If you have multiple WebSessionContext objects in play at the same time, the framework has to guess. If there are five sessions running, you have a 1 in 5 chance of it being right.

Note: I’d also suggest asking Daniel to convert his controls to use the WebSDK. It’s been around for three years now and you don’t need any of this “magic” HandleSpecialUrl stuff to make it work.

[quote=226371:@Greg O’Lone]It just needs to exist. A copy is stored down in the bowels of the framework when it’s created and removed when its destructor fires.

I’d still do it with a WebThread. Timers (assuming you’re not using WebTimers) run on the main thread and can confuse things further.
[/quote]

It’s web timer instantiated on the page. So I’m using page based code.

OK. As long as I can create UI elements in a WebThread. I’ll try this…

Wouldn’t it be useful to have an optional session property for all the constructors? So in a container control like I have for example:

I would think something like that where we could tell the framework what session to use would be useful. Seems better than creating this guessing game…

[quote]
Note: I’d also suggest asking Daniel to convert his controls to use the WebSDK. It’s been around for three years now and you don’t need any of this “magic” HandleSpecialUrl stuff to make it work.[/quote]

I know he wants to do that. Problem is he’s actually had very little time the last year or two for development due to his day job. He has a very complete set of controls. I know Brock came out with a jQuery library recently, but it’s not near as complete as Daniel’s. So I do hope Daniel completes his migration to the SDK soon. :slight_smile:

OK. I just set up the WebThread and that works great. So if that’s the right way to do it - cool. It’s kind of odd to set up an object and then purposely let it go out of scope…But I looked in the docs and I also followed your instructions. It works!

The docs are a little confusing though. They say:

So the first line tells the reader that web threads prevent you from having to use WebSessionContext. However, that’s not always true as in my case here. So not sure if that needs to be cleaned up a little…

That’s only the case for webthreads created from within a session because to inherits the session. When doing it from The main thread, you need to provide a reference because there isn’t one to inherit.

Thanks, Greg.

Also - FYI - I checked with Daniel and while his controls do use HandleSpecialURL, he also does manage the session contexts as well. That must explain why some of the time things worked fine. But in other cases, there was a context switch that must have screwed things up…