Release 10.1A: OpenEdge Application Server:
Developing AppServer Applications

> ## Examples

> The following examples show simple implementations of various client and remote procedures. The first two examples (Figure 3–1 and Figure 3–2) compare two similar implementations, one synchronous and the other asynchronous. These are non-executing examples, shortened for illustration. The third example (`client.p` and `server.p`) shows a fully executing, asynchronous implementation of client and remote procedures.

> ##### Synchronous request execution model

> Figure 3–1 shows a remote procedure executed synchronously. The application sets a simple status flag (Done) to indicate that the remote request has completed.

> As an event-driven GUI application, the client first executes a `WAIT-FOR` statement after having set up code for handing events (1). When the client executes `sync.p` as a synchronous remote procedure (2), the client blocks while the AppServer completes the request (3) and only continues execution after the remote procedure completes (4). Results from a synchronous remote procedure call are returned to the client immediately after the point of invocation (4), exactly as they are returned from a local procedure call.

> Note that in this example all of the remote processing and handling of the result occurs in the trigger for `bStatus`. While straight forward to implement, the client can block for an indeterminate period of time at the synchronous `RUN` statement waiting for `sync.p` to complete, depending on the application. Asynchronous requests allow a client to avoid this bottleneck.

> Figure 3–1: Synchronous requests

> ##### Asynchronous request execution model

> Figure 3–2 shows a remote procedure executed asynchronously. As in Figure 3–1, the client is an event-driven GUI and the application sets a simple status flag (Done) to indicate that the remote request has completed.

> In this implementation, the client first executes `async.p` as an asynchronous remote procedure (1, specified by the `ASYNCHRONOUS` keyword on the `RUN` statement). The client immediately continues execution, until it reaches the `WAIT-FOR` statement to get events (2) at the same time that the AppServer executes the remote request. Thus, at this point (1 and 2), the client and remote request are running in parallel. The client can continue to execute, calling additional asynchronous remote procedures on the same or different AppServer connections.

> As each asynchronous request completes (like `async.p` at 3), the client handles the results in a specified event procedure (4). This event procedure, specified in the asynchronous `RUN` statement, is essentially a “trigger” that executes on the client in response to a `PROCEDURE-COMPLETE` event posted to the client when the associated asynchronous request completes.

> As with user-interface events, the client can handle `PROCEDURE-COMPLETE` events in the context of a blocking I/O statement, such as the `WAIT-FOR` statement (4), or by executing the `PROCESS` `EVENTS` statement. OpenEdge maps the `PROCEDURE-COMPLETE` event for each asynchronous request to the appropriate event procedure using a unique asynchronous request handle that is generated for each request (not shown). This handle provides the mechanism that you can use to monitor the status of each request.

> Note that the example in Figure 3–2 provides the same functionality as Figure 3–1. In fact, `sync.p` and `async.p` are identical, except for their names, which are changed for readability. The AppServer has no idea whether its procedures are being called synchronously or asynchronously. The type of request is entirely a function of the client and its implementation.

> The main difference, is that the `bStatus` trigger in Figure 3–2 tests the status of the remote request and performs actions based on whether the request finished, rather than invoking the request itself, as in Figure 3–1. Thus, the synchronous bottleneck is removed. In all such cases, the synchronous bottleneck is avoided by handling the results asynchronously.

> Figure 3–2: Asynchronous requests

> ##### A complete asynchronous request example

> In the following example, the Progress 4GL client procedure, `client.p`, calls `server.p` (following) asynchronously on an AppServer. The `server.p` procedure returns the customer number corresponding to a customer name provided by `client.p`.

> The `client.p` procedure blocks and waits for the `PROCEDURE-COMPLETE` event that triggers execution of the `GetCustNum` event procedure. The `GetCustNum` procedure displays the result and deletes the corresponding asynchronous request handle (referenced by the `SELF` system handle).

> In a more typical example, additional event handlers (triggers) would be active for user-interface events, allowing the user of `client.p` to perform other tasks with the procedure while completion of the request is pending. In this case, the client application blocks immediately after submitting the request to wait for the result and terminate on the `PROCEDURE-COMPLETE` event.

> client.p
>     DEFINE VARIABLE async-request AS HANDLE.
>     DEFINE VARIABLE s-hdl AS HANDLE.
>     DEFINE VARIABLE customer-name as CHARACTER INITIAL"WILNER".
>     DEFINE VARIABLE ret AS LOGICAL.

>     CREATE SERVER s-hdl.

>     ret = s-hdl:CONNECT("-S 5162 -H zeus -AppService Inventory").

>     RUN server.p ON s-hdl
>        ASYNCHRONOUS SET async-request
>        EVENT-PROCEDURE "GetCustNum"
>           (INPUT customer-name, OUTPUT customer-number AS INTEGER).

>     WAIT-FOR PROCEDURE-COMPLETE OF async-request.

>     PROCEDURE GetCustNum:
>        DEFINE INPUT PARAMETER customer-number AS INTEGER.

>        DISPLAY customer-number.
>        DELETE OBJECT SELF.

>     END.

> server.p
>     DEFINE INPUT PARAMETER customer-name AS CHARACTER.
>     DEFINE OUTPUT PARAMETER customer-number AS INTEGER.

>     FIND customer WHERE name = customer-name.
>     customer-number = cust-num.

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