This section covers topics of relevance for developers.
This document list the differences when a Natural subprogram is executed by the Natural Subprogram Server (Natsubsrv) instead of by a terminal session.
It is not an introduction to Natural programming — for that, see the Natural documentation.
These differences are caused by two properties:
No terminal. The session is a server session; there is no screen and no keyboard.
No state between calls. One session serves the requests of many end users, one after another, and is reset at every request boundary.
This document covers the following topics:
The subprogram is entered by a CALLNAT and leaves by its normal end
or by ESCAPE ROUTINE.
The DEFINE DATA PARAMETER block is the only data channel into and
out of the subprogram.
Every request names its own library and its own user; both are established by the
server before the CALLNAT.
The session is reset at the request boundary, so every call starts from a defined state and leaves nothing behind.
In a subprogram, you can define the name of the API parameter that will be mapped to a Natural parameter.
This is done by adding the prefix oapi.n: followed by the
desired property name in the comment of the field.
For example, in Natural:
#PARAM1 (A1) /* oapi.n:nameParm IN
This creates an API parameter named nameParm, which is mapped to the #PARAM1 Natural
parameter.
| Statement | Reason |
|---|---|
TERMINATE |
Ends the session the server owns. The request is abandoned and output parameters are not written back. |
STOP |
Leaves the CALLNAT without writing the output
parameters back. Use ESCAPE ROUTINE.
|
FETCH |
Starts a program inside the request. The rest of the subprogram does not run and output parameters are not written back. |
RUN |
Requires source and a compiler in a session that serves requests. |
LOGON |
The library environment is established by the server per request. |
REINPUT |
Requires a terminal. |
Maps (INPUT USING MAP, WRITE USING
MAP), PROCESS PAGE |
Requires a terminal. |
SET KEY |
Requires a terminal. PF key settings are reset per request. |
SET CONTROL |
Requires a terminal. |
OPEN CONVERSATION, CLOSE
CONVERSATION |
Conversational RPC is not supported. |
DEFINE DATA CONTEXT |
Context variables are the data of a conversation, and
there is none. The area belongs to the single CALLNAT that
creates it and is released with it, so it carries nothing from one call to the
next. Use the PARAMETER block.
|
| Statement | What is Possible |
|---|---|
STACK |
Data can be placed on the stack and read back within the same request. Commands are not executed. |
INPUT |
Only without terminal I/O, that is, reading data from the
stack. Any INPUT that would address a screen fails.
|
A blocked subprogram blocks the session it runs in, and with it every request that would have been served by it.
Do not wait for user interaction, external events, or locks held elsewhere.
Keep CALLs to 3GL modules short. The server cannot interrupt a
running 3GL call, so the session stays occupied for as long as the call lasts.
Keep units of work short. Database locks are held until END
TRANSACTION or BACKOUT TRANSACTION, so a long unit of work
keeps other requests away from the same records.
The same applies to the subprogram as a whole: its runtime is the time the session is unavailable to everyone else.
Every call must stand on its own: it must not carry data forward, and it must not expect data left behind by an earlier call.
The following are put back to a defined state at every request boundary:
Global data areas, including the COMMON GDA.
Application independent variables (DEFINE DATA INDEPENDENT).
Session parameters, including those changed with SET GLOBALS and
*LANGUAGE.
The limit counters MADIO, MAXCL, MAXERR,
and LT.
Steplib chains.
Retained sets and command IDs.
PF key settings.
A GDA is empty at the start of every request unless the subprogram fills it itself,
and a set retained by RETAIN in one request does not exist in the
next.
Context variables (DEFINE DATA CONTEXT) are no way around this either.
Without a conversation, they are not a data area that spans calls: the context exists
for the one CALLNAT and is gone when it returns, so it can neither carry
state forward nor be read by the next request. For more information, see Must not be
used.
*ETID and the ET data stored under it belong to the session, not the end
user. They are shared by all requests the session serves and must not be used to carry
per-user state.
End your own units of work. Complete every one with END TRANSACTION or
BACKOUT TRANSACTION before returning. The server does not commit.
At the request boundary, the server normally backs out the transaction (BACKOUT
TRANSACTION) and closes open work files. On Linux, you can disable either
behavior with backoutTransactionPerRequest and
closeWorkFilesPerRequest; otherwise both behaviors always occur.
Notes:
CLOSE
MODE=USER does not keep it open across the request boundary.
Errors that occur while a request is being processed are intercepted by the server and reported to the caller with error number, line, status, and object name.
It is recommended to use ON ERROR to return errors as data the caller
can act on — an error code and message in the output parameters — and reserve
unhandled errors for genuinely unexpected conditions. Clean up on the error path
exactly as on the success path.
| Variable | Value |
|---|---|
*SERVER-TYPE |
CALLNAT |
*USER |
The user of the current request in uppercase, up to 8 characters. |
*NET-USER |
The user of the current request, like *USER.
On z/OS, it holds the user of the session the server started instead — the
same value as *INIT-USER — and therefore does not identify the
requesting user.
|
*LIBRARY-ID |
The library named by the current request. |
*INIT-USER, *INIT-ID,
*ETID |
The session the server started, not the requesting user.
On z/OS, *INIT-ID holds a session number that the server
increments for each new session.
|
*DEVICE |
A batch device. |
[ ] No TERMINATE, STOP, FETCH,
RUN, and LOGON.
[ ] No REINPUT, no maps, no SET KEY, no SET
CONTROL; INPUT only without terminal I/O.
[ ] No OPEN CONVERSATION / CLOSE CONVERSATION, no
DEFINE DATA CONTEXT.
[ ] The subprogram read only values that it had set itself; it did not read values from a GDA, independent variables, or values retained from an earlier call.
[ ] Every unit of work ended with END TRANSACTION or BACKOUT
TRANSACTION, on every path.
[ ] Every work file opened is closed, on every path.
[ ] Nothing blocks — neither the subprogram nor a 3GL module it calls.
[ ] The end user is identified through *USER, not through
*NET-USER, *INIT-USER, or *ETID.