B4J Code Snippet Resumable Subs (wait for / sleep) in server handlers

Resumable subs can only work when there is a message queue.

By default, server handlers end when the Handle sub is completed. They do not create a message loop.
If you want to wait for an event then you need to call StartMessageLoop and later StopMessageLoop.

Example of handler that downloads a page and returns it as the response:
B4X:
Sub Handle(req As ServletRequest, resp As ServletResponse)
   Download(resp)
   StartMessageLoop '<---
End Sub

Sub Download (resp As ServletResponse)
   Dim j As HttpJob
   j.Initialize("", Me)
   j.Download("https://www.example.com")
   Wait For (j) JobDone(j As HttpJob)
   If j.Success Then
     resp.Write(j.GetString)
   End If
   j.Release
   StopMessageLoop '<----
End Sub

The Handle sub cannot be a resumable sub itself.

Why?

- Wait For / Sleep must be called before the call to StartMessageLoop or they will never be executed.
- Wait For / Sleep code flow is equivalent to Return so if they appear before StartMessageLoop then the message loop will never be created and the handler will complete before the event is raised.

WebSocket handlers do have a message loop so nothing special should be done there.

Tip: Use the correct library.
OkHttpUtils2_NonUI for server apps and OkHttpUtils2 for UI apps.

Above tip is no longer correct. As OkHttpUtils2 is a b4xlib, you should always use OkHttpUtils2.
 
Last edited:

jinyistudio

Well-Known Member
Licensed User
Longtime User
Hi

Do i need to implement JobDone in your example like following red block?

B4X:
Sub Handle(req AsServletRequest, resp AsServletResponse)
   Download(resp)
   StartMessageLoop '<---
End Sub

Sub Download (resp AsServletResponse)Dim j AsHttpJob
   j.Initialize("", Me)
   j.Download("https://www.example.com")WaitFor (j) JobDone(j AsHttpJob)If j.Success Then
   resp.Write(j.GetString)EndIf
   j.Release
   StopMessageLoop '<----
End Sub


Sub JobDone(j AsHttpJob)
   if j.success then
      ...
   end if
End sub
 
Last edited:

derez

Expert
Licensed User
Longtime User
The Handle sub cannot be a resumable sub itself.

I tried sleep in a handle sub, it works in debug mode, doesn't work in release.
Caused me some head-banging till I remembered this thread...
 

derez

Expert
Licensed User
Longtime User
Solved, I found that I need to use the service httpUtils2Service and httpjob class instead of the libraries.

With the example from the first post, modified slightly, I have
B4X:
Sub Class_Globals
    Private st As String
End Sub

Public Sub Initialize
 
End Sub
Sub Handle(req As ServletRequest, resp As ServletResponse)
    st = req.GetParameter("action")
    Download( resp)
    StartMessageLoop '<---
End Sub

Sub Download (resp As ServletResponse)
    Dim j As HttpJob
    j.Initialize("", Me)
 
    If st = "on" Then
        j.Download("http://192.168.0.190/cgi-bin/relay.cgi?on")  ' error is from here
    Else
        j.Download("http://192.168.0.190/cgi-bin/relay.cgi?off")
    End If
    Wait For (j) JobDone(j As HttpJob)
    If j.Success Then
        resp.Write(st)
    End If
    j.Release
    StopMessageLoop '<----
End Sub

When run I get this error:
Waiting for debugger to connect...
Program started.
2017-11-02 18:10:11.136:INFO::main: Logging initialized @581ms to org.eclipse.jetty.util.log.StdErrLog
2017-11-02 18:10:11.279:INFO:eek:ejs.Server:main: jetty-9.4.z-SNAPSHOT
2017-11-02 18:10:11.330:INFO:eek:ejs.session:main: DefaultSessionIdManager workerName=node0
2017-11-02 18:10:11.330:INFO:eek:ejs.session:main: No SessionScavenger set, using defaults
2017-11-02 18:10:11.332:INFO:eek:ejs.session:main: Scavenging every 600000ms
2017-11-02 18:10:11.339:INFO:eek:ejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler@6276ae34{/,file:///D:/B4J/Kankun/Objects/www,AVAILABLE}
2017-11-02 18:10:11.350:INFO:eek:ejs.AbstractNCSARequestLog:main: Opened D:\B4J\Kankun\Objects\logs\b4j-2017_11_02.request.log
2017-11-02 18:10:11.404:INFO:eek:ejs.AbstractConnector:main: Started ServerConnector@4f933fd1{HTTP/1.1,[http/1.1]}{0.0.0.0:50190}
2017-11-02 18:10:11.405:INFO:eek:ejs.Server:main: Started @852ms
Emulated network latency: 100ms
Error occurred on line: 20
java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:273)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:268)
at javafx.application.Platform.runLater(Platform.java:83)
at anywheresoftware.b4j.objects.FxBA.postRunnable(FxBA.java:17)
at anywheresoftware.b4a.keywords.Common.CallSubDelayed4(Common.java:518)
at anywheresoftware.b4a.keywords.Common.CallSubDelayed2(Common.java:502)
at b4j.example.httpjob._download(httpjob.java:93)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at anywheresoftware.b4a.shell.Shell.runVoidMethod(Shell.java:657)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:234)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:159)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:90)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:93)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:77)
at anywheresoftware.b4j.object.JServlet$Handle.run(JServlet.java:130)
at anywheresoftware.b4a.keywords.SimpleMessageLoop.runMessageLoop(SimpleMessageLoop.java:30)
at anywheresoftware.b4a.StandardBA.startMessageLoop(StandardBA.java:26)
at anywheresoftware.b4a.ShellBA.startMessageLoop(ShellBA.java:114)
at anywheresoftware.b4a.keywords.Common.StartMessageLoop(Common.java:146)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:303)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:159)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:90)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:93)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:77)
at b4j.example.main.main(main.java:29)

The httpjob works correctly on the target when run from a UI b4j app :
B4X:
Dim j As HttpJob
j.Initialize("sendon",Me)
j.Download("http://192.168.0.190/cgi-bin/relay.cgi?on")

Help ?
 
Last edited:
Top