# Compaq ACMS for OpenVMS
Writing Server Procedures

Previous | Contents | Index

### 2.1.6.2 Using BASIC

The examples in this section show a BASIC initialization procedure that opens an Employee file and a History file.

The procedure PERS_UPD_SERVER_INIT_PROC first includes some common definitions used by the Personnel application and the record layouts for the Employee and History files:

        %INCLUDE "pers_files:pers_common_defns"
        %INCLUDE %FROM %CDD "pers_cdd.employee_record"
        %INCLUDE %FROM %CDD "pers_cdd.history_record"

The PERS_COMMON_DEFNS.BAS file includes definitions for the channel numbers used for the Employee and History files, together with frequently used BASIC errors, error message symbols, system services, and ACMS, OpenVMS, and RMS errors:

        !+
        ! Common definitions for the PERSONNEL application.
        !-

        !+
        ! Channel numbers.
        !-
        DECLARE LONG CONSTANT emp_file = 1%
        DECLARE LONG CONSTANT hist_file = 2%

        !+
        ! Frequently used BASIC error codes.
        !-
        DECLARE LONG CONSTANT basicerr_wait_exhausted = 15%
        DECLARE LONG CONSTANT basicerr_duplicate_key = 134%
        DECLARE LONG CONSTANT basicerr_record_locked = 154%
        DECLARE LONG CONSTANT basicerr_record_not_found = 155%
        DECLARE LONG CONSTANT basicerr_deadlock = 193%

        !+
        ! Personnel application messages
        !-
        EXTERNAL LONG CONSTANT persmsg_success
        EXTERNAL LONG CONSTANT persmsg_empexists
        EXTERNAL LONG CONSTANT persmsg_empnotfound
        EXTERNAL LONG CONSTANT persmsg_emplocked
        EXTERNAL LONG CONSTANT persmsg_empchanged
        EXTERNAL LONG CONSTANT persmsg_empdeleted

        !+
        ! Frequently used system services
        !-
        EXTERNAL LONG FUNCTION SYS$GETTIM

        !+
        ! ACMS, OpenVMS system and RMS status codes
        !-
        EXTERNAL LONG CONSTANT ACMS$_TRANSTIMEDOUT
        EXTERNAL LONG CONSTANT RMS$_NRU
        EXTERNAL LONG CONSTANT RMS$_DDTM_ERR

The initialization procedure then specifies the MAP statements for the Employee and History files:

        MAP ( emp_map ) employee_record emp_rec
        MAP ( hist_map ) history_record hist_rec

Next, using an error handler, the procedure initializes the return status to success and opens the Employee and History files. If both files are opened successfully, the procedure returns the success status, indicating that the server process is now ready for use. If an error occurs, the error handler sets the return status to the RMS error status, indicating that the initialization processing failed. The EXIT HANDLER statement causes BASIC to resignal the error, which ACMS then writes to the ACMS audit trail log. Note that the built-in RMSSTATUS function can be used only after BASIC has successfully opened a file.

        WHEN ERROR IN
            pers_upd_server_init_proc = persmsg_success
            OPEN  "emp_file:employee.dat"                           &
                        FOR INPUT AS FILE # emp_file,               &
                        ORGANIZATION INDEXED FIXED,                 &
                        ALLOW MODIFY,                               &
                        ACCESS MODIFY,                              &
                        UNLOCK EXPLICIT,                            &
                        MAP emp_map,                                &
                        PRIMARY KEY emp_rec::emp_badge_number

            OPEN  "hist_file:history.dat"                           &
                        FOR INPUT AS FILE # hist_file,              &
                        ORGANIZATION INDEXED FIXED,                 &
                        ALLOW MODIFY,                               &
                        ACCESS MODIFY,                              &
                        UNLOCK EXPLICIT,                            &
                        MAP hist_map,                               &
                        PRIMARY KEY hist_rec::hist_badge_number
        USE
            pers_upd_server_init_proc = VMSSTATUS
            EXIT HANDLER
        END WHEN

The procedures that run in PERS_UPD_SERVER use explicit lock control to handle record locks to ensure the consistency of the Employee and History files. For this reason, the OPEN statement contains an UNLOCK EXPLICIT clause. Any record accessed by any procedure in the task group remains locked until it is explicitly unlocked with an UNLOCK or FREE statement.

Example 2-6 contains a complete BASIC initialization procedure.

Example 2-6 BASIC Initialization Procedure for RMS Server

        FUNCTION LONG pers_upd_server_init_proc

        %INCLUDE "pers_files:pers_common_defns"
        %INCLUDE %FROM %CDD "pers_cdd.employee_record"
        %INCLUDE %FROM %CDD "pers_cdd.history_record"

        MAP ( emp_map ) employee_record emp_rec
        MAP ( hist_map ) history_record hist_rec

        WHEN ERROR IN
            pers_upd_server_init_proc = persmsg_success
            OPEN  "emp_file:employee.dat"                           &
                        FOR INPUT AS FILE # emp_file,               &
                        ORGANIZATION INDEXED FIXED,                 &
                        ALLOW MODIFY,                               &
                        ACCESS MODIFY,                              &
                        UNLOCK EXPLICIT,                            &
                        MAP emp_map,                                &
                        PRIMARY KEY emp_rec::emp_badge_number

            OPEN  "hist_file:history.dat"                           &
                        FOR INPUT AS FILE # hist_file,              &
                        ORGANIZATION INDEXED FIXED,                 &
                        ALLOW MODIFY,                               &
                        ACCESS MODIFY,                              &
                        UNLOCK EXPLICIT,                            &
                        MAP hist_map,                               &
                        PRIMARY KEY hist_rec::hist_badge_number
        USE
            pers_upd_server_init_proc = VMSSTATUS
            EXIT HANDLER
        END WHEN

        END FUNCTION

# 2.2 Writing Termination Procedures

Termination procedures perform application-specific cleanup work for a server process. Note that Rdb, DBMS, and RMS automatically release databases and close files when a process runs down.

The use of a termination procedure for a server is optional. If you do specify a termination procedure for a server, ACMS calls the termination procedure whenever a server process runs down. The only exception is when a server process is forced to run down as the result of a task cancellation; in that case, by default, ACMS does not call the termination procedure. However, by using the ALWAYS EXECUTE TERMINATION PROCEDURE ON CANCEL clause when you define the server in the task group definition, you can force ACMS to call the termination procedure when a server is run down due to a task cancellation. If you do not specify a termination procedure, ACMS runs down the server process without performing any application-specific termination processing.

ACMS runs down server processes when an application is stopped and when more processes than the minimum defined for the server have been started and the extra processes are not needed to handle users' demands. As with initialization procedures, have termination procedures do work specific to the server process rather than task-related work. Termination procedures do the same kind of work for server processes that use Rdb and DBMS databases and RMS files.

Follow these guidelines when writing a termination procedure for files and databases:

- Have termination procedures return a status value to the server process to indicate whether the termination procedure completed successfully.
  If you do not return a status value, the termination continues, but a message that the termination routine has failed is logged in the audit trail log.
- A termination procedure cannot assign values to fields in group or user workspaces.
  Because ACMS does not pass workspaces to termination procedures, there is no way to move data to fields in workspaces.

The following sections contain examples of termination procedures. They describe how to write code for Rdb and DBMS databases, and for RMS files.

## 2.2.1 Termination Procedures for Rdb Databases Using SQL

You do not need to write a termination procedure for a server that uses an Rdb database. When a server process stops, Rdb automatically releases the database used by the process, rolling back a database transaction if one is still active.

If you decide that you need a termination procedure to perform application-specific processing when the server stops, you must be careful. If you explicitly include a FINISH statement in a server termination procedure, Rdb commits an outstanding database transaction. However, typically you do not want to commit an outstanding transaction in a termination procedure. For example, you normally want to roll back an existing database transaction if:

- An incorrectly coded step procedure does not perform all necessary updates and does not commit a database transaction.
- There is an outstanding database transaction when a task is canceled and you specify that termination procedures be called for cancels.

In general, if there is a chance that your termination procedure will be called when there is an outstanding database transaction and you are going to use the FINISH verb, include a ROLLBACK verb before the FINISH verb.

Because a transaction is not usually active when the termination procedure runs, the termination procedure should ignore any error from the ROLLBACK verb. Any error returned by the FINISH verb is used as the return status of the termination procedure.

Example 2-7 shows a sample termination procedure for a fictional database.

Example 2-7 SQL Termination Procedure

    IDENTIFICATION DIVISION.
    **************************************************************
    PROGRAM-ID. PERS-TERM-PROC.
    **************************************************************
    *         F U N C T I O N A L   D E S C R I P T I O N        *
    *                                                            *
    *   This procedure is used to close the PERSONNEL database.  *
    *                                                            *
    **************************************************************
    ENVIRONMENT DIVISION.
    CONFIGURATION SECTION.
    **************************************************************
    DATA DIVISION.
    **************************************************************

    WORKING-STORAGE SECTION.
    *
    * return status
    *
    01 RET-STAT     PIC S9(9) COMP.
    *
    * Define the SQL return status
    *
    01 SQLCODE      PIC S9(9) COMP.
    *
    EXEC SQL
    DECLARE EXTERNAL SCHEMA FILENAME personnel_database:employees
    END-EXEC.

    **************************************************************
    PROCEDURE DIVISION GIVING RET-STAT.
    **************************************************************
    MAIN SECTION.

    000-CLOSE_DB.

            SET RET-STAT TO SUCCESS.
    *
    * <<<<Insert application-specific cleanup here>>>>
    *
            EXEC SQL ROLLBACK END-EXEC.
            EXEC SQL FINISH END-EXEC.
            IF SQLCODE < ZERO
            THEN
                MOVE RDB$LU_STATUS TO RET-STAT
                CALL "SQL$SIGNAL"
        END-IF.

    100-EXIT-PROGRAM.
            EXIT PROGRAM.

For more information about SQL, refer to the SQL documentation.

## 2.2.2 Termination Procedures for Rdb Databases Using RDO

You do not need to write a termination procedure for a server that uses an Rdb database. When a server process stops, Rdb automatically releases the database used by the process, rolling back a database transaction if one is still active.

If you decide that you need a termination procedure to perform application-specific processing when the server stops, you must be careful. If you explicitly include a FINISH statement in a server termination procedure, Rdb commits an outstanding database transaction. However, typically you do not want to commit an outstanding transaction in a termination procedure. For example, you normally want to roll back an existing database transaction if:

- An incorrectly coded step procedure does not perform all necessary updates and does not commit a database transaction.
- There is an outstanding database transaction when a task is canceled and you specify that termination procedures be called for cancellations.

In general, if there is a chance that your termination procedure will be called when there is an outstanding database transaction, and you are going to use the FINISH verb, include a ROLLBACK verb before the FINISH verb.

Because a transaction is usually not active when the termination procedure runs, any error from the ROLLBACK verb is ignored. Any error returned by the FINISH verb is used as the return status of the termination procedure. For example:

        !
        ! <<<<Insert application-specific cleanup here>>>>
        !
        &RDB&   ROLLBACK
        &RDB&   ON ERROR
                    personnel_term_proc = persmsg_success
        &RDB&   END_ERROR

        &RDB&   FINISH
        &RDB&   ON ERROR
                    personnel_term_proc = RDB$LU_STATUS
        &RDB&   END_ERROR

## 2.2.3 Termination Procedures for DBMS Databases

You do not need to write a termination procedure for a server that uses a DBMS database. When a server process stops, DBMS automatically unbinds the database used by the process, rolling back a database transaction if one is still active. This section illustrates how to unbind from a DBMS database using the DBMS UNBIND embedded DML statement. Note that there is no UNBIND statement in the COBOL language.

The following example illustrates a termination procedure for a DBMS database written in BASIC:

        FUNCTION LONG pers_upd_server_term_proc

        %INCLUDE "pers_files:pers_common_defns"

        !
        ! <<<<Insert application-specific cleanup here>>>>
        !
        # INVOKE DEFAULT_SUBSCHEMA -
                 WITHIN PERS_CDD.PERSONNEL_SCHEMA -
                 FOR PERS_DB:PERSONNEL -
                 ( RECORDS )

        # ROLLBACK ( TRAP ERROR )
        # UNBIND

        pers_upd_server_term_proc = persmsg_success

        END FUNCTION

If a database transaction is active when a step procedure explicitly unbinds from a database, DBMS returns an error. Therefore, use the ROLLBACK statement to roll back an outstanding transaction. Because there is not usually a transaction active when a termination procedure is executed, ignore any error returned by the ROLLBACK statement. Finally, use the UNBIND statement to unbind from the database.

## 2.2.4 Termination Procedures for RMS Files

You do not need to write a termination procedure for a server that uses RMS files. When a server process stops, RMS automatically closes any files that the process has opened. A termination procedure for an RMS server simply closes each open file used by the server, returning a failure status if an error is detected.

### 2.2.4.1 Using COBOL

The termination procedure in Example 2-8 closes the Employee and History files. Any error is signaled and returned as the procedure's return status.

Example 2-8 COBOL Termination Procedure for RMS Files

    IDENTIFICATION DIVISION.
    PROGRAM-ID. pers_upd_server_term_proc.

    ENVIRONMENT DIVISION.

    INPUT-OUTPUT SECTION.
    FILE-CONTROL.
    SELECT  emp_file
            ORGANIZATION INDEXED
            ACCESS RANDOM
            ASSIGN TO "emp_file:employee.dat".

    SELECT  hist_file
            ORGANIZATION INDEXED
            ACCESS RANDOM
            ASSIGN TO "hist_file:history.dat".

    I-O-CONTROL.
    APPLY LOCK-HOLDING ON emp_file,
                          hist_file.

    DATA DIVISION.

    FILE SECTION.
    FD      emp_file
            EXTERNAL
            DATA RECORD IS employee_record
            RECORD KEY emp_badge_number OF employee_record.
    COPY "pers_cdd.employee_record" FROM DICTIONARY.

    FD      hist_file
            EXTERNAL
            DATA RECORD IS history_record
            RECORD KEY hist_badge_number OF history_record.
    COPY "pers_cdd.history_record" FROM DICTIONARY.

    WORKING-STORAGE SECTION.

    01  status_result               PIC S9(5) COMP.
    PROCEDURE DIVISION GIVING status_result.
    DECLARATIVES.
    employee_file SECTION.
        USE AFTER STANDARD ERROR PROCEDURE ON emp_file.
    employee_file_handler.
            CALL "LIB$SIGNAL" USING BY VALUE RMS-STS OF emp_file,
                                    BY VALUE RMS-STV OF emp_file.
            MOVE RMS-STS OF emp_file TO status_result.
    history_file SECTION.
        USE AFTER STANDARD ERROR PROCEDURE ON hist_file.
    history_file_handler.
            CALL "LIB$SIGNAL" USING BY VALUE RMS-STS OF hist_file,
                                    BY VALUE RMS-STV OF hist_file.
            MOVE RMS-STS OF hist_file TO status_result.
    END DECLARATIVES.

    MAIN SECTION.

    000-start.
        SET status_result TO SUCCESS.
    *
    * <<<<Insert application-specific cleanup here>>>>
    *
        CLOSE emp_file.
        CLOSE hist_file.

    999-end.
        EXIT PROGRAM.

### 2.2.4.2 Using BASIC

The termination procedure in Example 2-9 closes the Employee and History files. Any error is signaled and returned as the procedure's return status.

Example 2-9 BASIC Termination Procedure for RMS Files

        FUNCTION LONG pers_upd_server_term_proc

        %INCLUDE "pers_files:pers_common_defns"

        !
        ! <<<<Insert application-specific cleanup here>>>>
        !
        WHEN ERROR IN
            pers_upd_server_term_proc = persmsg_success
            CLOSE # emp_file
            CLOSE # hist_file
        USE
            pers_upd_server_term_proc = VMSSTATUS
            EXIT HANDLER
        END WHEN

        END FUNCTION

# 2.3 Server Process Rundown

One way to achieve high system performance is to avoid stopping and restarting servers. In addition to the overhead of OpenVMS process creation, starting a server also involves running the server initialization procedure that binds to databases and opens files. Perform these operations as infrequently as possible.

On the other hand, if your server is interrupted and left in an unpredictable state as a result of a task cancellation, it is best to run down the server process and start a new one.

In order to balance these two needs, ACMS allows you to control whether a server process is run down when the execution of a server procedure is interrupted due to a task cancel. ACMS provides the following three options:

- Run down on cancel only if the cancel caused the server to be interrupted
  With this option, ACMS runs down the server process only if the execution of a server procedure was interrupted due to the task cancellation. For example, if a task is retaining context in a server, but the server is not actually executing a procedure at the time of the cancel, ACMS does not run down the server.
- Always run down on cancel
  With this option, ACMS always runs down the server process if the task is canceled while it has context in the server. This option can cause unnecessary server process rundowns. For example, if a task is retaining context in a server when it is canceled, ACMS always runs down the server process, even if the task was not actually executing a server procedure at the time of the cancel. Running down a server in this situation is not necessary because a server procedure was not actually interrupted; therefore, the server is in a predictable state.
- Do not run down on cancel
  With this option, under normal conditions ACMS never runs down the server process if the task is canceled while it has context in the server. However, note that under certain conditions, such as when a server procedure generates a fatal OpenVMS exception, ACMS always runs down a server process. Use this option only when you can guarantee that all context in the server can be cleaned up. Failure to clean up all server context can result in the failure of a subsequent task that uses the server process.

In most cases, the recommended option is to run down down on cancel only if the cancel caused a server procedure to be interrupted. This option balances the need for good performance with the need to run down servers that are in an unpredictable state.

You can specify the rundown option for your servers in the following ways:

- Define the rundown option as a server subclause in your task group definition. The choices of syntax are:

  > RUNDOWN ON CANCEL IF INTERRUPTED
  > RUNDOWN ON CANCEL
  > NO RUNDOWN ON CANCEL

  If you do not specify a rundown attribute, the default is RUNDOWN ON CANCEL.
- Override the rundown attribute that you specified in your task group definition by returning a status from a cancel procedure. In two instances, ACMS overrides the option that you specify. If a fatal error is generated in procedure code, or if a channel is left open to a device when a procedure finishes, ACMS always runs down the server process. See Section 2.4 for more information on writing server cancel procedures.

Table 2-1 shows whether or not ACMS runs down a server during a cancel operation. The table assumes that a task has context in the server at the time the cancellation occurs. In cancel processing, ACMS never runs down a server if the task does not have context in any servers when the cancellation occurs.

Table 2-1 Server Rundown
Rundown Characteristic | RUNDOWN ON CANCEL | RUNDOWN ON CANCEL IF INTERRUPTED | NO RUNDOWN ON CANCEL
--- | --- | --- | ---
Server executing during cancel? | No | Yes | No | Yes | No | Yes
ACMS$RAISE_..._EXCEPTION | N/A 1 | Run down | N/A 1 | Not run down | N/A 1 | Not run down
ACMSAD$REQ_CANCEL | N/A 1 | Run down | N/A 1 | Run down | N/A 1 | Not run down
Retain context and cancel task in action step | Run down | N/A 1 | Not run down | N/A 1 | Not run down | N/A 1
Fatal error generated in procedure code | N/A 1 | Run down | N/A 1 | Run down | N/A 1 | Run down
Channel open to device error created from procedure | N/A 1 | Run down | N/A 1 | Run down | N/A 1 | Run down
All other cancels | Run down | Run down | Not run down | Run down | Not run down | Not run down

1N/A = Not applicable.

Previous | Next | Contents | Index