MailSocket EventHandlers question

I have a Session.MailSocket1 (and its various EventHandlers) created via code in Session.Open.

I have a SendEmail button on WebPage1.

Various Session.MailSocket1 EventHandlers call a WebPage1.WebDialog, and also hide or expose a WebPage1.ProgressWheel1 and a few WebPage1 buttons (e.g., the SendEmail button).

This all works fine most of the time. But after a while (maybe a few days), Session.MailSocket1.ConnectionEstablished fails to expose the WebPage1.ProgressWheel, and Session.MailSocket1.MailSent fails to show the WebPage1.WebDialog announcing mail is sent. However, email is always sent successfully.

It seems somehow that either Session has lost track of WebPage1, or WebPage1 has lost track of Session.

Closing the problem Session fails to resolve this. When this problem happens, all existing and new Sessions have the same problem from that point on. Killing the app fixes it, which I do by renaming the CGI file and then renaming it back, relying on Phillip Zedalis’ App.Open code that kills the app when the CGI file goes missing.

I thought I might fix this by having each Session.MailSocket1 EventHandler mentioned above call a method that refreshes WebPage1 by running WebPage1.Show before hiding or exposing the WebPage1.ProgressWheel, etc. That failed to resolve this.

So before I try another “solution,” I thought I’d post the above to get some ideas.

My other possible “solutions” to try are the following:

  1. Attach MailSocket1 to WebPage1 instead of Session.

  2. Leave MailSocket1 where it is (Session), but read up on how to put SendEmail in a Thread, and use a WebPage1.Timer to poll the Thread for activity that demands WebPage1 UI updates. The docs seem easy to follow on how to do this.

This is what I would recommend.

That won’t work either. Sockets fire their events on the main event loop which means that they don’t fire within the context of any particular session. I suggest that you subclass the SMTPSecureSocket and add a Session property so you can keep track of which session to which it belongs and use them instead. Make sure you set that property to Nil when you’re done with the socket or use a WeakRef to prevent memory leaks.

Fair enough, but wouldn’t it be better to add a WebPage1 property to the socket instead of a Session property, since all the communication is going back to WebPage1?

Well, that’s an eye-opener. Thanks, Greg.

I guess I can do that, but since WebPage1 is tied a particular Session, would it make a big difference?

Well you could, but you’d have to run a call on app to get the session anyway because what really needs to happen is for you to call:

Dim sc As WebSessionContext sc = New WebSessionContext(CurrentSession)

Because without this framework calls will throw an exception about there being no session.

In re-reading Greg’s post

it sounds like, even though I was connecting the Socket to a Session Property, its events were not so “connected”. Does this mean that if 2 users were logged in and hitting the SendEmail button, one might get the result of a MailSent event that was intended for the other?

Okay. After a lot of reading and re-reading of Greg’s suggestion and various forum posts and Xojo docs, I did the following:

  1. Subclassed SecureSMTPSocket: MailSocket

  2. MailSocket has a Computed Property called Session (As Session):

[code]Sub Set (value as Session)
mSession = New WeakRef(value)
End Sub

Function Get() as Session
If mSession = Nil or mSession.value = Nil Then
Return Nil
Return Session(mSession.Value)
End If
End Function[/code]

  1. Session has a Property called MailSocket1 (As MailSocket). Session.Open assigns Session.MailSocket1 an instance of MailSocket and adds Event Handlers (as Session methods) to it. MailSocket1.Session is assigned App.SessionWithIdentifier(Self.Identifier).

  2. Each MailSocket1 Event Handler now has this code preceding calls to WebPage1:

Dim sk As MailSocket sk = MailSocket1 Dim sc As WebSessionContext sc = New WebSessionContext(sk.Session) Dim page As WebPage1 sc.Session.CurrentPage = page

I can’t imagine I need to test for the existence of the session in question because all this is being called by the current session and the current session is the session in question, and what the above is supposed to do is help the Socket find that session. Unless I really misunderstand something important.

Does this all look okay? It seems to be working, but the problem before would show up after a few days.

You do need to test if sk.session is nil. It’s entirely possible that the background session cleanup process will have run and the session is gone.

Also, that last line is setting the current page to a nil object. That will most definitely fail. You could use “new WebPage1” on the previous line but it depends on what you’re trying to do.

Thanks, Greg. It’s probably a good idea at this point to give an outline of what this is supposed to do. I’ll stop using generic code references, so I get this right. The user sees AgentWeekPage.AgentWeekContainer1, and on that page is a Send button which calls a SendMail method. While on that page, while connected, they see AgentWeekPage.AgentWeekContainer1.ProgressWheel, and various buttons get hidden. Various MailSocket1 events hide or show the ProgressWheel and buttons (e.g., the ConnectionEstablished event shows the ProgressWheel and hides the buttons, and the MailSent event hides the ProgressWheel and shows the buttons).

So the user never leaves this page during the entire SendMail routine. The problem has been that after a few days or so, users would no longer see the ProgressWheel, and the MailSent event would no longer seem to fire (the SuccessDialog would not appear and buttons would not re-appear). But the mail was always sent successfully. So it was as though the MailSocket1 events were no longer communicating properly with the current page. The ConnectionEstablished event would properly hide the buttons (a session method), but not show the ProgressWheel (a page object). And the MailSent event would not re-expose the buttons (a session method).

So I added the code you saw above, using WebSessionContext, etc. Again, the SendMail routine works a number of times, just like before, but eventually exhibits the behavior outlined above. When it stops working properly, it fails for any current or new user. When this happens, I can fix this by renaming the CGI file and renaming it back, relying on App.Open code that kills the app when that file goes missing.

The reason I didn’t use ‘New’ for the current page is that I figured once the correct Session was found, the page the user is on will be seen by just assigning to sk.Session.CurrentPage the name of the page. And when I used ‘New’ initially, it resulted in a short-lived blank screen each time it was called. I figured using ‘New’ was unnecessary because the page was currently active. I see why what I did would produce a Nil object. Can I just assign sc.Session.CurrentPage directly the existing page (see below)? If not, I’ll make it a New Page.

I still don’t understand why I need to check for the existence of the session, because the session associated with the Session.Identifier grabbed in Session.Open is the session the user is in the entire time this process runs. Unless, of course, I misunderstand “session” here. But I’ll add a test now (see below).

So, e.g., here’s the relevant Session.ConnectionEstablished event handler code I’ll test for a while now (it’s working now):

[code]Dim sk As MailSocket
sk = Session.MailSocket1
Dim sc As WebSessionContext
sc = New WebSessionContext(sk.Session)

If sc.Session = Nil Then
MsgBox “Session died.”
sc.Session.CurrentPage = AgentWeekPage // does this need to be New?
// Turn on ProgessWheel and hide container buttons.
AgentWeekPage.AgentWeekContainer1.ProgressWheel1.Visible = True
End If[/code]

After re-reading the doc on WebSessionContext, it looks like I need a “new” page in there, so I changed

sc.Session.CurrentPage = AgentWeekPage


sc.Session.CurrentPage = New AgentWeekPage

Oops…should be testing for sk.Session=Nil instead of sc.Session=Nil:

[code]Dim sk As MailSocket
sk = Session.MailSocket1

If sk.Session = Nil Then
MsgBox “Session died.”
Dim sc As WebSessionContext
sc = New WebSessionContext(sk.Session)
sc.Session.CurrentPage = New AgentWeekPage
// Turn on ProgessWheel and hide container buttons.
AgentWeekPage.AgentWeekContainer1.ProgressWheel1.Visible = True
End If[/code]

The more I read about this, I wonder if I really need

Dim sc As WebSessionContext sc = New WebSessionContext(sk.Session) sc.Session.CurrentPage = New AgentWeekPage

in there for each MailSocket1 event handler. Assuming I check for the existence of sk.Session in each such event handler, can’t I assume that the WebPage running in that session (AgentWeekPage) is still accessible to such an event handler?

Nope. I need that code in there, or else ConnectionEstablished might eventually stop updating the WebPage, like before I started experimenting with code that gets the Socket to see the WebPage.

After a lot of testing, I now have the ConnectionEstablished event handler properly updating the WebPage every time. But I find that if there are multiple sessions running, if I let the current session time out and I reload the browser with that page and try a SendMail again, the MailSent event hander won’t fire (though the ConnectionEstablished event handler always fires fine). Subsequent timeouts have the same failures at that point. I tested this by placing a MsgBox(“Mail Sent”) as the first line of the MailSent event handler, just in case the problem was that it was firing without updating the current WebPage. The failures were cases where that MsgBox didn’t display. In all cases, the email is successfully sent.

Both ConnectionEstablished and MailSent use the same code to get the correct Session and current WebPage:

Dim sk As MailSocket sk = MailSocket1 If sk.Session = Nil Then MsgBox "Session died." Return End If Dim sc As WebSessionContext sc = New WebSessionContext(sk.Session) sc.Session.CurrentPage = New AgentWeekPage

Ever since yesterday morning, this is working fine as long as I diligently code this at key points to keep the socket aware of the current Session/WebPage, using the code in the previous post above. Both ConnectionEstablished and MailSent event handlers fire as desired now. Sorry for the lengthy posts.