Embedded Mode Web Client¶
Introduction¶
Architecture¶
Example Use Cases¶
In-Process vs External Web Application¶
Runtime Configuration¶
The required runtime configuration for the embedded mode is mostly related security items, to explicitly allow (or disallow) access to the application from the 3rd party embedded app. Even if the embedded web app is running from within the FWD Server, it is considered a 3rd party app, and needs to be configured explicitly.
A. Configuring the embedded web app and the spawned clients¶
The embedded app can be run in the same Jetty server as the FWD's Virtual Web Client, or in a standalone server. But regardless, to be able to spawn a FWD Client, a session with the FWD Server must be established - and this session requires a SSL certificate. This is specified in the embeddedWebApp
node, under the server/default
or server/[name]
:
<node class="container" name="embeddedWebApp"> <node class="boolean" name="enabled"> <node-attribute name="value" value="TRUE"/> </node> <node class="string" name="authAlias"> <node-attribute name="value" value="ehotel"/> </node> <node class="string" name="authKeyStore"> <node-attribute name="value" value="path/to/private-key.store"/> </node> <node class="string" name="authKeyStorePassword"> <node-attribute name="value" value="[private-key-store-password]"/> </node> </node>
The SSL connection to the FWD Server will be established using the certificate specified in the authKeyStore
file, which can be opened via the authKeyStorePassword
. The FWD session will be established under the authAlias
FWD username. Also, this certificate must be installed in the FWD Server's certificate section, and a FWD process account must be configured:
<node class="process" name="ehotel"> <node-attribute name="enabled" value="TRUE"/> <node-attribute name="description" value="E-Hotel Server"/> <node-attribute name="master" value="FALSE"/> <node-attribute name="server" value="FALSE"/> <node-attribute name="alias" value="ehotel"/> </node>The user under which the embedded app has authenticated with the FWD Server must be allowed to spawn clients, via some specific configurations in the FWD directory:
- specify the mode under which the embedded app is connected as
headless
and that it runs the clients in embedded mode:<node class="container" name="ehotel"> <node class="boolean" name="headless"> <node-attribute name="value" value="TRUE"/> </node> <node class="container" name="webClient"> <node class="boolean" name="embedded"> <node-attribute name="value" value="TRUE"/> </node> </node> </node>
- specify the default OS account under which the spawning will be performed:
<node class="container" name="webClient"> <node class="boolean" name="override"> <node-attribute name="value" value="TRUE"/> </node> <node class="string" name="defaultAccount"> <node-attribute name="value" value="[OS-account]"/> </node> </node>
- allow this OS account to spawn clients:
<node class="container" name="trustedspawner"> <node class="container" name="000100"> <node class="resource" name="resource-instance"> <node-attribute name="reference" value="[OS account]"/> <node-attribute name="reftype" value="TRUE"/> </node> <node class="trustedSpawnerRights" name="rights"> <node-attribute name="allow" value="true"/> </node> <node class="strings" name="subjects"> <node-attribute name="values" value="ehotel"/> </node> </node> </node>
B. Allow the embedded application to specify launch options for the spawned client.¶
In the FWD directory, theremotelaunchoption
ACLs are used to specify which bootstrap configuration parameters can be set as a command-line argument for the spawned client. This includes:
- the
client:cmd-line-option:startup-procedure
, to specify the FWD entry point. - the
client:cmd-line-option:parameter
, to specify any-param
arguments for the FWD client. - the
client:web:embedded
, to mark the client as embedded.
<node class="container" name="remotelaunchoption"> <node class="container" name="000100"> <node class="resource" name="resource-instance"> <node-attribute name="reference" value="[boostrap-key]"/> <node-attribute name="reftype" value="TRUE"/> </node> <node class="remoteLaunchOptionRights" name="rights"> <node-attribute name="allow" value="true"/> </node> <node class="strings" name="subjects"> <node-attribute name="values" value="ehotel"/> </node> </node> </node>
where:
[bootstrap-key]
is one of the bootstrap keys which can be set by the FWD client.ehotel
is the FWD user account under which the embedded application has connected.
If a bootstrap key appears in the spawned client's arguments, and is not allowed via an explicit ACL, the spawning will fail. This is used to not allow the spawned client to i.e. target a different FWD server, than the one used to spawn it.
The default options are client:cmd-line-option:startup-procedure=emain.p client:web:embedded=true
, and these can be overridden via the embeddedWebApp/cfgOverrides
directory configuration.
C. Allow the embedded app to invoke 4GL code from the FWD Server¶
Implicitly, remote calls from the embedded app into the FWD Application Server are not allowed. Each program exposed to this 3rd party app must be explicitly allowed.
For the FWD user account under which the spawned FWD Client is running, access to these programs is configured viaremoteentrypoint
ACLs; there are at least two programs which need to be accessed:
fwd-embedded-driver.p
, which contains FWD's code to manage the FWD Client in embedded modeemain.p
, the main program started by the spawned FWD Client.
These programs and any other programs which can be ran from the embedded app, via the Javascript Appserver APIs, must be configured like this:
<node class="container" name="remoteentrypoint"> <node class="container" name="000300"> <node class="resource" name="resource-instance"> <node-attribute name="reference" value="path/to/program.p"/> <node-attribute name="reftype" value="TRUE"/> </node> <node class="remoteEntryPointRights" name="rights"> <node-attribute name="allow" value="true"/> </node> <node class="strings" name="subjects"> <node-attribute name="values" value="[FWD account]"/> </node> </node> </node>
The program must be specified usings its legacy location in the 4GL source code, a full path. This must be resolvable from the PROPATH, and must match the target used by the Javascript AppServer API call (e.g. the target of a RUN
statement from 4GL code).
D. Paths requests by the embedded application¶
The embedded application uses these URIs to perform requests:/embedded/launch
, to spawn a new FWD client/embedded/adm_windows.json
, to retrieve a JSON with the ADM windows determined by the conversion rules. This file is not mandatory to be used by the embedded application./embedded/index.html
, with the login page/embedded/custom
, holding custom Javascript or other resources. When the embedded app runs from within the FWD Server, this will serve resources from the[pkgroot]/custom
package./embedded/fwd
, to retrieve FWD builtin Javascript modules. When the embedded app runs from within the FWD Server, this will serve resources from thecom/goldencode/p2j/ui/client/gui/driver/web/res/
package.
Implementation Guide¶
A. Summary¶
The embedded application is built around an iframe which is the communication point with the converted application. This is performed by sending requests to execute 4GL code, which can either drive the iframe's ADM window or interpret the results to i.e. build custom HTML widgets, and more.
When implementing the embedded application, once you have the 4GL APIs implemented, for development purposes is easier to run it in standalone mode, and not via the FWD server. This allows you to debug and work on the UI without having to restart the FWD server.
On the embedded application side, there are both Javascript and 4GL files which require custom implementation. The embedded application project is usually a sub-folder of the main converted application, namedembedded/
. This folder has the following structure:
src/resources/custom
will contain custom-built Javascript code, images, other JS libraries, not included in FWD.src/resources/fwd
will have copies of all the FWD Javascript libraries, fromsrc/com/goldencode/p2j/ui/client/gui/driver/web/res/
folder in the FWD project. The files which can be used are:p2j.embedded.js
, main library which allows communication with the iframep2j.emain.js
, a library containing some APIs used for managing the ADM windows in embedded modep2j.module.js
andp2j.modules.css
, defines a tree-like JS widget which contains the modules available from the embedded application. These usually are all the ADM windows available to the user, from a main application page, in the legacy application.p2j.menu.js
andp2j.menu.css
, defines a JS widget which maps the legacy menubar for an ADM window.p2j.toolbar.js
, maps an ADM window's toolbar to a JS widget.p2j.chips.js
andp2j.chips.css
, a widget for managing the opened windows in embedded mode.
- on 4GL side, standard configuration relies on a
emain.p
program, which defines the login/logout business logic, application initialization logic, other helper 4GL procedures which are used to extract i.e. the toolbar, menubar, etc from an ADM window - the embedded application must write at a minimum an
index.html
and anemain.js
file, which are used to configure the application and define the HTML structure. Beside these, each ADM window can have a custom Javascript file, which can be used to augment the window with other widgets, specific to it. The example configuration in Hotel GUI can be used as a starting point to see how these can be defined.
The FWD conversion project for your application will need to include in the conversion (and in the generated Java code) the fwd-embedded-driver.p
program, emain.p
and any other programs written specifically for managing the embedded mode.
B. FWD conversion augmentation for ADM programs¶
The conversion process automatically augments the generated code with helper Java methods which expose information about the structure and widgets available in the ADM window. These APIs are emitted only if the conversion process determines that the ADM program for that window has the feature required to define the API:fwdGetWindow
, a 4GL function which will return the ADM window handle, in ADM terms, from the&SCOPED-DEFINE WINDOW-NAME
4GL preprocessor directive.fwdGetMainFrame
, a 4GL function which will return the main frame handle, in ADM terms, from the&SCOPED-DEFINE FRAME-NAME
4GL preprocessor directive.fwdGetToolbarFrame
, a 4GL function which will return the frame handle for a frame namedoptions-frame
.fwdGetADMVersion
, a 4GL function which will return the program's ADM version, from the&SCOPED-DEFINE ADM-VERSION
4GL preprocessor directive.fwdGetTabHandle
, a 4GL function which will return the handle for the ADM frame which acts as the tabulator. This is determined from theadm-create-objects
ADM procedure, from a code like:WHEN 0 THEN DO: RUN init-object IN THIS-PROCEDURE ( ... INPUT 'Folder-Labels = ' ... OUTPUT h-folder END.
Theh-folder
handle will represent the handle for this 'tabbed frame'. This frame is required so that the 4GL driver code can disablechangeFolderPage
notification from within ADM, as the tabs (pages in embedded mode) will be managed explicitly by the 4GL driver.fwdGetPages
, a 4GL function which will return a comma-separated list of page names (what thefolder-labels
defined in the snippet above has.fwdGetPageHandles
, a 4GL function which will return a handle array, with the frame handles which will be shown for a certain page. This again is computed from theadm-create-objects
procedure, from each code:RUN init-object IN THIS-PROCEDURE ( ... INPUT FRAME f-main, OUTPUT h-frame
which appears in aWHEN <pagenum>
branch.fwdGetHiddenEmbeddedWidgets
, a 4GL function which returns the widget names which must not appear on this ADM window - these are provided via FWD preprocessor hints, like:<hints> <uast name="hidden-embedded-widgets" datatype="string[]"> <array-val value="logout" /> <array-val value="exit" /> </uast> </hints>
for each program defining an ADM window.adm_windows.json
, a JSON structure with a list of all ADM windows, having a structure like:[ { "window-title" : "Hotel Demo FWD Application", "program-name" : "main-window.w" } ]
For each window, its title and ADM program is specified. This file is generated by the FWD conversion rules, and can be used to build the module list, but is optional.
C. Management of ADM windows¶
The ADM windows are driven in FWD by logic which exists as 4GL code in the fwd-embedded-driver.p
plus various Javascript code to manage the embedded application, and with the help of custom 4GL and Javascript logic, dependent on the how the converted application was written. This section will describe how FWD handles applications which rely on the ADM paradigm.
- the decorations will be removed (border, title); this is possible only in FWD, which will automatically discard the window's decoration, for any FWD Client running in embedded mode.
- status and message area can be hidden, if configured as such; in 4GL, you can't hide a window's status or message area once it has been realized. FWD disables these checks for embedded mode, allowing toggling the
status-area
andmessage-area
attributes, even if the window is visible. - no menubar will be presented - the window's
menubar
will be automatically set to unknown and, if a menubar was set, it will be saved to expose it to the embedded application. - toolbar and the window's tabs will be hidden; this is automatically performed by the FWD's embedded driver, with knowledge from the conversion generated 4GL functions.
When an ADM window is to be displayed, this is processed and only the frames determined to be the core part of the ADM window will be left visible. These frames (for the currently displayed page) will be relocated so that any empty space above and to the left will be occupied by them; in other words, the frames' location will be translated so that they will be located relative to the window's top-left corner. The window will also have its width set to match the total width of the visible frames, so that it can be centered in the iframe's boundaries.
Modal dialogs are supported; these are special in that they must prevent user action to both the embedded application components and the iframe; nested modal dialogs are supported, too. This is accomplished by using an overlay HTML div
component, which is carefully placed so that it is below the topmost modal dialog and above everything else (by manipulating its z-order
CSS property). When a modal dialog is discarded, this overlay will be moved behind the new topmost dialog, and so on, until no other modal dialog is displayed - at this time, the overlay will be hidden and the FWD iframe and embedded application are available to the user.
To make the FWD iframe 'blend' with the embedded application, the background color can be synced with that of the iframe's; this is automatically performed by providing a hex-format color (which can be retrieved from the iframe's background), and this will be automatically set to the 4GL window and parent frames for the ADM frames displayed in the current page. This is possible only via FWD's new BGCOLOR-RGB
extension attribute.
ADM has some peculiarities which were addressed via named events; for example, the ADM's standard folder component (aka tabs, or pages) are subscribed to changeFolderPage
event - but this will interfere when selecting a page from embedded mode, so FWD will unsubscribe the ADM folder component from this event. Additionally, the ADM code was changed so that a fwd-adm-page-changed
event will be published if the business logic decides to override the user's (or initially) selected page and select another one - this will notify FWD's driver and will automatically initialize the newly selected page.
The broker-dispatch
is used extensively in ADM to dispatch method calls to certain ADM components. In some cases, it was found that business logic can dispatch initialize
, adm-hide
or adm-destroy
, which will interfere with the currently selected page; FWD needs to be aware of these cases, so ADM's broker-dispatch
code was added the fwd-dispatch-pre
and fwd-dispatch-post
named events, published at the begining and the end of the broker-dispatch
call. These events can be used to track if the business logic touches the currently displayed ADM window, so that it can process it, to be compatible with embedded mode.
- each 4GL program for an ADM window must be ran persistent, to allow the FWD driver to manage it.
- after the program is ran, the window is initialized by calling the
initializeObject
(for ADM2) or dispatching theinitialize
method (for ADM1). - selecting a page will require running
selectPage
(for ADM2) orselect-page
(for ADM1) procedure, using the ADM program's handle. This procedure has an argument the page which requires to be selected - but, if the business logic decides to select another page, thefwd-adm-page-changed
event will be published and FWD will know that another page was selected by the business logic, and inform the embedded application of this (so that it can correctly highlight the visible page).
Not all ADM windows have pages - these cases are automatically identified by the FWD driver and the embedded app; just the window's main frame will remain visible, while everything else will be hidden.
More then one ADM window can be opened in the embedded application, but at a certain time only one can be visible. When switching between ADM windows in the embedded application, FWD's embedded driver will automatically modify the z-order for the selected window to be placed on top, and also hide all other windows, so that only this window remains visible. If the business logic decides to open another ADM window, this window will automatically be managed by FWD's embedded driver, and placed in the list of available windows; if the business logic focuses this window, FWD will show it as the current window automatically.
D. Embedded widgets¶
FWD provides a default set of widgets which can be used by the embedded application. These are mainly related to emulating the ADM window's 'decorations' (like toolbar, menubar, tabs) and presenting them as HTML widgets.
Each widget has a JS component (a javascript file used to build the widget in the embedded application), and 4GL code which provides the data to build this widget. The standard approach in FWD is for each widget (like a toolbar button, or a menu item) to have a counterpart 4GL widget, to which the action (i.e. button press) is delegated, via an APPLY event TO widgetHwnd
statement. This allows the business logic for your application to have minimal changes, and the embedded app to reflect any change in the code or legacy 4GL UI immediately. This is not mandatory, other custom approaches can be used.
D.1. Building the module tree¶
The module tree is a JS component implemented in FWD by p2j.module.js
. Its data can be built from adm_windows.json
or retrieved via custom 4GL logic - this logic will reside in a custom fwdGetModules
functions, which will return a JSON structure with the following data:
[ { "label" : <text>, "mnemonic" : <char>, "module" : <module ID>, "items" : [ { "label" : <text>, "mnemonic" : <char>, "module" : <module ID>, "items" : [ ... ] }, ... ] }, ... ]
This is a tree-like structure which defines the module's title (label), an optional mnemonic, the module ID (a specific ID uniquely identifying the module in your application - a program name, other identifier) and a list of children modules. Only the leaf nodes will actually open a module.
D.2. Building the pages¶
The tabs (pages) of an ADM window are computed by default from the 'folder frame' in your ADM window, by invoking the fwdGetPages
function in this window's program. Alternatively, your embedded application can define a custom initEmbeddedPageCustom
4GL procedure in your emain.p
implementation, which will build the pages from some other component - this is especially useful if your application has the pages emulated via some custom widget, like a TABSET
.
initEmbeddedPage
procedure in FWD's fwd-embedded-driver.p
- so you will need to manually ensure that only the required frames in this ADM window will be made visible. The definition of this procedure is:procedure initEmbeddedPageCustom. def input param phandle as handle. def input param pnum as int. def input param hwin as handle. def input param hframe as handle. def input param bgColorRgb as int. def output param pages as char. end.
where:
phandle
is the handle of this ADM windows (e.g. module) external program.pnum
is the page being shown.hwin
is the handle for the 4GL window showing this ADM window.hframe
is the handle for the main frame for this ADM windowbgColorRgb
is the background color used by the iframe (you can use this to synchronize the background color for the 4GL frame/window to match the iframe, via FWD'sBGCOLOR-RGB
extension attribute).pages
is an output parameter which must be returned as a comma-separated list of page names.
- determine the list of pages in this ADM window
- leave visible only the ADM frames specific to this page
- center/position the main frame to top row
- update the background color for main-frame's parent frame(s) and window, via the
BGCOLOR-RGB
attribute.
D.3. Building the toolbar¶
Usually, each ADM window comes with one or more toolbars. By default, FWD will use the frame returned by fwdGetTabHandle
to gather all visible buttons, and compute a JSON structure containing the details about these buttons. Alternatively, if your emain.p
defines a custom fwdListToolbar
function, you can override this default implementation and manage your own way to build the toolbar.
function fwdListToolbar returns char (input hframe as handle, output btns as handle extent 100, output btnNum as int).
where:
hframe
is the handle to the toolbar's frame.btns
will be an array holding handles to the toolbar's components, as in 4GL.btnNum
is the number of valid entries inbtns
.- the returned value will be a JSON structure describing the toolbar.
The JSON structure is:
[ { "handle" : <HANDLE attribute>, "name" : <NAME attribute>, "label" : <LABEL attribute>, "tooltip" : <TOOLTIP attribute>, "image" : <image path>, "sensitive" : <SENSITIVE attribute>, "width" : <WIDTH-PIXELS attribute>, "height" : <HEIGHT-PIXELS attribute>, "x" : <X attribute>, "y" : <Y attribute> }, ... ]
The above attributes are usually from a BUTTON
4GL widget, but these can be replaced with data computed in other means. Note that the toolbar will order the components based on their X
value.
The image must be a resource available via the embedded/custom/path/to/image
path. This image can be overriden in the embedded application via custom function configured with the p2j.toolbar.js
- this will just return a custom HTML for that toolbar's item, if possible - otherwise, FWD will use button with the image, as it appears in the legacy application.
When a toolbar element is clicked by the user, by default a APPLY "choose" to <handle>
will be executed via the inMsg_fwdToolbarItemSelected
procedure in fwd-embedded-driver.p
. This behaviour can be overridden in emain.p
, if a custom inMsg_fwdToolbarItemSelected
is defined:
procedure inMsg_fwdToolbarItemSelected. def input param hitem as handle. end.
Knowing the element's handle, the business logic can perform custom actions to run the code associated with that element.
D.4. Building the menu¶
An ADM window usually can have a menubar. This menubar's components will be exposed to the embedded application automatically, if the embedded application is configured as such. The menubar is driven by default via the p2j.menubar.js
component. The structure of the JSON is:
{ "sensitive" : <MENUBAR:SENSITIVE attribute>, "visible" : <MENUBAR:VISIBLE attribute>, "menu-items" : [ { "handle" : <HANDLE attribute>, "type" : <TYPE attribute> "subtype" : <SUBTYPE attribute>, "label" : <LABEL attribute>, "accelerator" : <ACCELERATOR attribute>, "mnemonic" : <the label's mnemonic>, "sensitive" : <SENSITIVE attribute>, "read-only" : <READ-ONLY attribute>, "visible" : <VISIBLE attribute>, "toggle-box" : <TOGGLE-BOX attribute>, "checked" : <CHECKED attribute>, "menu-items" : [ ... ], }, ... ]This is a tree-like structure describing the entire menubar. When a sub-menu or menu-item is selected from the embedded emulated menubar, an
APPLY
will be emulated in the business logic for that element, with:
- a
MENU-DROP
event, for aSUB-MENU
element - a
CHOOSE
event, for aMENU-ITEM
element - a
VALUE-CHANGED
event, for aMENU-ITEM
which has theTOGGLE-BOX
attribute set.
The APPLY
will target the handle for that menubar element. In fwd-embedded-driver.p
, the inMsg_fwdMenuItemSelected
procedure will be invoked and will take care of executing the APPLY
.
E. Writing the custom driver code¶
E.1. Configuration¶
The standard configuration is done in Javascript, after thep2jInitialized
API message is received from the 4GL business logic, via P2J-REMOTE-CALL
(informing the embedded web app that the auth passed). The configuration requires to specify the following details, in a Javascript object:{ "toolbar" : { "container" : "fwdToolbar", // ID of the toolbar element "customImage" : customImageFunc, // function(imagePath, tooltip, label) "imageFolder" : "embedded/custom/", // the root folder to add as a prefix "handler" : p2j.toolbar // instance of p2j.toolbar }, "menubar" : { "container" : "fwdMenubar", // ID of the menubar element "event" : openMenuItem, // function(handleId, callback) "handler" : p2j.menubar // instance of p2j.menubar }, "module" : { "container" : "fwdModule", // ID of the module element "event" : showEmbeddedWindow, // function(moduleId, windowTitle) "handler" : p2j.modules // instance of p2j.modules }, "chipset" : { "container" : "window-chip-set", // ID of chipset element "remove" : removeWindow, // function(chipId) "interact" : showWindow, // function(chipId) "handler" : p2j.chips // instance of p2j.chips }, "initCallback" : hideLoading, // function(), executed after initialization "populateWindows" : undefined, // function(JSON) - populate the available windows list, if not using a p2j.module "topWindowChanged": undefined, // function(windowId), executed when the top window has switched. windowId is a HWND value. "populatePages" : populatePages, // function(windowId, pagesTitles[], currentPage) "windowDestroyed" : windowDestroyed, // function(windowId) "defaultBgColor" : defaultBGColor, // colorRgb "embedded-status-area" : true, "show-status-callback" : showStatus, // function(txt) "embedded-message-area" : true, "show-messages-callback" : showMessages // function(txt[]) }
where:
- the
toolbar
,menubar
,module
andchipset
are optional. Each of them have a:container
, representing the ID of a parent DOM element where the widget will be attachedhandler
, a JS object which will be used to construct the widget
initCallback
is a function executed after the configuration has been performed. This can automatically load a certain window or just do nothing.populateWindows
is an optional function which loads the module list from theadm_windows.json
.topWindowChanged
is an optional function executed when the top window has changed, via the business logic or by the user.populatePages
is a function which allows you to build the pages switching widgetwindowDestroyed
is an optional callback exeuted when an open window has been destroyed (by the user or business logic).defaultBgColor
is optional and represents a hex-encoded color, to be set as the background for the 4GL window.embedded-status-area
andshow-status-callback
are optional and are used to receive notifications from the FWD client, when a new status message has been set for the current window. If the flag is set, the 4GL window will have its status-area removed.embedded-message-area
andshow-messages-callback
are optional and are used to receive notifications from the FWD client, when aMESSAGE
statement is executed with the current window. If the flag is set, the 4GL window will have its message-area removed.
- for the toolbar, how to process the HTML element describing an certain item in the toolbar.
customImage
is an optional function which, if it returns a valid value, this will represent the HTML code for this toolbar's item. It can be used to override a certainimagePath
and build a more friendly element.- when the toolbar item uses the same image as the Virtual Desktop FWD client,
imageFolder
represents URI prefix to be added to the HTML image's path, so that it can be retrieved.
- for the menubar, the
event
represents an optional function which will be executed before the 4GL action (representing the user's click on item) is performed. This can be - for the module,
event
is a mandatory function which will perform the logic to open a module (i.e. the ADM window), when the user selects it. Thep2j.emain.showEmbeddedWindow
is the default function which can be used for performing the 'module invocation. - the
chipset
is usually used to manage the opened ADM windows. Theremove
callback is executed when a chip is deleted, andinteract
when the chip is selected:- for
remove
, a function is required to specify how to close the ADM window.p2j.emain.removeWindow
can be used. - for
interact
, a function is required to specify what to do when an ADM window is selected.p2j.emain.switchWindow
can be used.
- for
API¶
The Javascript AppServer defines APIs to access 4GL business logic, by invoking external programs, internal procedures or user-defined functions, and more. The APIs are split into:- emulating RUN statement or user-defined function calls
- managing named events via
PUBLISH
,SUBSCRIBE
orUNSUBSCRIBE
- other misc APIs
A. running 4GL business logic via external programs, internal procedures and user-defined functions¶
This starts by obtaining a handle to an external program ran persistent, which is allowed access to, via theremoteentrypoint
configuration; once this handle value is obtained, any public internal procedures or functions can be ran from within it. The available APIs are accessed via p2j.embedded
container:
invokeExtenalProgram(callback, name, args)
, to invoke an external program.invokeExternalProgramPersistent(callback, name, args)
, to invoke an external program ran persistent.invokeFunction(callback, name, args, handle)
, to invoke a user-defined function in the specified external program's handle.invokeProcedure(callback, name, args, handle)
, to invoke an internal procedure in the specified external program's handle.
A persistent program can be deleted via the deleteHandle(callback, handleId)
API.
callback
argument, this is a function which will be called when the response is received from the FWD Application Server, for this call. The function will take a single argument, msg
, which is a JSON with the following structure:
type
set top2j.remote.INVOKE
id
set to the request message IDmode
set top2j.remote.RESPONSE
payload
, with the response data
payload
will include:
handle
, with the handle ID for external programs ran persistent, or procedure or function calls. This is a hex-encoded 64 bit value, as string.return
, the return value from this call; this can beRETURN-VALUE
for procedures or the function's return value, otherwise.error
,error-num
,error-msg
- set if there was an ERROR condition raisedquit
, set if there was a QUIT condition raisedstop
, set if there was a STOP condition raisedarguments
, key-value pairs with any OUTPUT or INPUT-OUTPUT arguments. The keys are always lowercased, and case-sensitive. The order is indeterminate.
When there is a args
argument, this will be key-value pairs with the argument values. The keys must always match argument names for the target, and they are case-insensitive. The order in which they appear is indeterminate, and no mode is required. TABLE, TABLE-HANDLE, EXTENT, BUFFER is not supported.
B. Managing named events¶
Named events work in a similar way as 4GL events, with some extensions in FWD:- an event can be published globally, to all FWD clients subscribed to it.
- you can subscribe to an event in this FWD client or globally, and receive the publish notification.
- you can unsubscribe from an event.
When publishing events, the publisher can be an external program ran persistent by the JS AppServer, or a special kind of resource, called EXTERNAL RESOURCE
in FWD. This is a kind of resource which can be created by the embedded application, to manage published events. It will reside in the associated FWD Client's context, but it will need to be managed by the embedded application (i.e. deleted and created). This external resource is created via the createResource(callback)
API, and can only be deleted via the deleteHandle(callback, handleId)
API.
subscribe(callback, global, subscriber, event, publisher, method)
, used to subscribe a specific resource to the specified event, where:callback
is a function which will receive the responseglobal
will listen for events from any FWD clientssubscriber
is the subscriber's ID, and is optional; if not set, an external resource will be created and received via thepayload.subscriber-id
, in the response.event
is the event namepublisher
is the publisher, which can be a hex-encoded handle ID representing a persistent program or an external resourcemethod
is the Javascript function when the event is raised; the arguments will be passed via an 0-indexed array, with each argument string-encoded.
unsubscribe(callback, global, subscriber, event, publisher)
, used to unsubscribe a specific resource from the specified event, where the arguments have the same meaning as insubscribe
.publish(event, publisher, args)
, to publish an event on behalf of the specified publisher, which can be the handle for an external program or an external resource.
callback
will have the following structure:
type
set top2j.remote.SUBSCRIBE
,p2j.remote.UNSUBSCRIBE
orp2j.remote.PUBLISH
id
set to the request message IDmode
set top2j.remote.RESPONSE
payload
, with the response data, which will be:subscriber-id
- hex-encoded handle ID; only forsubscribe
andpublish
error
,error-num
,error-msg
- set if there was an ERROR condition raisedevent
, the published event, only forpublish
parameters
, a 0-indexed array with the parameters, only forpublish
C. Other misc APIs¶
External resources are a special kind of resource available only in FWD. These can be created by non-4GL 3rd party applications, and currently can be used only for named event mananagement. They reside on the FWD Client's context, but are managed by the embedded application, created or destroyed:createResource(callback)
will create this external resource, with its handle passed to thecallback
function - this has a single argument a JSONmsg
, andpayload.resource-id
will be the hex-encoded 64 bit value, represneting the resource's ID, as string.
An external resource or an external program ran persistent can only be deleted by the embedded application which created it, via the deleteHandle(callback, handleId)
API; also, this API can only delete these kind of resources - anything else is prohibited. The callback
's msg.payload
will have only error
, error-num
, error-msg
set if there was an ERROR condition raised.
The embedded application can work with widgets via their HWND
value - this is an internal ID uniquely identifying each widget, like buttons, menu-items, etc. They can be exposed via OUTPUT arguments from invoked 4GL business logic, and the HWND can be used to emulate an APPLY
to them, for example to press a 4GL button. This can be performed via the applyEvent(event, widgetId)
API.
NO-ERROR
mode is not enabled by default, but this can be managed via the silentErrorEnable()
and silentErrorDisable()
APIs; once set, it is assumed that all calls will be performed under NO-ERROR
mode, until this mode is unset.
D. Executing JS code in embedded app, from the 4GL business logic.¶
A new 4GL statement, P2J-REMOTE-CALL
, was added in FWD - this is a special statement which executes a remote call into the embedded app, synchronously - it will block and wait a response from the embedded application.
The statement has a single argument a string value, a JSON structure; this JSON is mandatory to have an api
key with the name of the remote API which will be invoked. The other keys can be used to transmit additional data.
P2J-REMOTE-CALL
on the remote, embedded app, side, you will be required to register a parser and a handler function for each api
used by the P2J-REMOTE-CALL
function. The p2j.embedded.registerIncomingRequest(api, respCallback, parseCallback)
must be used to specify, for each api
:
respCallback
, a callback function having as single argument a JSON object.parseCallback
, a function used to interpret the JSON sent by theP2J-REMOTE-CALL
, and build a new JSON structure which will be used as an argument forrespCallbac
.
It is important for the response and parse functions to finish execution and terminate cleanly, as the 4GL side will wait for a response message that it finished execution - this is a simple OK
message implicitly sent by p2j.embedded.js
.
If an api
is no longer needed, this can be deregistered via p2j.embedded.deregisterIncomingRequest(api)
.
setPageLoadedCallback(callback)
- callback executed when the iframe has loadedsetPageClosedCallback(callback)
- callback executed when the iframe has closedsetSocketDisconnectedCallback(callback)
- callback execued when the web socket has disconnectedsetSocketReconnectedCallback(callback)
- callback execued when the web socket has reconnectedsetWaitForInputCallback(waitForInputCallback)
- callback executed when the iframe is in a state waiting for input from the user; in such cases, the iframe must be visible and the user must be able to send input (keyboard, mouse).
api
's used to manage the embedded application or windows:
P2J-REMOTE-CALL('~{ "api":"p2jInitialized" ~}')
, executed after the business logic has performed the authentication, to notify the embedded app that it can initializeP2J-REMOTE-CALL('~{ "api":"showError" , "error":"<msg>" ~}')
- shows a specific error messages in the embedded application's login screenP2J-REMOTE-CALL('~{ "api":"admPageRefresh", "windowId":"<windowid>", "page":"<pageNum>" ~}')'
- special API executed when the page has been refreshed (hide, view, etc) by the ADM business logicP2J-REMOTE-CALL('~{ "api":"admPageChanged", "windowId":"<windowid>", "page":"<pageNum>" ~}').
- special API executed when the page has been changed by the ADM business logicP2J-REMOTE-CALL('~{ "api":"notifyDisconnect" ~}').
, executed beforeemain.p
program has terminated.
© 2016-2019 Golden Code Development Corporation. ALL RIGHTS RESERVED.