Special Considerations

This section covers topics of relevance for developers.


Developing Natural Subprograms for Natsubsrv

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:

Execution Model

  • 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.

Mapping OpenAPI Names

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.

Restricted Statements

Must not be used

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.

Restricted use

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.

Must not block

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.

Statelessness

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.

Transactions and Work Files

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:

  1. A unit of work that is not committed within the request is lost.
  2. Records must not be held across calls.
  3. A work file must be opened, used and closed within one request. CLOSE MODE=USER does not keep it open across the request boundary.

Error Handling

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.

System Variables

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.

Checklist

  • [ ] 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.