This is an overview of the different possibilities in setting up connection between 4GL application (as web client) and Web API (as web service). A few distributed application examples are provided here. Lycia 4GL is used both for client and server parts in these code samples but any part can be easily replaced with any other modern programming language.
Any 4GL function can be published as web service without any coding or code reading at all. LyciaWeb will translate HTTP requests to 4GL function calls, interaction statement actions and field updates. By default, any deployed application can be called this way. For security reasons only functions starting with the "web_" prefix can be called. The prefix pattern can be changed during runtime.
The API is accessed via /sapi and /api URLs of LyciaWeb, for stateful and stateless usages respectively. For example, in default installation the stateful Web API can be accessed from the same computer with URLs with the http://localhost:9090/LyciaWeb/sapi prefix.
The Lycia Web API application is a standard Java servlet and can be run on any standard Java servlet container. Session tracking is done by means of the container. So it has access to all the container options for session handling. By default, session is stored in cookies (the JSESSIONID parameter), but it can be also URL parameter or SSL session.
The format of URL for access to stateful API should be:
http://{host}:{port}/LyciaWeb/sapi/{guiserver_port}/{application}
or
http://{host}:{port}/LyciaWeb/sapi/{command}/{name}?{arg1}={val1} &{arg2}={val2}...
where
{host} |
host name, e.g. localhost |
{port} |
port number, which equals 9090 by default |
{guiserver_port} |
port number, which equals 9090 by default |
{application} |
application name from folder ‘progs‘ of Application Server |
{command} |
command for execution by current application. E.g. set, get, setfocus, callmethod, closewindow, etc. |
{name} |
command parameter, like function name, field name, etc. |
{arg…} |
parameter name |
{val…} |
parameter value |
This is the example of the link that can be used to start the application:
http://localhost:9090/LyciaWeb/sapi/cms/cms?user=username&passwor d=12345
Servlet container is a part of a web server that interacts with the servlets. Among its known implementations, there are:
Apache Tomcat
Oracle Weblogic
IBM WebSphere
GlassFish
Jetty
JBoss, etc.
The best way to write a web function is to write a new function inside which an old function is called. This will help you to avoid changing all the names in the calls to the old function inside your 4GL project. Name the function web_<someone>, it should have the same name for security reasons.
This example illustrates how to call 4GL application through another 4GL application.
Let's assume that there is some 4GL application called web_srv_func_sum.exe which we will use as a test application. This application contains additional function web_f_sum
:
function web_f_sum(a, b)
define a,b int
return a + b;
end function
To invoke this function in another application, we establish the web session and call the application web_srv_func_sum.exe.
main
define sesweb.Session
define r int
call ses.setLyciaApplication("localhost:9090/LyciaWeb/sapi",
"default-1889","web_srv_func_sum.exe")
call ses.callMethod("web_f_sum", 5,7) returning r
display"returns : ", r
end main
There is an option to include nested arrays in the code. The example below shows it can be realized within the code (Server side application web_srv_func_array2d.exe):
main
define str string
define ch CHAR
define a INTEGER
let a = 25
display "web_f_sum(2,3) returns ", web_f_sum(2, 3)
prompt "Input the value:" for str
if str is null then
display "string was not entered - exit"
exit program 0
end if
display "WAIT 3 sec"
sleep 3
end main
function web_f_sum(a, b)
define a,b int
return a +b;
end function
function web_matrix_int4x4_tranform(arr) -- the function is called from client side
define arr, res array[4, 4] of int
define i, j int
for i=1 to 4 step 1
j = 1 to 4 step 1
let res[i, j] = arr[j, i]
end for
end for
return res
end function
The program from the client side will look as follows:
main
define sesweb.Session
define rq web.Request
define rs web.Response
define r int
define arr, res array[4, 4] of int
define i, j int
call ses.setLyciaApplication("localhost:9090/LyciaWeb/sapi","default-1889", "web_srv_func_array2d.exe")
call ses.get() returning rs
display "get: " , rs.getBody()
call ses.get() returning rs
display "get: ", rs.getBody()
sleep 20
display "call web_f_sum(102, 304)"
call ses.callMethod("web_f_sum", 102, 304) returning r
display "returns : ", r
for i=1 to 4 step 1
for j=1 to 4 step 1
let arr[i, j] = 4*(j-1)+i-1
end for
end for
for i=1 to 4 step 1
display arr[i, 1], ", ", arr[i, 2],", ", arr[i, 3],", ", arr[i, 4]
end for
display ""
call ses.callMethod("web_matrix_int4x4_tranform", arr) returning res
display "result: ", res
for i=1 to 4 step 1
display res[i, 1], ", ", res[i, 2], ", ", res[i, 3], ", ", res[i,4]
end for
end main
This program illustrates interaction statements usage for server side:
main
define nam char(20),
age int,
mstat char(1),
total char(30)
options input wrap
options message line last
open form f1 from "fields_fm"
display form f1
display "Press F1 to call the function" at 2, 2 ATTRIBUTES(GREEN)
display "!" TO b_ok
input nam, age, mstat, total from pers_rec.*
ON KEY (F1)
message "KEY EVENT F1"
let total = trim(nam)||" "|| age ||" "||trim(mstat)
display total to formonly.total
end input
end main
The interaction statements usage for client side:
main
define ses web.Session
define rq web.Request
define rs web.Request
define r int
call ses.setLyciaApplication("localhost:8080/LyciaWeb/sapi", "default-1889", "web_srv_form1.exe")
call ses.get() returning rs
display "get1: ", rs.getBody()
call ses.set("name")
call ses.key("nextfield")
call ses.get() returning rs
display "get2: ", rs.getBody()
call ses.set("33")
call ses.key("nextfield");
call ses.set("M")
call ses.get() returning rs
display "get3: ", rs.getBody()
call ses.key("F1")
call ses.get() returning rs
display "get4: ", rs.getBody()
call ses.key("nextfield")
call ses.get() returning rs
display "get5: ", rs.getBody()
call ses.key("accept")
end main
There is another lightweight mode for automatic program execution similar to Web API which does not require deployment to application server and does not involve any network interaction. It is called headless mode in this document. The application can be run simply from terminal in headless mode if QX_HEADLESS_MODE environment variable is set to 1. It will receive requests to its standard input and send responses to standard output. So some timed automatic tasks implemented in 4GL may be run via this mode using some shell script.
Below are some examples of interaction in the command line for the headless mode in XML.
The sample application in cvs: web_srv_form1.exe
In the given code, the symbols '<<<' and '>>>' mean server response and client response respectively. Customers can set their own in the end of the code to identify the response direction.
<?xml version="1.0" encoding="utf-8"?>
<response><openwindow>SCREEN</openwindow><displayform>f1</displayform><displayat
row="2" col="2" attr="green">Press F1 to call the function</displayat><displayt
o field="b_ok" attr="">!</displayto><input><nam/><age>0</age><mstat/><total/></i
nput><setfocus>formonly.nam</setfocus><interact_dialog/></response>
<<<
<?xml version="1.0" encoding="utf-8"?>
<fglevent name="nextfield"/>
>>>
<?xml version="1.0" encoding="utf-8"?>
<response><setfocus>formonly.age</setfocus><interact_dialog/></response>
<<<
<?xml version="1.0" encoding="utf-8"?>
<setvalue>12</setvalue>
>>>
<?xml version="1.0" encoding="utf-8"?>
<fglevent name="nextfield"/>
>>>
<?xml version="1.0" encoding="utf-8"?>
<response><setfocus>formonly.mstat</setfocus><interact_dialog/></response>
<<<
<?xml version="1.0" encoding="utf-8"?>
<setvalue>S</setvalue>
>>>
<?xml version="1.0" encoding="utf-8"?>
<fglevent name="f1"/>
>>>
<?xml version="1.0" encoding="utf-8"?>
<response><displayto field="total" attr="">  </displayto><interact_dialog/></response>
<<<
<?xml version="1.0" encoding="utf-8"?>
<fglevent name="nextfield"/>
>>>
<?xml version="1.0" encoding="utf-8"?>
<response><setfocus>formonly.total</setfocus><interact_dialog/></response>
<<<
<?xml version="1.0" encoding="utf-8"?>
<fglevent name="accept"/>
>>>