Overview
Genero Web application (GWA) is a Genero application running entirely in the browser.
The GWA is an end-to-end web application solution that bundles together a Genero Browser Client, Genero p-code modules (42m), and resources into a single web page application (SPA). The GWA build process creates WebAssembly (external link) (wasm) files for your application — a format that enables deployment on the web and execution of the application in a browser. The GWA uses the emscripten (external link) open source compiler to create the virtual machine (runtime) for the Genero p-code in the 42m modules. This results in a highly-optimized executable application that runs almost as fast on the browser as native code on the desktop, while being portable and safe.
With a GWA application, you do not need a runtime system (fglrun) process on the server side. In fact, there is no other software server side except a web server. A GWA application is by default a "progressive web app" (PWA); meaning once loaded from a web server, it can continue to run offline without any connection to the internet.
All GUI features available in the Genero Browser Client are fully supported in a GWA application,
so Genero forms and styles run as usual together with Genero interaction statements such as
INPUT/INPUT ARRAY/CONSTRUCT/MENU/DIALOG
, and so on.
A GWA application has certain similarities with Genero Mobile for Android™(GMA) and Genero Mobile for iOS (GMI) application; however, the main difference is that the Genero runtime is not compiled into native code, instead the runtime system is compiled to WebAssembly (external link) modules (wasm) for execution on the browser.
Another noticeable difference between GMA/GMI and GWA, is that a GWA application emulates a file system in the browser, whereas in a GMA/GMI application the file systems of Android/IOS® can be used.
Prerequisites
- The GWA uses Service Workers that require you to run applications with HTTPS activated, or to use localhost. Even if in development localhost can be used, it is mainly necessary to develop with HTTPS activated. Of course, in production, HTTPS is mandatory.
-
Note:
Self-signed certificates are not supported on all browsers.
GWA use cases
The following are examples of some common GWA use cases:
- Run application from a URL: Build a GWA application like you would a mobile application for GMI/GMA except with the advantage that you do not have to managed it through App Store; the installation is an ordinary web URL. Once the URL is selected by the end user, the application is downloaded, starts immediately, and is also immediately available for offline use.
- Launch from mobile home screen icon: For mobiles with Chrome/Edge browsers, an installation menu appears, prompting the end user to save this application under a mobile home screen icon. Starting the application from this icon does not require an internet connection. With other browsers, users can bookmark the application with the same offline functionality. In IOS Safari® for example, add to home screen.
- Launch from desktop icon: For desktops with Chrome/Edge browsers, an install button appears in the address bar, which allows the user to save the application to a desktop icon, turning the application into a desktop application with offline functionality.
- Facilitate large numbers of simultaneous end users: GWA applications are the medium of choice when it comes to giving access to large number of simultaneous end users. Standard Genero desktop applications are server based and scale well up to 1000 and even more end users, but if the potential simultaneous end user count is much higher (for example, 100,000 or more) then it is time to switch to GWA architecture – with web services server side and the program logic running entirely client side.
- Create GWA applications written in Genero BDL: This is typically the case for a GWA
application: code written entirely in Genero runs client side, communication with the server is done
via Genero
com
API/REST services. GWA applications can therefore compete with other client-side JavaScript technologies; with the added advantage that you can code your applications in Genero BDL.
Recommendations for use of Genero Web application
GWA has these best practice recommendations:
- Install the GWA and the REST services on the same host: As GWA is actually a web application running in a browser, you can only communicate via REST web services to exchange data with a third party server. Therefore, we recommend you install the GWA and the REST services on the same host and port; otherwise, you may face a lot of Cross-Origin Resource Sharing (CORS) issues.
- Set CORS headers on external servers providing web services: If you need to access
another REST service located on another host and port machine, and if the REST requests are basic –
simple
GET
orx-WWW-form-urlencoded
type requests – you will have to set the following CORS header in the second host machine:Access-Control-Allow-Origin:"https://host:port"
Features with limited support
- A minimum subset of the Genero Web Services REST API is supported: the
com
API (excluding the specific XML API) is based on the JavaScriptXMLHttpRequest
object. - A limited number of GWS REST functions are available. When using fglrestful to generate REST stub files, only the JSON API
(
util.JSON
) is supported. Therefore, you must use option--legacyJSONApi
of the fglrestful tool. Only requests with JSON or plain text are supported. XML is not supported. - The
RUN
command is under construction to supportRUN "fglrun program"
(GUI only) without OS shell underneath. - Only the SQLite database is supported at this time.
- All socket or pipe Channel API are not working and will return an error, whereas file Channels are supported.
Unsupported features
The following language features are not supported:
- SOAP is not supported.
- XML encryption API is not supported.
IMPORT xml
,IMPORT
security are not supported.IMPORT json
is not supported
File system
When the GWA application is launched in the browser, a virtual UNIX-like file system emulation of your GWA application is created in memory.
- /app directory contains the .42m modules and program resources (with possible subdirectories).
- /fgl contains the Genero runtime assets.
- /tmp is for temp files.
- /home is the default directory when the application starts. The /home is also the "persistent" directory of the GWA because it stores data even if the application is restarted (or the browser is restarted). For more information about managing persistence in the GWA, go to Managing persistence in the file system.
os.Path.mkdir
function as needed.Setting environment with fglprofile
Configure environment settings with FGLPROFILE entries.
If you have a fglprofile file in the root of your program directory, it will be bundled with your application package. The GWA adopts the same behavior as GMA/GMI for setting environment. For more information, go to Setting environment variables in FGLPROFILE (mobile).
- If you have to set
FGLLDPATH
because some42m
files are located on subdirectories, you must create afglprofile
with an entry calledmobile.environment.FGLLDPATH
- You can also set other environment variables such as
FGLIMAGEPATH
In this example, environments for FGLLDPATH and FGLIMAGEPATH are set. "
$FGLAPPDIR
" refers to the virtual file system /app directory in memory.For a portable variant of your GWA application, set these environment variables as shown:#portable variant which works also for GMI/GMA mobile.environment.FGLLDPATH = "$FGLAPPDIR/dbsync" mobile.environment.FGLIMAGEPATH="$FGLAPPDIR/image_dir:$FGLDIR/lib/image2font.txt"
For a non-portable variant, or for GWA specific, set the environment variables as shown:# non portable: GWA specific mobile.environment.FGLLDPATH = "/app/dbsync" mobile.environment.FGLIMAGEPATH = "/app/image_dir:/fgl/lib/image2font.txt"
Managing persistence in the file system
As directories in a GWA application are in memory, this means the lifetime of those directories is the same as the lifetime of the application. Understanding the file system will help you develop a strategy for making data persistent.
Before the GWA application starts, the file system emulation is populated with data provided by
gwabuildtool. For example, the "/app"
directory is populated with the data from the gwabuildtool
--program-dir
option.
The /home directory is excluded from the pre-population of the file system; all data which can be bundled lands in /app directory.
Home directory
The /home directory is the "persistent" directory of the GWA, where the
application state can be stored even if the application is restarted or the browser is restarted.
Data in the /home
directory is stored in the browser cache, or to be more precise,
in IndexedDB (external link) caches. Therefore, if you want to store data, you
must put your files in the /home directory using explicit channel operations
such as os.Path.copy
commands as shown in the next section.
Database persistence
Database persistence is managed via a SQLite database file, which must be placed in the
/home
directory.
If an SQLite database file needs to be created at application startup, you must write some
4GL
code to create the file before calling the CONNECT
instruction.
# check for database
VAR home_db="/home/mydatabase.db"
#...
IF NOT os.Path.exists(home_db) THEN --Copy to home directory first time
--the initial mydatabase.db is bundled by gwabuildtool into /app
IF NOT os.Path.copy("/app/mydatabase.db", home_db) THEN
DISPLAY "Can't copy initial db"
EXIT PROGRAM 1
END IF
END IF
# connect to database
Accidental clearing of the cache
Explicitly cleaning the browser cache for the site hosting a GWA application will remove all stored data of a GWA application.
As there is always the danger that an end user may clear browser cache and thus delete the contents of a GWA application /home directory unintentionally, your GWA application should be prepared to send the data to a REST server via REST API, or synchronise data with the server by other combinations of GWS calls.
Web application manifest file
A web application manifest file is a JSON file that provides information about a GWA.
The manifest has some parameters that allows you to change icons, add screenshots, change application title, and so on, according to the Web Application Manifest (external link) specification.
When you call the gwabuildtool tool to build the GWA, it looks for a dedicated manifest in the programdir/gwa of your program directory.
In the manifest file, the path to icons and screenshots are set by the "src"
property. "src" does not refer to a web server path, instead gwabuildtool
searches for those images in the absolute path or a path that is relative to your
programdir/gwa path.
So an image named "src" : "foo.png"
will be found in
programdir/gwa/foo.png. This is the recommended default
directory for those kind of images.
- An image named "/Users/bar/foo.png" will be found in /Users/bar/foo.png.
- An image named
"../foo.png"
will be found in programdir.
Packaging web components
Web components must be packaged with the GWA application because they are preloaded and must also be available offline.
The directory programdir/webcomponents is the default location for web components in your program directory. All components located there will be automatically packaged.
Furthermore, the gwabuildtool has a
--webcomponent
switch to bundle a web component from another location in the file
system other than programdir/webcomponents. The switch can
be used multiple times in the build command.
Including built-in web components
COMPONENTTYPE
attribute. This affects the build in the following way:- It adds the specified Built-in web components from
$FGLDIR/webcomponents to the build. For example, if a
COMPONENTTYPE
matches "fglsvgcanvas", the fglsvgcanvas web component is automatically included in the GWA. - It raises a warning if, for a particular
COMPONENTTYPE
, no web component has been packaged.
A call to ui.Interface.filenameToURI
(asset_in_filesystem) can be
used to produce URLs to make a web component visible.
Notice that URL paths starting with "webcomponent" refer to files bundled in the programdir/webcomponents subdirectory and URLs starting with HTTP(S) can refer to assets in the internet.
The gwa_webcos
demo is a good source to check the various asset paths.
Application updates
A GWA API queries the status of the application and can update it automatically even when not active.
When a GWA application ends, an application ended page is displayed similar to GBC. (This page can be customized). By default a "Reload page" button is shown on this page, which enables a restart of the application.
If the application has been updated server side in the meantime, clicking the Reload page button will start the updated version of the application.
Automatic application updates
As the GWA uses JavaScript service workers, the application can even be updated when the program is not active; the browser automatically checks the server side for a new version and downloads the new version if there is one.
By default the application is also updated automatically when it is reloaded. This can happen when the user clicks the Reload page button on the application ended page, by using the browser reload menu action (F5 usually), or by reopening the browser with the same application URL.
To keep track of application updates, the gwabuildtool has an
--app-version
switch to set the application version. This version can be queried by
using methods in the gwa.app
package module.
User-driven update
The gwa.app
package module has a function to query the up-to-date status of the
application and perform a user-driven update immediately if a version difference is detected.
If a network connection is not available, the current installed version in the browser is used. Internally, the GWA tries first to contact the server for an update, if this fails, after 500ms the cached application version is used.
It is recommended that you code to check for updates after your application starts by calling the
gwa.app.getServerVersionAndBuild()
method.
For an example performing a user-driven update, explore the GWA "update" demo in your GWA installation directory. If installed in FGLDIR, you will find the demo in $FGLDIR/demo/gwa/update or if installed in a separate directory, you will find it in gwa-install-dir/demo/gwa/update.
Single sign-on (SSO)
Single Sign-On (SSO) allows users to sign in through an Identity Provider (IdP) using one set of credentials and access multiple applications, such as a Genero Web application. Enabling SSO increases security and makes it easier for users to sign in to your GWA application.
Genero provides SSO based on the OpenID Connect (OIDC) protocol via its Genero Identity Provider (GIP). For more information on GIP and SSO, refer to the Single Sign-On User Guide. Any identity provider that supports OIDC will work with GWA and can manage the creation of user accounts as well as authentication and authorization during sign-in.
Overview
When your GWA application is registered with an IdP for SSO, the end-user enters credentials once in a login form on the browser to get authorization to use the application. If your application performs further request to REST services, an access token is required. Your application must perform a request via a password-credential flow to the IdP in order to receive an authorization and an access token for this purpose.
CORS constraints
Access-Control-Allow-Origin
HTTP header on the other
server in order to authorize GWA to contact the IdP on a different host. The header must provide the
hostname and port number of the server where the IdP is located. For example, if you IdP runs on a
server called "cube", then add the following
header:Access-Control-Allow-Origin:"https://cube:6394"
This can be achieved on
the GAS by setting the SERVICE (for HTTP)
entry of the as.xcf
with the server address: #...
<SERVICE>
<HEADER Name="Access-Control-Allow-Origin">https://cube:6394</HEADER>
</SERVICE>
#...
OAuth API
In your GWA application, you must call functions of the OAuthAPI library in order to authenticate and retrieve an access token to call additional REST services. See the example in Typical BDL OAuth initialization.
Of course, you can also use fglrestful --auth yes to generate the stubs to
connect using OAuth protocol to any REST services. However, as GWA has a limited number of GWS
functions available at this time, you must use option --legacyJSONApi
of the fglrestful tool, and only requests with JSON or plain text are supported.
XML is not supported.
Typical BDL OAuth initialization
IMPORT FGL OAuthAPI
-- IDP constants
CONSTANT idp_issuer = "https://host:port/gas/ws/r/services/GeneroIdentityProvider"
CONSTANT client_id = "XXXXX" -- OAuth client id as registered in your IdP
CONSTANT private_id = "ZZZZZ" -- OAuth private id as registered in your IdP
-- OAuthAPI
DEFINE metadata OAuthAPI.OpenIDMetadataType
DEFINE tokens OAuthAPI.OpenIdCResponseType
DEFINE user_login, user_pswd STRING
DEFINE s INTEGER
DEFINE r BOOLEAN
MAIN
-- Initialize user_login and user_pswd (from a login dialog for example)
-- CALL AskForLoginAndPassword() RETURNING s, user_login, user_pswd
IF s<0 THEN EXIT PROGRAM 0 END IF
-- Fetch IDP metadata
CALL OAuthAPI.FetchOpenIDMetadata(5, idp_issuer)
RETURNING metadata
IF metadata.issuer IS NULL THEN
DISPLAY "ERROR: Could not fetch OAuthAPI.OpenIDMetadataType"
EXIT PROGRAM 1
END IF
-- Perform password credential flow
CALL OAuthAPI.RetrievePasswordTokenForNativeApp(5, metadata.token_endpoint,
user_login, user_pswd,
client_id,
private_id,
NULL)
RETURNING tokens
-- Initialize OAuthAPI via returned tokens
LET r = OAuthAPI.InitNativeApp(5, tokens,
client_id,
private_id,
metadata.token_endpoint)
IF NOT r THEN
DISPLAY "ERROR: Could not initialize native app with OAuthAPI.InitNativeApp"
EXIT PROGRAM 1
END IF
-- Use any GWS client stub generated with fglrestful --oauth yes --legacyJSONApi
END MAIN
Front calls with runOnServer
The runOnServer
front call allows you to start an application via the
Genero Application Server (GAS) from a Genero Web application (GWA).
A runOnServer
call is basically implemented as described for Genero Mobile for
iOS (GMI) and Genero Mobile for Android
(GMA) applications in mobile.runOnServer; however, there are two
notable differences or things to consider when implementing runOnServer
calls in a
Genero Web application:
- In GWA, unlike for GMI/GMA, both
RUN
andRUN ... WITHOUT WAITING
can be used in any combination.If we wanted to limit it to a
RUN
, theGBC
would need to know if it is in arunOnServer
state.The
runOnServer
application finishes when theGBC
loaded with the remote application detects application termination (meaning the session ended). - In the GWA, you can omit the full origin
(http://host:port) in the
GAS
URL argument, because the origin of the starting GWA application and the GAS application must be the same. (A Cross-Origin Resource Sharing (CORS) issue could arise, but this would require a modification in the GBC.) Therefore, the call for the GAS URL argument can be reduced to a simple path like/ua/r/simple
. In GMI/GMA, this is not possible because the native application always runs on another origin.