B4J Tutorial [WebApp] Chatroom - Threads, Sessions and Server events

SS-2014-04-16_14.40.15.png


Online example: http://basic4ppc.com:51042/chat/index.html

This example demonstrates several important concepts of the web app framework.

Multiple pages

The chatroom is made of two pages. The login page and the chat page.
The WebSocket is broken when the browser navigates to a different page (or refreshes the current page). The http session allows us to pass data from one page to another. In this example the user name is stored in the login page and then retrieved in the chat page:
B4X:
'login
ws.Session.SetAttribute("name", name)
WebUtils.RedirectTo(ws, "chat.html")

'chat page
If ws.Session.HasAttribute("name") = False Then
   'name not found. Go to the login page. This will happen if the user goes to the chat page directly.
   WebUtils.RedirectTo(ws, "index.html")
Else
   name = ws.Session.GetAttribute("name")
   ws.Session.RemoveAttribute("name") 'remove the attribute
   'the user will be redirected to the login page next time.

   CallSubDelayed3(ChatShared, "NewConnection", Me, name)
   txt.RunMethod("select", Null)
End If

Threads

Each WebSocket class instance runs in a different thread (in release mode). This allows many users to be connected at the same time.
CallSubDelayed was modified in B4J v2.00 to be executed on the target object thread. This allows us to safely call subs in other modules or instances.
In this example we have a code module named ChatShared. This module holds references to all the active connections and whenever there is a new message it uses CallSubDelayed to notify each of the instances about the new message:
B4X:
'ChatShared code module
'notify each of the connected users about the new message
For Each c As Chat In connections.Values
   CallSubDelayed2(c, "NewMessage", msg)
Next

'Chat WebSocket class
Sub NewMessage(Msg As String)
   ChatTxt.RunMethod("append", Array As Object(Msg))
   ws.RunFunction("scrollDown", Null)
   ws.Flush
End Sub

The code in code modules will run in the main thread if we call it with CallSubDelayed.
In this example we call WebUtils methods directly as these methods do not rely on any shared data.

Server events

One of the great features of WebSockets is the simple support for server events. Unlike ajax based solutions, the clients don't need to poll the server with endless requests. At any given time the server can send messages to the client.
There is one extra step with server events. You must call ws.Flush at the end of the event. Flush is called internally in the case of client events.
 

Attachments

  • Chatroom.zip
    6.8 KB · Views: 1,805
Last edited:

billzhan

Active Member
Licensed User
Longtime User
Erel,

I read the classes tutorial,when comes to the differences between class and code module(B4A):
The code module doesn't hold a reference to any context. For that reason it is impossible to handle events or use CallSub with code modules.

I am not sure about some issues,as something, as something changed in B4J:

1.ChatShared.Init is called in main module, what's the lifecycle of ChatShared (and it's variables, Public AvoidDuplicates and Private connections)?

2.I think in B4A Public and Private variables can't hold values,is that right?

3.Can I create more than one thread safe maps in the same way? When I need to re-initital it, Do I just need to call CreateThreadSafeMap again?
B4X:
AvoidDuplicates = Main.srvr.CreateThreadSafeMap

4.Why you use CallSubDelayed instead of call it directly for chat class? Are they the same ?
B4X:
CallSubDelayed3(ChatShared, "NewConnection", Me, name)
ChatShared.NewConnection(Me,name)

Thanks,
bz
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
B4J code modules are not the same as B4A code modules. I recommend you to start with this tutorial: http://www.b4x.com/android/forum/threads/b4j-tutorial-for-basic4android-developers.34659/#content

1. There is no lifecycle to B4J code modules. They exist as long as the process is running.
2. Not sure that I understand. Variables can holds values.
3. You can create as many thread safe maps as you want. You can either call AvoidDuplicates.Clear or create a new one with the code you posted.

4. This is very important point. See the "threads" section in the first post. It is only relevant to server (or WebApps) projects. Each class instance runs in its own thread. If you call ChatShared.NewConnection then this sub will be executed in the class instance thread.
If you call CallSubDelayed then the target thread will be used to execute the code. In the case of code modules it is always the main thread.
 
Last edited:

slamarca

Member
Licensed User
Longtime User
Hi Erel, in line 20 of module Chat.bas should be "ws.Session.RemoveAttribute("name")" in place of "RemoteAttribute".
Btw, are there any docs on WebSocket and JqueryElement methods and properties?
UPDATE: I've found them....

Keep up the good work!

Sergio from Italy
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
#3

B4J code modules are not the same as B4J code modules. I recommend you to start with this tutorial: http://www.b4x.com/android/forum/threads/b4j-tutorial-for-basic4android-developers.34659/#content

4. This is very important point. See the "threads" section in the first post. It is only relevant to server (or WebApps) projects. Each class instance runs in its own thread. If you call ChatShared.NewConnection then this sub will be executed in the class instance thread.
If you call CallSubDelayed then the target thread will be used to execute the code. In the case of code modules it is always the main thread.

I guess this means that in this specific case (ChatShared.NewConnection) is better that the code is executed in the "routine's thread" (Main for modules), since in the example "[Server] Login System & Filters Tutorial", in the SigninHelper (which runs in its own thread, if I understand well) there is this call (DB is a module):

B4X:
success = DB.CheckCredentials (userName, password)



(I bought an electron microscope and found: "B4J code modules are not the same as B4J code modules". An incorrect character: unforgivable :D)
 

LucaMs

Expert
Licensed User
Longtime User
Each class instance runs in its own thread. If you call ChatShared.NewConnection then this sub will be executed in the class instance thread.
If you call CallSubDelayed then the target thread will be used to execute the code. In the case of code modules it is always the main thread.

So, if I want to use a generic function of a module I can run it in the thread of a websocket handler by calling it by CallSub. But it is possible, in this way, that two classes use that routine simultaneously without interference?

Otherwise, I was thinking to use a class for the utility routines and create an instance of this class within the websocket handler.
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
So, if I want to use a generic function of a module I can run it in the thread of a websocket handler by calling it by CallSu
You don't need to use CallSub in this case. Just call it directly.
But it is possible, in this way, that two classes use that routine simultaneously without interference?
There is no problem with running the same code concurrently. Only if the code is accessing global variables then it can be problematic. If you are not sure then it is better to use CallSubDelayed and run all the shared stuff on the main thread.
 

masmukti

Member
Licensed User
Longtime User
what do you mean the link is not correct?
i am running in local host, and compiling is success
 

vfafou

Well-Known Member
Licensed User
Longtime User
Hello!
Is it possible to place in a queue the Events called from clients?
I have a database congestion problem when many concurrent clients are trying to login, because I do some queries in order to return to the clients their current status.
Thank you in advance!
 
Top