B4J Tutorial [BANano+jSERVER=BANanoServer] Going full circle

Discussion in 'B4J Tutorials' started by alwaysbusy, Nov 15, 2019.

  1. alwaysbusy

    alwaysbusy Expert Licensed User


    The next version of BANano will come with the BANanoServer.b4xlib library. B4J has an excellent jetty based server: the jServer library. BANanoServer uses this lib and provides you with a lot of goodies to make building WebApps with B4J very simple.

    NOTE: BANano will still work without the BANAnoServer Library as it did before in case you want to use another backend like PHP, but only having to program in one language has huge advantages and is what RAD is al about. :)

    When you will look at the code of BANanoServer, ABM users will recognize the powerful Caching system, Root and http2 filters. In BANano, all that code is removed from your own projects view (but still accessible as it is an open source .b4xlib).

    Some 'easy-to-use' methods to communicate between the browser and the server are added. (to call methods from each other and to exchange files).

    There are some things that I would like to make better (I had to do a couple of hacks like the #If BANano trick and using the Abstract Editor in a non-UI project (for jServer needed) has some limitations, but hopefully @Erel will have some ideas how we can overcome them. I will talk with him privately if something can be done.

    A BANano+BANanoServer WebApp basically exists of the following components:

    The target is writing a tutorial and make a reallife WebApp to keep track of versions for your own apps (FIXES, NEW, CHANGES, DEPRECIATED, etc). During this tutorial we will explore all the parts one need to make an app:

    (will be extended)

    And here is also a sneak peek at how the communication will work. You will see the similarity I tried to archieve between the SERVERPage and BROWSERPage classes to make it very easy for even a novice programmer.

    Main (Setting up de server and BANano):
    'Non-UI application (console / server application)
    #Region Project Attributes
    #MergeLibraries: True
    #IgnoreWarnings: 16, 10, 14, 15  
    #End Region

    Sub Process_Globals
    ' must be public in Main because everyone has to use this one.
       Public Server As BANanoServer
    End Sub

    Sub AppStart (Args() As String)
    ' initialize the BANano Server
    ' for our caching
       Server.SessionMaxInactiveIntervalSeconds = 60
    Server.CacheScavengePeriodSeconds = 30
    ' for our upload
       Server.UploadAllowedFileTypes = "ZIP;JPG"
    Server.UploadMaxSize = 1024*1024*5 ' 5 MB
       ' Other important ones
       ' the prefix of our BROWSER (BANano only code) classes that miror their SERVER counterpart (default is "BROWSER")
       Server.BROWSERPrefix = "BROWSER" ' IMPORTANT to set this one if you do not use this default Prefix!
    ' initialize BANano
       Server.BANano.Initialize("BANano""BANanoServer" ,1)
    ' some B4J typical libs we want to be ignored by the Transpiler
    ' transpile all the BANano b4J code to javascript
    ' add your SERVER classes, not the BROWSER parts:
       Server.AddWebSocket("/ws/" & Server.BANano.StaticFolder & "/about" , "SERVERAbout")
    ' set the start page one will go to if they enter the site by the root
       Server.StartPage = "about"
    ' lets start the B4J server
       If Server.PortSSL <> 0 Then
    End If
    End Sub

    'Return true to allow the default exceptions handler to handle the uncaught exception.
    Sub Application_Error (Error As Exception, StackTrace As StringAs Boolean
    Return True
    End Sub
    SERVERAbout (a SERVERPage that talks with its BROWSERPage counterpart):
    ' B4J compatible ONLY code, no BANano allowed.
    'WebSocket class
    Sub Class_Globals
    Private ws As WebSocket
    Private CacheReport As BANanoCacheReport  
    ' a class used by both the SERVER and the BROWSER
       Private Human As SHAREDHuman
    End Sub

    Public Sub Initialize
    End Sub

    Private Sub WebSocket_Connected (WebSocket1 As WebSocket)
       ws = WebSocket1
    ' Lets update the cache with this class
       CacheReport = Main.Server.UpdateFromCache(Me, ws)
    Log("PageID: " & CacheReport.BANPageID)
    Log("Comes From Cache:" & CacheReport.ComesFromCache)
    Log("Is a reconnecting socket: " & CacheReport.IsReconnected)
    ' We can already send stuff to the browser, using normal B4J jServer code with Future
       Dim fut As Future = ws.RunFunctionWithResult("AddInTheBrowser"Array(2030))
    Log("BROWSER says sum of 20+30=" & fut.Value)
    ' or if no result is expected back
       ws.RunFunction("BROWSERTest"Array("Hello from the SERVER!"))
    ' IMPORTANT lets tell the browser we are ready to receive call from the browser
       ' Uses the classic WebSocket_Connected and WebSocket_DisConnected events on the browser size
       ' Use Main.Server.SendReady(ws, "ws") if you use the advanced events OnOpen, OnMessage, OnServerReady, ...
    End Sub

    Private Sub WebSocket_Disconnected
    End Sub
    ' event raised to distribute incoming events coming from the BROWSER
    public Sub BANano_ParseEvent(params As Map)
       Main.Server.ParseEvent(Me, ws, CacheReport.BANPageID, params)
    End Sub
    ' event raised when a file has been uploaded
    public Sub BANano_Uploaded(status As Int, fileName As String)
    Log(fileName & " = " & status)
    Select Case status
    Case 200 ' OK
           Case 500 ' was not a POST call
           Case 501 ' file to big
           Case 502 ' file type not allowed
       End Select
    End Sub

    ' called from the BROWSER
    public Sub AddOnTheServer(first As Int, second As Int) As Int
    Return first + second
    End Sub

    ' called from the BROWSER
    public Sub SERVERTest(msg As String)
    Log("BROWSER says '" & msg & "'")
    End Sub

    ' called from the BROWSER
    public Sub GetHuman() As String
    ' make a human that the browser can retrieve
       Human.Name = "Alain"
       Human.Gender = 
    Return Human.ToJSON
    End Sub

    ' called from the BROWSER
    public Sub SetHuman(HumanStr As String)
    Log("Human.Name (changed in the browser) is " & Human.Name)
    End Sub
    BROWSERAbout (a BROWSERPage that talks with its SERVERPage counterpart):
    'BANano compatible ONLY code. You can not use typical B4J libraries here.  Use their BANano version (if it exists)
    'Making changes in a this module/class in B4J debug mode will NOT have any effect until recompiled!
    #Region BANano
    ' <-------------- IMPORTANT! This is because we want this module to be transpiled by BANano
    #End Region

    Sub Class_Globals
    Private BANano As BANano 'ignore
       Private ws As BANanoWebSocket
    ' a class used by both the SERVER and the BROWSER
       Private Human As SHAREDHuman  
    ' BANano Custom View objects that are on the UploadForm layout
       Private SKButton1 As SKButton 'ignore
       Private SKTextBox1 As SKTextBox 'ignore
       Private SKLabel1 As SKLabel 'ignore
    End Sub

    'Initializes the object. You can NOT add extra parameters!
    Public Sub Initialize
    ' does the browser support websockets?
       If ws.IsSupported Then
    ' here we connect to our SERVERAbout websocket class using the 'classic' B4J Websocket events WebSocket_Connected and WebSocket_Disconnected
           ws.Initialize("ws://" & BANano.Location.GetHost & "/ws/" & BANano.StaticFolder & "/about")
    End If
    End Sub

    ' Server says socket is ready
    Sub WebSocket_Connected()
    Log("My B4J PageId: " & BANano.GetPageID)
    ' load de layoout for the upload
    ' running a method on the server and wait for the result
       Dim value As Int = 0
    Dim prom As BANanoPromise = ws.RunFunctionWithResult("AddOnTheServer"Array(3 , 6))
    Log("SERVER says sum of 3+6=" & value)
    ' getting the human class from the server
       Dim humanJson As String
    Dim promHuman As BANanoPromise = ws.RunFunctionWithResult("GetHuman"Null)
    ' making a change to the object
           Human.Name = "Alain Bailleul"
    ' and sending it back
    Log("Hello Human " & Human.Name & " (" & Human.Gender & ")")
    ' note that this will be logged BEFORE the sum and the Human retrieval.  If you want it AFTER then it has to be put inside the Promise
       ws.RunFunction("SERVERTest"Array("Hello from the BROWSER!"))

    ' get some assets from the server/internet
       Dim Files As List = Array("./assets/B4X.jpg""https://jsonplaceholder.typicode.com/posts""./assets/UTF8.txt")
    Dim responses() As String
    Dim dataUrlProm As BANanoPromise = BANano.GetFileAsDataURL(Files.Get(0), Null)
    Dim jsonObjProm As BANanoPromise = BANano.GetFileAsJSON(Files.Get(1), Null)
    Dim textProm As BANanoPromise = BANano.GetFileAsText(Files.Get(2), Null"UTF-8")
    ' return when all 3 promises are done
       Dim prom As BANanoPromise = BANano.PromiseAll(Array(dataUrlProm, jsonObjProm, textProm))
    For i = 0 To responses.Length - 1
    Log(Files.Get(i) & ":")
    End Sub

    Sub WebSocket_Disconnected(event As BANanoEvent)
    Log("Websocket closed")
    End Sub

    ' called from the SERVER
    public Sub AddInTheBrowser(first As Int, second As Int) As Int
    Return first + second
    End Sub

    ' called from the SERVER
    public Sub BROWSERTest(msg As String)
    Log("SERVER says '" & msg & "'")
    End Sub

    ' uploading a file to our server
    Sub SKButton1_Click (event As BANanoEvent)
    Dim Response As BANanoObject
    ' get the file object
       Dim theFile As BANanoObject = SKTextBox1.File
    If theFile = Null Then
    "Please select a file first!")
    End If
    ' prevent big uploads
       If theFile.GetField("size") > 1024 * 1024 * 5 Then
    "File is to big!")
    End If
    ' returns a promise
       Dim prom As BANanoPromise = BANano.UploadFile(theFile)
           SKLabel1.Text = 
    "Upload status: " & Response.GetField("status").Result
    End Sub
    SHAREDHuman (a SHAREDCode class that can be used both in a BROWSERPage and SERVERPage class):
    'This class is shared between the BANanoServer and BANAno
    'Use to specify which code is specific for the BANanoServer (B4J) and the BANano Browser part
    '  #If #B4J
    '  #Else
    '  #End If
    'Making changes in a this module/class in B4J debug mode will NOT have any effect on the BANano side until recompiled!
    #Region BANano
    ' <-------------- IMPORTANT! This is because we want the non specific B4J code in this module to be transpiled by BANano
    #End Region

    Sub Class_Globals
    Public Name As String
    Public Gender As String
    End Sub

    'Initializes the object. You can add parameters to this method if needed.
    Public Sub Initialize
    End Sub

    public Sub ToJSON() As String
    Dim m As Map = CreateMap("name": Name, "gender": Gender)
    #If B4J
           Dim json As JSONGenerator      
       #Else 'if BANANO
           Dim json As BANanoJSONGenerator      
    #End If
    Return json.ToString
    End Sub

    public Sub FromJson(jsonStr As String)
    Dim m As Map
    #if B4J
           Dim json As JSONParser      
       #Else 'if BANAno
           Dim json As BANanoJSONParser
    #End If
       m = json.NextObject
       Name = m.Get(
       Gender = m.Get(
    End Sub
    Last edited: Nov 26, 2019
  2. OliverA

    OliverA Expert Licensed User

    If I could do a +100 on the likes I would. This is awesome!!!!
    DonManfred and alwaysbusy like this.
  3. Mashiane

    Mashiane Expert Licensed User

    So excited. So Im sure you also have .GetFileAsBlob & .GetFileAsBase64 isnt it? ;)
    joulongleu likes this.
  4. Roberto P.

    Roberto P. Well-Known Member Licensed User

    fantastic. great Alain

    Now it will be difficult to understand the differences of ABMaterial and Banano Server and to understand when and why to use one or another?

    expect your updates.

    Happy holidays
  5. Marc Van Cauwenberghe

    Marc Van Cauwenberghe Member Licensed User

    I too am getting a bit dizzy
    What to use and when?
    A feature matrix would be great.

    But anyway. Thanks for the great work.

  6. Mashiane

    Mashiane Expert Licensed User

    That is just a front end for BANano, you can always start with the basics on BANano for Dummies by Example and anytime you need help you can always shout!

    All the best!
  7. Johan Hormaza

    Johan Hormaza Active Member Licensed User

    Mashiane and alwaysbusy like this.
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice