You can use various options on the RUN statement to execute remote procedures:
Run a synchronous external procedure, using the ON[SERVER]handle-variable[TRANSACTIONDISTINCT] option, as shown:
DEFINE VARIABLE hAppSrv AS HANDLE NO-UNDO.
CREATE SERVER hAppSrv.
hAppSrv:CONNECT(...).
. . .
RUN order.p ON SERVER hAppSrv TRANSACTION DISTINCT.
With a small change, this example can execute a remote procedure or a local procedure, depending on whether hAppSrv contains a server handle value or the value of the SESSION system handle. Thus, with the same statement, you can run order.p remotely (with a server handle) or locally (with the SESSION handle), and determine the choice at run time, as shown:
DEFINE VARIABLE hAppSrv AS HANDLE NO-UNDO.
CREATE SERVER hAppSrv.
IF NOT hAppSrv:CONNECT(...) THEN hAppSrv = SESSION. . . .
RUN order.p ON SERVER hAppSrv.
Run a remote persistent procedure, using the ON SERVER and PERSISTENT SEThandle-variable options, as shown:
DEFINE VARIABLE hAppSrv AS HANDLE NO-UNDO.
DEFINE VARIABLE hOrder AS HANDLE NO-UNDO.
CREATE SERVER hAppSrv.
hAppSrv:CONNECT(...).
. . .
RUN order.p PERSISTENT SET hOrderON SERVER hAppSrv TRANSACTION DISTINCT.
A synchronous remote persistent procedure executes similarly to the same persistent procedure run locally. The AppServer session creates the context for the persistent procedure as it starts to execute, and that context persists after it returns until the end of the AppServer connection or until the persistent procedure is explicitly deleted (see Deleting remote persistent procedures . Once a remote persistent procedure context is instantiated, you can execute any remote internal procedure or user-defined function that is defined within that remote persistent procedure.
The execution context for a remote persistent procedure is managed almost exclusively within the AppServer session where the persistent procedure is instantiated. However, the persistent procedure handle (hOrder in the example), as a proxy procedure handle, provides some additional access to the remote context from the client, allowing the client to delete the persistent procedure remotely. For more information on proxy procedure handles, see Understanding proxy procedure handles .
Run a remote external procedure on a stateless or state-free AppServer, using the ON SERVER option and either SINGLE-RUN SEThandle-variable (as shown below) or SINGLETON SEThandle-variable
DEFINE VARIABLE hAppSrv AS HANDLE NO-UNDO.
DEFINE VARIABLE serverHandle AS HANDLE NO-UNDO.
CREATE SERVER hAppSrv.
serverHandle:CONNECT(...).
. . .
RUN aproc.p SINGLE-RUN SET procHandle ON SERVER serverHandle.
RUN internalProcA IN procHandle (INPUT argIn1, OUTPUT argout1).
RUN internalProcB IN procHandle. (INPUT argin2, OUTPUT argout2).
DELETE PROCEDURE procHandle.
Although the preceding example illustrates the use of the SINGLE-RUN option, SINGLETON can be substituted with no other syntax differences. The purpose of both options is to allow the client to access internal procedures and user-defined functions found in the specified external procedure, without causing the client to be bound to the AppServer as it is when RUN PERSISTENT is used. Single-run and singleton operations offer the potential for greater AppServer throughput (since the lack of binding increases the availability of agents) and improved application performance (because the number of messages that the client must send to the AppServer is minimized).
In the case of both SINGLE-RUN and SINGLETON, the remote external procedure is not instantiated, and no message is sent to the AppServer, until the first internal procedure or user-defined function is called (RUN internal procA in the preceding example). On each such call to an internal procedure or user-defined function, the AVM sends the AppServer a message with the names of both the external procedure and the internal procedure or function; these are the only messages generated. Note that if AppServer access is being controlled by an export list, procedures run using the SINGLE-RUN and SINGLETON options should be added using the EXPORT( ) method. See ControllingAppServer entry points for more information.
The difference between SINGLE-RUN and SINGLETON lies in how the AVM deals with the remote external procedure instance(s) created on the server. In the single-run case, a new instance of the external procedure is created with each call into that procedure, and the instance is deleted after execution of the call. In the singleton case, once the external procedure is instantiated on the first call to an internal procedure or user-defined function, that instance remains in AppServer memory until it is explicitly deleted or the session ends. This singleton instance is used for all subsequent calls into the remote procedure running on this AppServer agent, whether the calls come from the client that originally caused the instantiation or from a different client.
The following restrictions apply to the use of the SINGLE-RUN and SINGLETON options:
The remote external procedure cannot run asynchronously; the use of the ASYNCHRONOUS option with the SINGLE-RUN or SINGLETON option results in an error. However, its internal procedures can run asynchronously.
The external procedure cannot have input or output parameters.
DELETE PROCEDURE only deletes the proxy handle on the client for a procedure run with the SINGLE-RUN or SINGLETON option. This is because a single-run procedure is deleted automatically by the AVM, and a singleton procedure can be running on more than one AppServer agent, where it can be deleted using ABL code running on the agent.
Run a synchronous internal procedure or user-defined function using the INhandle-variable option, as shown:
DEFINE VARIABLE hAppSrv AS HANDLE NO-UNDO.
DEFINE VARIABLE hProc AS HANDLE NO-UNDO.
FUNCTION fnOrders RETURNS LOGICAL IN hProc.
CREATE SERVER hAppSrv.
hAppSrv:CONNECT(...).
RUN order.p PERSISTENT SET hProc ON SERVER hAppSrv TRANSACTION DISTINCT.
. . .
RUN GetOrders IN hProc. IF fnOrders THEN
DISPLAY "Orders Found!"
In this example, both the GetOrders procedure and fnOrders user-defined function are defined in the remote persistent procedure specified by the proxy procedure handle, hProc.
For stateless and state-free AppServers, the same functionality is available by using the INhandle-variable option with RUN SINGLE-RUN or RUN SINGLETON (provided the external procedure associated with handle-variable does not have parameters). These options are preferable to RUN PERSISTENT because they do not bind the client to the AppServer or application service.
Note that running a synchronous remote internal procedure or invoking a remote user-defined function is syntactically identical to running it locally. OpenEdge automatically determines the right AppServer connection and remote procedure or function to execute from the specified proxy procedure handle.
Pass INPUT, OUTPUT, and INPUT-OUTPUT variable, TEMP-TABLE, or ProDataSet parameters, but notBUFFER parameters.
Run a procedure (internal or external) asynchronously, using the ASYNCHRONOUS option, as shown:
DEFINE VARIABLE hAppSrv AS HANDLE NO-UNDO.
DEFINE VARIABLE hAsync AS HANDLE NO-UNDO. DEFINE VARIABLE hAsync2 AS HANDLE NO-UNDO. DEFINE VARIABLE hProc AS HANDLE NO-UNDO.
DEFINE TEMP-TABLE ttOrder...
FUNCTION fnHighVolume RETURNS LOGICAL IN hProc.
CREATE SERVER hAppSrv.
hAppSrv:CONNECT(...).
RUN order.p PERSISTENT SET hProc ON SERVER hAppSrv TRANSACTION DISTINCT ASYNCHRONOUS SET hAsync.
DO WHILE NOT hAsync:COMPLETE: PROCESS EVENTS. IF hAsync:COMPLETE THEN
RUN GetOrders IN hProc ASYNCHRONOUS SET hAsync2 EVENT-PROCEDURE GetOrderRecords IN THIS-PROCEDURE (INPUT-OUTPUT TABLE ttOrder).
ELSE /* Order module not ready, so work on other stuff while waiting. */
. . .
END.
WAIT-FOR CLOSE OF THIS-PROCEDURE.
PROCEDURE GetOrderRecords: DEFINE INPUT PARAMETER TABLE FOR ttOrder. DEFINE VARIABLE lSee AS LOGICAL NO-UNDO.
IF fnHighVolume THEN DISPLAY "Orders > 100: Call in extra help!". MESSAGE "Do you want to see the orders?" UPDATE lSee. IF lSee THEN ... /* Display ttOrder. */ RETURN. END.
This example asynchronously instantiates a remote persistent procedure, order.p, and when available, asynchronously calls the GetOrders remote internal procedure defined in order.p to return a TEMP-TABLE parameter with order information. While waiting for the persistent procedure, order.p, to become available, the procedure does an unspecified amount of work. This part of the example is procedural, using the PROCESS EVENTS statement to handle the PROCEDURE-COMPLETE event each time through the loop. The status of the request is checked by testing the COMPLETE attribute of the asynchronous request handle (hAsync).
After the example runs GetOrders asynchronously, the example blocks using the WAIT-FOR statement to handle the PROCEDURE-COMPLETE event for the GetOrders request. When GetOrders completes, the associated event procedure, GetOrderRecords, executes. GetOrderRecords returns the TEMP-TABLE parameter and executes the remote user-defined function fnHighVolume (also defined in order.p) to provide warning of heavy order traffic. Note that the remote user-defined function is (and can only be) called synchronously.
Note: An external procedure called with RUN SINGLE-RUN or RUN SINGLETON cannot run asynchronously. That is, including the ASYNCHRONOUS option with SINGLE-RUN or SINGLETON is an error. However, internal procedures defined in a single-run or singleton procedure can run asynchronously. The syntax shown in the preceding example for asynchronously running GetOrders is also valid in the context of a single-run or singleton procedure.
Clearly, the handling of asynchronous requests is more complex than for synchronous requests. While this example is partly procedural, you typically handle all asynchronous requests in an event-driven context using a blocking I/O statement, such as the WAIT-FOR statement. For more information on how to manage asynchronous requests, see Managing asynchronous requests.
However, note that you cannot use compile-time (preprocessor) arguments when running a remote procedure.