# Compaq ACMS for OpenVMS
Writing Applications

Previous | Contents | Index

# Chapter 4
Defining Workspaces

Chapter 2 briefly introduces the concept of ACMS workspaces and shows how to use them to pass data between parts of an application. Chapter 4 describes workspaces in more detail. Specifically, this chapter describes the three types of ACMS workspaces and explains how to use them to perform the following functions:

- Using a workspace that you define, rather than a system workspace, to do error handling in a definition
- Using data that the user types in response to the Selection: prompt on a menu as input to a task definition
- Using special kinds of workspaces to pass information needed for more than one instance of a task

Compaq ACMS for OpenVMS Writing Server Procedures explains how to write the procedures required for the task definitions explained in this chapter.

# 4.1 Understanding the Types of ACMS Workspaces

The three types of workspaces you can use in your task definitions are:

- Task
- User
- Group

Task is the default and most frequently used workspace type. Use task workspaces to store database records; to pass data between parts of your application, such as the form and exchange steps; and to perform error handling. Because ACMS retains task workspaces only for the life of a task instance, task workspaces do not use a lot of memory and CPU time. A task instance is one iteration of a task. Therefore, unless your application has certain requirements that can be met only by using user or group workspaces, use task workspaces.

A system workspace is one of three ACMS-supplied task workspaces. The ACMS$PROCESSING_STATUS workspace stores status information returned from procedures. The ACMS$SELECTION_STRING workspace stores text strings passed by the terminal user when the user selects a task from the menu. The ACMS$TASK_INFORMATION workspace stores task execution information. Because system workspaces are task workspaces, they do not use much memory and CPU time.

User workspaces let you store information used by a single user in many tasks or many task instances. User workspaces are helpful when the user needs to use the same information as a key for multiple task selections. For example, in a personnel application, a user might need to retrieve various information about an employee by choosing several tasks. Instead of requiring the user to type in the employee's number for each task, you can store the employee number in a user workspace so that it is available for all tasks that the user selects during that particular ACMS session.

You might also use user workspaces to store user-specific information that changes during the user's session. For example, you might want to keep a log of the type of work done by the user. Because ACMS retains user workspaces for the duration of the user's session, user workspaces require more memory and CPU time than task workspaces.

Group workspaces let you store information used by many users in many tasks or instances of the same task. You typically use group workspaces to store static information. For example, an accounting application might include several tasks that use the current interest rate. Instead of requiring users to repeatedly enter the interest rate, you can store it in a group workspace. Because ACMS retains group workspaces as long as the application is started, group workspaces require more memory and CPU time than task workspaces.

Table 4-1 briefly summarizes the availability and purpose of the three types of workspaces.

Table 4-1 Summary of ACMS Workspaces
Type | Available | Purpose
--- | --- | ---
Task | For duration of task instance | Passing information between: Steps in a task Exchange steps and forms Processing steps and servers Parent tasks and called tasks
User | For user's ACMS session | Storing user-specific information
Group | As long as the application is started | Storing static information required by many tasks in a group

# 4.2 Handling Errors with a Task Workspace

You want to trap errors that a task encounters and tell the user what happened whenever a task encountered them. You use workspaces to pass error-related information among the procedure, the definition, and the form in task definitions.

There are two kinds of workspaces you may want to use for error handling:

- The ACMS system workspace ACMS$PROCESSING_STATUS
- A task workspace that you define

The task definitions explained in Chapter 2 used the ACMS$PROCESSING_STATUS system workspace for error handling. When you use the ACMS$PROCESSING_STATUS workspace with the GET ERROR MESSAGE clause, ACMS:

1. Checks the status value returned by a procedure and placed, by ACMS, in the ACMS$L_STATUS field of the ACMS$PROCESSING_STATUS workspace
2. Gets a message from a file named by the MESSAGE FILE clause
3. Stores the message in the ACMS$T_STATUS_MESSAGE_LONG field of the ACMS$PROCESSING_STATUS workspace

To handle messages for a task, you may want to use a workspace that you define rather than the ACMS$PROCESSING_STATUS system workspace if:

- You want to return to the user information not related to the return status of a procedure
- You want to store literal message text instead of using message files
- You have other needs that are not completely met by the system workspace

When using workspaces that you define to handle errors, pass the workspace to the form or procedure and move values to a field in the workspace. You then test the workspace in the action part of the step to determine what to do next. This is how the tasks explained in Chapter 2 use the QUIT_KEY field of a workspace to handle actions in exchange steps. You can handle actions in processing steps in the same way.

For example, you can add a field for storing information returned by the WRITE_RESERVE_PROC procedure to the record definition for ADD_RESERVE_WKSP used in the Add Car Reservation task in Chapter 2. Example 4-1 shows the new CDD definition for ADD_RESERVE_WKSP.

Example 4-1 Record Definition for ADD_RESERVE_WKSP

    Definition of record ADD_RESERVE_WKSP
    |   Contains field          WK_CONTROL
    |   |  Datatype                 text size is 7 characters
    |   Contains field          CUST_NUMBER
    |   |  Datatype                 signed longword
    |   Contains field          CUST_NAME
    |   |  Datatype                 text size is 30 characters
    |   Contains field          CUST_STREET_ADDRESS
    |   |  Datatype                 text size is 30 characters
    |   Contains field          CUST_CITY
    |   |  Datatype                 text size is 20 characters
    |   Contains field          CUST_STATE
    |   |  Datatype                 text size is 2 characters
    |   Contains field          CUST_ZIP
    |   |  Datatype                 text size is 5 characters
    |   Contains field          CUST_PHONE
    |   |  Datatype                 text size is 10 characters
    |   Contains field          CAR_TYPE
    |   |  Datatype                 text size is 3 characters
    |   Contains field          RENTAL_DATE
    |   |  Datatype                 text size is 6 characters
    |   Contains field          RETURN_DATE
    |   |  Datatype                 text size is 6 characters

The WRITE_RESERVE_PROC procedure returns a value to WK_CONTROL, the new field you have added. Because the WK_CONTROL field is a 7-character text field, the WRITE_RESERVE_PROC procedure can return a literal string describing the success or failure of writing a new record to the Reservation file.

Now you add a workspace that you define for error handling to the USING part of the CALL clause in the processing step of the Add Car Reservation task:

    PROCESSING
      CALL WRITE_RESERVE_PROC IN RESERVE_SERVER
        USING ADD_RESERVE_WKSP;
      CONTROL FIELD WK_CONTROL
        "FAILURE"  : GOTO PREVIOUS EXCHANGE;
      END CONTROL FIELD;

The WRITE_RESERVE_PROC procedure stores a value in the WK_CONTROL field of ADD_RESERVE_WKSP, and you use the CONTROL FIELD clause to test that value. Then you can return a message to the user. There are three ways to return messages to the user:

- Return error message text from the procedure code. In this case, the procedure moves the error message text into a workspace that is passed to the form. The form displays the message text like any other output.
- Use the GET ERROR MESSAGE clause in the task definition to move a message from a message file into a workspace. You can name the message symbol used for getting the message in the GET ERROR MESSAGE clause, or it can be the return status value of the procedure, placed by ACMS in the ACMS$PROCESSING_STATUS workspace by ACMS. See the Compaq ACMS for OpenVMS ADU Reference Manual for more information on the GET ERROR MESSAGE clause.
- Include literal error message text in the form, using an IF THEN ELSE response statement to determine whether or not to display the text or what text to display.

In this case, if the WRITE_RESERVE_PROC procedure returns the string FAILURE to the WK_CONTROL field, GOTO PREVIOUS EXCHANGE returns ACMS to the previous exchange. You can use the form in the previous exchange to display a message telling the user that the new car reservation record was not added to the file. The "FAILURE" in this example is literal error message text that displays when the WRITE_RESERVE_PROC procedure returns the string FAILURE to the WK_CONTROL field.

Example 4-2 shows the complete definition for the Add Car Reservation task using a workspace that you define for error handling.

Example 4-2 Task Definition for Add Car Reservation Task

    REPLACE TASK ADD_CAR_RESERVATION_TASK
      WORKSPACES ARE ADD_RESERVE_WKSP, QUIT_CTRL_WKSP;

    BLOCK WORK WITH FORM I/O IS

     GET_RENTAL_INFORMATION:
      EXCHANGE WORK IS
       RECEIVE FORM RECORD ADD_RESERVE_FORM_REC_LIS
        RECEIVING ADD_RESERVE_WKSP, QUIT_CTRL_WKSP;
      ACTION IS
       CONTROL FIELD IS QUIT_CTRL_WKSP.QUIT_KEY
       "QUIT"  :  EXIT TASK;
       END CONTROL FIELD;

     WRITE_RESERVATION_INFORMATION:
      PROCESSING WORK IS
       CALL WRITE_RESERVE_PROC IN RESERVE_SERVER
       USING ADD_RESERVE_WKSP;
      ACTION IS
       CONTROL FIELD IS WK_CONTROL
       "FAILURE"  :  GOTO PREVIOUS EXCHANGE;
       END CONTROL FIELD;
    END BLOCK WORK;
    ACTION IS
      REPEAT STEP;
    END DEFINITION;

# 4.3 Using Data Supplied at the Menu

You can use information that the user supplies when selecting a task from a menu as input to a task.

Users often must type the number or keyword of the task they want to run when selecting a task from a menu like the one shown in Figure 4-1.

Figure 4-1 A Selection Menu

To display the review history of an employee from the menu in Figure 4-1, the user types HISTORY in response to the Selection: prompt.

When a user selects a task from a menu, the first step of the task is often an exchange step asking the user to supply a key, usually something like an employee number or department number. Instead of asking the user for the key after making the selection, however, you can let the user supply that information when making a selection. ACMS stores this selection string in the system workspace, ACMS$SELECTION_STRING.

Example 4-3 shows the record definition of the ACMS$SELECTION_STRING workspace.

Example 4-3 Definition for the ACMS$SELECTION_STRING Workspace

    DEFINE RECORD DISK1:[CDDPLUS]ACMS$DIR.ACMS$WORKSPACES.ACMS$SELECTION_STRING.
      ACMS$SELECTION_STRING STRUCTURE.
        ACMS$T_SELECTION_STRING   DATATYPE TEXT 255.
      END STRUCTURE.
    END RECORD.

Like the ACMS$PROCESSING_STATUS system workspace, the ACMS$SELECTION_STRING workspace is automatically available to all tasks. In the ACMS$SELECTION_STRING workspace, ACMS stores any text containing up to 255 characters that the user types after typing the number or keyword of a task.

If you use the ACMS$SELECTION_STRING workspace to let the user type in a record key, the first exchange step of the task can check for that key in the ACMS$SELECTION_STRING workspace. The exchange step displays a DECforms panel asking for the key only if the workspace field is empty. Any validation work that the form normally performs is bypassed. If the field contains a key, ACMS does not do any work in the first exchange step of the task and goes directly to the second step.

Include the ACMS$SELECTION_STRING workspace in the first exchange step of the Display Basic task so that the user can type the number of the employee when selecting the task from a menu.

    VALIDATE_EMPLOYEE:
      EXCHANGE
        CONTROL FIELD ACMS$T_SELECTION_STRING
          ""         :  RECEIVE FORM RECORD DISPLAY_NUMBER_FORM_REC
                         RECEIVING ACMS$SELECTION_STRING;
          NOMATCH    : NO EXCHANGE;
        END CONTROL FIELD;

You can also use the CONTROL FIELD clause to take conditional actions based on the contents of a workspace field. ACMS checks the field ACMS$T_SELECTION_STRING field in the VALIDATE_EMPLOYEE step. If the field is empty, the user did not supply an employee number when selecting the Display Basic task, and DISPLAY_BASIC_FORM displays a panel asking for the employee number. The form stores this number in the ACMS$SELECTION_STRING workspace.

At the end of the previously shown exchange step, the ACMS$SELECTION_STRING workspace always contains an employee number, typed in either as a selection string or in response to the initial panel. Because the processing step in the Display Basic task needs the employee number to know what record to get, you must pass the contents of the selection string workspace to the procedure in the processing step.

    PROCESSING
      CALL DISPLAY_BASIC_GET
        USING ACMS$SELECTION_STRING, HIST_RECORD, PERS_RECORD;
      CONTROL FIELD ACMS$T_STATUS_TYPE
        "G"        : GOTO NEXT STEP;
        "B"        : GET ERROR MESSAGE;
                     GOTO PREVIOUS EXCHANGE;
      END CONTROL FIELD;

The DISPLAY_BASIC_GET procedure uses the employee number stored in the ACMS$SELECTION_STRING workspace to read basic employee information from both the History and Personnel files. You name the ACMS$SELECTION_STRING workspace in the USING part of the CALL clause, if the processing step uses information stored in the workspace.

The error handling in this processing step is the same as that in other tasks explained in this manual. If the DISPLAY_BASIC_GET procedure returns a recoverable error, ACMS:

1. Stores the value B in the ACMS$T_STATUS_TYPE field of the ACMS$PROCESSING_STATUS workspace
2. Repeats the first exchange, where the form uses the contents of the ACMS$T_STATUS_TYPE field to display an error message to the user, asking for another employee number

The DISPLAY_BASIC_GET procedure has to test and clear the ACMS$T_SELECTION_STRING workspace before using the form. If the workspace had contained a value when tested in the first exchange step, this would have put the task into an infinite loop.

Example 4-4 shows the complete definition for the Display Basic task.

Example 4-4 Definition for Display Basic Task

    REPLACE TASK DISPLAY_BASIC
      USE WORKSPACES ADMIN_WORKSPACE, HIST_RECORD, PERS_RECORD,
                     QUIT_CTRL_WKSP;
      DEFAULT SERVER IS ADMINISTRATION_SERVER;
      DEFAULT FORM IS DISPLAY_BASIC_FORM
      BLOCK
        WORK WITH NO SERVER CONTEXT FORM I/O
        VALIDATE_EMPLOYEE:
          EXCHANGE
            CONTROL FIELD ACMS$T_SELECTION_STRING
              ""         : RECEIVE FORM RECORD DISPLAY_NUMBER_FORM_REC
                             RECEIVING ACMS$SELECTION_STRING;
              NOMATCH    : NO EXCHANGE;
            END CONTROL FIELD;
          PROCESSING
            CALL DISPLAY_BASIC_GET
              USING ACMS$SELECTION_STRING, HIST_RECORD, PERS_RECORD;
            CONTROL FIELD ACMS$T_STATUS_TYPE
              "G"        : GOTO NEXT STEP;
              "B"        : GET ERROR MESSAGE;
                           GOTO PREVIOUS EXCHANGE;
            END CONTROL FIELD;
        DISPLAY_BASIC_DATA:
          EXCHANGE
            TRANSCEIVE FORM RECORD DISPLAY_BASIC_FORM_REC,
                                   QUIT_CTRL_FORM_REC
              SENDING ADMIN_WORKSPACE
              RECEIVING QUIT_CTRL_WKSP;
            CONTROL FIELD IS QUIT_CTRL_WKSP.QUIT_KEY
              "QUIT"     : EXIT TASK;
            END CONTROL FIELD;
        END BLOCK WORK;
        ACTION
         REPEAT STEP;
     END DEFINITION;

When using the ACMS$SELECTION_STRING workspace to let the user supply a record key when selecting a task, you:

1. Name the ACMS$SELECTION_STRING workspace in the form definitions and in the steps using that workspace
2. Use the CONTROL FIELD clause in the first exchange step to do conditional work based on the value in the ACMS$T_SELECTION_STRING field
3. Do not name the ACMS$SELECTION_STRING workspace or any other ACMS system workspace in the WORKSPACES or USE WORKSPACES clause of task or task group definitions

When you use the contents of the ACMS$SELECTION_STRING workspace as input to a task, validation of that information, normally handled by the form, must be handled in the procedure code of the task.

# 4.4 Using Group Workspaces

Often there is some information that many tasks in a task group use. For example, in an accounting application there may be several tasks in a task group that always require the current interest rate. You can store this shared information in a group workspace.

Each task instance using a group workspace gets its own copy of the workspace. ACMS discards the copy of the workspace belonging to each task instance, but unless the workspace is being updated, ACMS keeps the original contents of a group workspace when a task instance is finished, as it does with the contents of a task workspace. This procedure allows many tasks or different instances of the same task to use the contents of a group workspace many times.

A Labor Reporting task shows one example of how to use a group workspace. This task lets the user type information about the work that an employee did on different projects. Part of the information used by the task each time it runs is the week-ending date. Rather than require the user to supply the date whenever typing information for an employee, you can store that date in a group workspace. The task can then access that workspace each time it runs.

Example 4-5 shows the record definition for LABOR_GROUP_WORKSPACE.

Example 4-5 Record Definition for GROUP_WORKSPACE

    DEFINE RECORD
        DISK1:[CDDPLUS]ACMS$DIR.ACMS$EXAMPLES_RMS.LABOR_GROUP_WORKSPACE.
      LABOR_GROUP_WORKSPACE STRUCTURE.
        WK_ENDING_DATE    DATATYPE TEXT 6.
                          INITIAL VALUE "".
      END LABOR_GROUP_WORKSPACE STRUCTURE.
    END LABOR_GROUP_WORKSPACE.

Because the task is going to take a value from the workspace, you need a way to put the value into the workspace. There are several ways to initialize the contents of a group workspace:

- Run a task to load a value into the workspace when the application using the workspace is started.
- Check the contents of the workspace in the initial step of each task using the workspace. If the workspace is empty, either run a procedure or call a form in that step to initialize the workspace. However, when many tasks use the same group workspace, those tasks are not likely to have update access to that workspace.
- Check the contents of the workspace in the initial step of each task using that workspace. If the workspace is empty, use the MOVE clause in the action part of the step. You may use MOVE to initialize signed longword fields and text fields. If you want to initialize other data types, use one of the other methods discussed here.
- Check the contents of the workspace in the initial step of each task using the workspace. If the workspace is empty, invoke another task that initializes the workspace.

The Labor Reporting task uses the last of these methods to set the initial contents of the workspace. The first step of the task uses a CONTROL FIELD clause to test the contents of the workspace and, if the workspace does not contain the week ending date, runs another task, Get Initial Value, that initializes the workspace. You can run another task without returning the user to a selection menu by using the task-call-task feature, described in detail in Chapter 5.

The Get Initial Value task consists of a single processing step that runs a procedure to initialize the group workspace.

The Labor Reporting task is the only task using the workspace named LABOR_GROUP_WORKSPACE, so you can declare this group workspace at either the group or task level. However, by declaring workspaces at the group level, you ensure that the same workspace name is used consistently by all tasks. You use the USE WORKSPACES clause in the task definition to specify that workspaces to be used in a task are declared at the group level:

    USE WORKSPACES LABOR_GROUP_WORKSPACE, LABOR_RECORD, LABOR_WORKSPACE;

Here is the first step of the Labor Reporting task:

    PROCESSING
      NO PROCESSING;
      CONTROL FIELD LABOR_GROUP_WORKSPACE.WK_ENDING_DATE
        ""         : GOTO TASK GET_INITIAL_VALUE_TASK;
        NOMATCH    : GOTO NEXT STEP;
      END CONTROL FIELD;

The WK_ENDING_DATE field must be defined to have an initial value of " ". If you do not define an initial value for the field, ACMS initializes it with zeros. If the CONTROL FIELD clause tests the field for " " and finds zeros, it always takes the action associated with the NOMATCH keyword, because the field is not empty.

If the WK_ENDING_DATE field is empty, ACMS runs INITIAL_VALUE_TASK. Example 4-6 shows the definition for that task.

Example 4-6 Complete Definition for the Get Initial Value Task

    REPLACE TASK GET_INITIAL_VALUE_TASK
      USE WORKSPACE LABOR_GROUP_WORKSPACE WITH UPDATE LOCK;
          PROCESSING
            CALL LABOR_GET_INITIAL_VALUE IN WORKSPACE_SERVER
              USING LABOR_GROUP_WORKSPACE;
            CONTROL FIELD ACMS$T_STATUS_TYPE
              "G"        : GOTO TASK LABOR_DATA_ENTRY_TASK;
              "B"        : CANCEL TASK;
            END CONTROL FIELD;
    END DEFINITION;

The LABOR_GET_INITIAL_VALUE procedure gets the week-ending date from a file and stores it in the WK_ENDING_DATE field of the workspace LABOR_GROUP_WORKSPACE. When the week-ending date needs to be changed, the contents of the file must be changed, and the application or applications using that workspace must be stopped and restarted.

If the LABOR_GET_INITIAL_VALUE procedure is unsuccessful, ACMS cancels the task, returning the user to the menu. If the procedure is successful, ACMS processes the rest of the Labor Reporting task. When the initial processing step of that task runs, the LABOR_GROUP_WORKSPACE contains the week-ending date. The next step in the task can:

- Display a panel by using the contents of the workspace
- Do processing by using the contents of the workspace

In the Labor Reporting task, once the first processing step has checked the contents of the group workspace, an exchange step uses a form to display an initial panel. Here is the first exchange step of the Labor Reporting task:

    EXCHANGE
      TRANSCEIVE FORM RECORD LABOR_GROUP_FORM_REC, EMPLOYEE_FORM_REC_LIS
       SENDING LABOR_GROUP_WKSP
       RECEIVING EMPLOYEE_RECORD_WKSP, QUIT_CTRL_WKSP;
    ACTION IS
      CONTROL FIELD IS QUIT_CTRL_WKSP.QUIT_KEY
      "QUIT"  :  EXIT TASK;
      END CONTROL FIELD;

When this step is complete, the task runs like any other update task. The user supplies a record key to a form in this exchange step. A procedure in a processing step uses that key to read information from a file or database. A form in an exchange step displays that information for the user. The user can then type additional information. In the Labor Reporting task, the user types project information for an employee.

Example 4-7 shows the complete definition for the Labor Reporting task.

Example 4-7 Definition for Labor Reporting Task

    REPLACE TASK LABOR_DATA_ENTRY_TASK
      USE WORKSPACES
        EMPLOYEE_RECORD_WKSP, LABOR_GROUP_WKSP, LABOR_RECORD_WKSP,
        QUIT_CTRL_WKSP;
      DEFAULT SERVER IS LABOR_SERVER;
      DEFAULT FORM IS LABOR_FORM
      BLOCK
        WORK WITH FORM I/O
          PROCESSING
            NO PROCESSING;
             CONTROL FIELD LABOR_GROUP_WKSP.WK_ENDING_DATE
              ""         : GOTO TASK GET_INITIAL_VALUE_TASK;
              NOMATCH    : GOTO NEXT STEP;
            END CONTROL FIELD;

          EXCHANGE
            TRANSCEIVE FORM RECORD LABOR_GROUP_FORM_REC,
                                   EMPLOYEE_FORM_REC_LIS
             SENDING LABOR_GROUP_WKSP
             RECEIVING EMPLOYEE_RECORD_WKSP, QUIT_CTRL_WKSP;
          ACTION IS
            CONTROL FIELD IS QUIT_CTRL_WKSP.QUIT_KEY
            "QUIT"  :  EXIT TASK;
            END CONTROL FIELD;

         PROCESSING
            CALL LABOR_EMPLOYEE_GET USING EMPLOYEE_RECORD_WKSP;
            CONTROL FIELD ACMS$T_STATUS_TYPE
              "B"        : GET ERROR MESSAGE;
                           GOTO PREVIOUS EXCHANGE;
              "G"        : GOTO NEXT STEP;
            END CONTROL FIELD;
        GET_PROJECT_DATA:
          EXCHANGE
            TRANSCEIVE FORM RECORD LABOR_FORM_REC, LABOR_FORM_REC_LIS
              SENDING LABOR_RECORD_WKSP
              RECEIVING LABOR_RECORD_WKSP, QUIT_CTRL_WKSP;
          ACTION IS
            CONTROL FIELD IS QUIT_CTRL_WKSP.QUIT_KEY
            "QUIT"  :  EXIT TASK;
            END CONTROL FIELD;
          PROCESSING
            CALL LABOR_PROJECT_PUT
              USING LABOR_GROUP_WORKSPACE, LABOR_RECORD_WKSP;
            CONTROL FIELD ACMS$T_STATUS_TYPE
              "B"        : GET ERROR MESSAGE;
                           GOTO PREVIOUS EXCHANGE;
            END CONTROL FIELD;

        END BLOCK WORK;
        ACTION
          REPEAT STEP;
    END DEFINITION;

Previous | Next | Contents | Index