Release 10.1A: OpenEdge Application Server:
Developing AppServer Applications

> ## Running remote procedures

> A remote procedure, whether it runs persistently or non-persistently, is scoped only to the AppServer session in which it runs. The order of execution for a remote procedure, relative to the client, depends on whether you execute it synchronously or asynchronously.

> ##### Synchronous remote procedures

> If you run a remote procedure synchronously, the client application blocks until the remote procedure request completes and returns to the client. Execution then resumes in the client after the `RUN` statement that invoked the remote procedure.

> ##### Asynchronous remote procedures

> If you run a remote procedure asynchronously, the client application continues execution immediately after the `RUN` statement that invokes the remote procedure completes. The remote procedure executes on the AppServer whenever the AppServer is available to execute it. You can then access the results of the remote request in an event procedure that executes in response to a `PROCEDURE-COMPLETE` event. The client handles the event in the context of a `PROCESS` `EVENTS` or other blocking I/O statement, similar to a user-interface event, and executes the event procedure like a user-interface trigger.

> ##### RUN statement options for remote procedures

> You can use various options on the `RUN` statement to execute remote procedures:

> - Run a synchronous external procedure using the `ON` [`SERVER`] `handle-variable` [`TRANSACTION` `DISTINCT`] 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` `SET` `handle-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 hOrder
>       ON 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 the "Deleting remote persistent procedures" section). 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 persistent procedure handles, see the "Understanding proxy persistent procedure handles" section.

> - Run a synchronous internal procedure or user-defined function using the `IN` `handle-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 persistent procedure handle, `hProc`.

> 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 persistent procedure handle.

> - Pass `INPUT`, `OUTPUT`, and `INPUT-OUTPUT` variable or `TEMP-TABLE` parameters, but not `BUFFER` parameters.

> - Run a procedure (internal or external) asynchronously using the `ASYNCHRONOUS` option, as shown:

>     DEFINE VARIABLE hAppSrv AS HANDLE NO-UNDO.
>     DEFINE VARIABLE hProc AS HANDLE NO-UNDO.
>     DEFINE VARIABLE hAsync AS HANDLE NO-UNDO.
>     DEFINE VARIABLE hAsync2 AS HANDLE NO-UNDO.
>     DEFINE TEMP-TABLE order-table ...

>     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 order-table).
>       ELSE /* Order Module Not Ready! */
>            /* Work on other stuff while waiting. */
>                        .
>                        .
>                        .
>     END.

>     WAIT-FOR CLOSE OF THIS-PROCEDURE.

>     PROCEDURE GetOrderRecords:
>       DEFINE INPUT PARAMETER TABLE FOR order-table.
>       DEFINE VARIABLE lSee AS LOGICAL INIT FALSE 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 order-table. */
>       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.

> 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 the "Managing asynchronous requests" section.

> However, note that you cannot use compile-time (preprocessor) arguments when running a remote procedure.

> ##### Remote procedure behavior

> In addition to the behavior specified by the `RUN` statement options, a remote procedure:

> - Executes in the AppServer session as if it was the first procedure executed in the session (`top level`), using the normal Progress 4GL rules for starting and terminating transactions. For more information about the normal Progress 4GL rules for procedure execution, see OpenEdge Development: Progress 4GL Handbook .

> Note: Remote procedures can also participate in automatic transactions, which are unique to AppServer sessions. For information, see the sections on transaction management in Chapter 2, " Programming the AppServer."

> - Can create other objects in the AppServer session, such as other persistent procedures. These objects are only available locally within the AppServer agent in which they are created. They cannot be directly shared between a Progress 4GL client application and the AppServer session.

Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095