Compaq ACMS for OpenVMS
Systems Interface Programming


Previous Contents Index

2.2.4 MACRO

The MACRO library SYS$LIBRARY:ACMSMAC.MLB is supplied for MACRO programmers. See the MACRO documentation for information on using macro libraries. SYS$LIBRARY:ACMSMAC.MLB contains the following interface definition macros for agent programs:

Use these macros to include the appropriate definitions for the services used. These macros take one parameter, either <=> or <==>. The parameter determines whether the constant definitions are made locally or globally.

Each of these macros:

2.2.5 Pascal

The source file SYS$LIBRARY:ACMSPAS.PAS is supplied for Pascal programmers. The system manager must process this file into a PASCAL ENVIRONMENT file. See the Pascal documentation for information on creating and using environment definitions.

The source file:

Note

If you use the nonpositional syntax form of parameter association, you must use PROCEDURE_ rather than PROCEDURE as the formal parameter name for ACMS$GET_PROCEDURE_INFO, because PROCEDURE is a reserved word in Pascal. See VAX Pascal Reference Manual for more information.

2.2.6 PL/I

The text library SYS$LIBRARY:ACMSPLI.TLB is supplied for PL/I programmers. See PL/I documentation for information on using text library files in programs.

This text library provides the following modules for agent programs:

These modules:

2.2.7 Other Languages

Programmers in other languages must define:


Chapter 3
Agent Programs that Coordinate Distributed Transactions

A distributed transaction is the grouping of operations on multiple recoverable resources (such as files and databases) into a single recovery unit or logical database transaction. Distributed transactions can include more than one type of resource manager and have the properties of atomicity, isolation, and durability.

An agent program, which can run either under the control of ACMS or external to ACMS, can call tasks that are executed as part of a distributed transaction. For example, an agent program can access a database locally and then call an ACMS task in an application on a remote node. The task can call a step procedure that accesses a second database locally on the remote node. You can coordinate both database accesses as part of the same distributed transaction. See Section 3.3.

An agent program uses a set of OpenVMS system services to start and end distributed transactions. Table 3-1 contains these services.

Table 3-1 System Services Used in Distributed Transactions
System Service Use to
$START_TRANS Start transaction
$START_TRANSW Start transaction and wait
$END_TRANS End transaction
$END_TRANSW End transaction and wait
$ABORT_TRANS Roll back transaction
$ABORT_TRANSW Roll back transaction and wait

Note

The optional TRANSW (wait) system services complete synchronously; that is, they return to the caller after the request has actually completed.

For more information on DECdtm services, refer to documentation for OpenVMS Version 5.4 or higher.

For a task to execute as part of a distributed transaction started in an agent program, the agent program and the application containing the task must conform to a number of composability rules. Lists of these rules follow.

3.1 Starting a Distributed Transaction

When an agent program calls a task, it is important for the agent program to control the task's participation in an active distributed transaction. To do this, the agent program must use the TID argument in the ACMS$START_CALL and ACMS$CALL services in either one of two ways:

The following services accept a TID:

ACMS$CALL
ACMS$CALL_A
ACMS$START_CALL
ACMS$START_CALL_A

The calling sequences for these services are listed in Chapter 5.

When an agent program passes a TID to one of these services, ACMS attempts to pass the TID on to the task. If an agent program tries to pass a TID to a task that cannot join a distributed transaction, either the ACMS$WAIT_FOR_CALL_END or the ACMS$CALL service returns ACMS$_TASKNOTCOMP, indicating that the task is not composable.

If ACMS attempts to pass a TID to a task in an application that does not follow composability rules, one of the following errors is returned:

Example 3-1 illustrates the logic of an agent program that processes records from a data file and calls tasks as a single distributed transaction.

Example 3-1 A Specialized User-Written Agent

 
status = $OPEN                       ! Open a data file 
status = $CONNECT                    ! Connect a record stream 
status = $OPEN                       ! Open an error file 
status = $CONNECT                    ! Connect a record stream 
UNTIL <no more records to process> 
    BEGIN 
    trx_aborted = FALSE              ! Assume everything will work 
    status = $START_TRANSW           ! Start a transaction 
    status = $GET                    ! Read a record from the file 
    status = $ACMS$CALL              ! Call a task 
    IF .status                       ! If task completed OK, 
    THEN                             ! then... 
        BEGIN               
        status = $DELETE             !   Delete the record 
        status = $END_TRANSW         !   and end the transaction 
        IF NOT .status               !   If transaction rolled back, 
        THEN                         !   then... 
            txn_aborted = TRUE       !     Set the flag so we know 
        END 
    IF NOT .status                   ! If something went wrong (task 
    THEN                             ! failed or txn rolled back, 
        BEGIN                        ! then... 
        IF NOT .txn_aborted          !   If txn hasn't rolled back, 
        THEN                         !   then... 
            $ABORT_TRANS             !     Roll back transaction 
        $START_TRANSW                !   Start a new transaction 
        $GET                         !   Reread record from file 
        $PUT                         !   Store record in error file 
        $DELETE                      !   Delete rec. from data file 
        $END_TRANSW                  !   And end the transaction 
        END  
$DISCONNECT                          ! Disconnect both 
$DISCONNECT                          ! record streams 
$CLOSE                               ! Close both the data 
$CLOSE                               ! and error files 

3.2 Rolling Back a Distributed Transaction

An agent program can use the $ABORT_TRANS service to roll back a transaction and, therefore, cancel the associated task. The task is cancelled with the OpenVMS status DDTM$_ABORTED.

If you require a more meaningful reason for the cancellation, you can use the ACMS$CANCEL_CALL service to cancel the task. If the task is still active, the EXC cancels the task. However, the message sent from the agent program to cancel a task may not reach EXC before the task completes and EXC returns the status to the agent. In this case, the agent program should check the task completion status to determine whether or not to roll back the transaction. The following example illustrates this condition:


 
   status = ACMS$START_CALL 
   < additional processing > 
   IF .error_condition_detected 
   THEN 
       cancel_status = ACMS$CANCEL_CALL 
   task_status = ACMS$WAIT_FOR_CALL_END 
   IF .error_condition_detected OR NOT .task_status 
   THEN 
       IF .task_status 
       THEN 
           cancel_status = $ABORT_TRANS 
   < task termination processing > 

An agent program that starts a distributed transaction can roll back the transaction by a call to $ABORT_TRANS at any of the following times:

3.3 Accessing Remote Data

Compaq ACMS for OpenVMS Concepts and Design Guidelines discusses ways in which you can access remote data in a distributed transaction. Two methods for an ACMS application on one node (A) to access data on another node (B) are the following:

Briefly stated, the advantages of the first method over the second are that it minimizes the following:

When you use the first method, ACMS remote access, a step procedure on Node A acts as an agent program by using the SI system service call ACMS$CALL to invoke a task in an application on Node B. ACMS$CALL passes the TID to the application on Node B and forces the called task on Node B to join the distributed transaction that starts in the calling task on Node A. The called task on Node B executes and performs database access by calling appropriate processing step procedures locally.

Figure 3-1 illustrates the use of a step procedure as an agent program to call a task in an application on a remote node.

Figure 3-1 Using a Step Procedure as an Agent Program


Example 3-2 contains the task definition that calls the procedure BANKING_SAMPLE_TXN. The processing step in the task definition starts a distributed transaction.

Example 3-2 Task Definition that Calls a Procedure Used as an Agent

REPLACE TASK BANKING_SAMPLE_TSK /DIAGNOSTIC 
DEFAULT FORM IS BANKING_SAMPLE_FORM; 
WORKSPACE IS BANKING_SAMPLE_WORKSPACE WITH TYPE TASK; 
     BLOCK WORK WITH FORM I/O 
 
        INPUT_REQUEST: 
         EXCHANGE 
   TRANSCEIVE RECORD BANKING_SAMPLE_REC, BANKING_SAMPLE_REC 
     IN BANKING_SAMPLE_FORM 
          SENDING BANKING_SAMPLE_WORKSPACE 
          RECEIVING BANKING_SAMPLE_WORKSPACE; 
 
        BANKING_SAMPLE_PROCESSING: 
 
        PROCESSING WITH DISTRIBUTED TRANSACTION 
          CALL BANKING_SAMPLE_TXN IN BANKING_SAMPLE_SERVER 
          USING BANKING_SAMPLE_WORKSPACE; 
 
!+ 
!  Check the return status in the field ACMS$L_STATUS. 
!  This field contains 1 is success, -913 if deadlock. 
!  If deadlock, try again.  Otherwise, cancel task. 
!- 
 
 
        ACTION IS 
            SELECT FIRST TRUE OF 
            (ACMS$L_STATUS = 1):    COMMIT TRANSACTION; 
            (ACMS$L_STATUS = -913): ROLLBACK TRANSACTION; 
                                    GOTO STEP BANKING_SAMPLE_PROCESSING; 
            NOMATCH:                ROLLBACK TRANSACTION; 
                                    CANCEL TASK; 
            END SELECT; 
 
 
        EXCEPTION ACTION IS 
            CANCEL TASK; 
 
 
 
        OUTPUT_REQUEST: 
 
         EXCHANGE 
          TRANSCEIVE RECORD BANKING_SAMPLE_REC, BANKING_SAMPLE_REC 
            IN BANKING_SAMPLE_FORM 
          SENDING BANKING_SAMPLE_WORKSPACE 
          RECEIVING BANKING_SAMPLE_WORKSPACE; 
 
 
 
!+ 
!  Examine the control field. If the request returns Y, 
!  then leave the task; else, repeat the task. 
!- 
 
 
        ACTION IS 
            IF (BANKING_SAMPLE_WORKSPACE.WORKSPACE_EXIT_SWITCH = "Y") THEN 
                EXIT TASK; 
            ELSE 
                GOTO STEP BANKING_SAMPLE_PROCESSING; 
            END IF; 
 
 
    END BLOCK WORK; 
 
END DEFINITION; 

The agent program BANKING_SAMPLE_TXN, shown in Example 3-4, performs local updates of branch and teller data. It then invokes a remote task to update an account on a remote node. The remote task, BANKING_SAMPLE_ACTUPD_TSK, joins in the distributed transaction by declaring WITH DISTRIBUTED TRANSACTION on a block step in the task definition. The remote task calls a procedure on the remote node, BANKING_SAMPLE_ACTUPD_TXN, to update the database locally on the remote node.

Example 3-3 contains the task definition in the application on the remote node.

Example 3-3 Task Definition that Calls a Procedure to Update Remote Data

REPLACE TASK BANKING_SAMPLE_ACTUPD_TSK /DIAGNOSTIC 
WORKSPACE IS BANKING_SAMPLE_WORKSPACE WITH TYPE TASK; 
TASK ARGUMENT IS BANKING_SAMPLE_WORKSPACE WITH ACCESS MODIFY; 
    BLOCK WORK WITH DISTRIBUTED TRANSACTION NO I/O 
 
 
        BANKING_SAMPLE_PROCESSING: 
 
        PROCESSING 
            CALL BANKING_SAMPLE_ACTUPD_TXN IN BANKING_SAMPLE_ACTUPD_SERVER 
            USING BANKING_SAMPLE_WORKSPACE; 
 
    END BLOCK WORK; 
 
 
    ACTION IS 
        IF (ACMS$L_STATUS <> 1) THEN 
            CANCEL TASK RETURNING ACMS$L_STATUS; 
        END IF; 
 
END DEFINITION; 

3.4 Step Procedure in C that Acts as an Agent Program

Following is the flow of events in the agent program shown in Example 3-4. The numbers coincide with those in the sample program.

  1. Declare tid_structure used to retrieve TID from ACMS.
  2. Set up SQL context structure for local updates.
  3. Set up interface to SI services.
  4. Pull in workspace definitions from CDD.
  5. Retrieve TID from ACMS.
  6. Call initialization routine in preparation for call to remote task.
  7. Call remote task passing submitter ID, task information, workspace, and TID.
  8. Check return status of call to remote task; log error message if failure.
  9. Check for SQL errors.

    Note

    Steps 10 through 13 are actually substeps within step 6.
  10. Specify task name and application name; note that the application is running on a remote node.
  11. Sign in to ACMS.
  12. Prepare and get remote procedure information.
  13. Pass workspace from agent program to remote task.

Example 3-4 Step Procedure in C that Acts as an Agent Program

 
/*************************************************************************/ 
/*                                                                       */ 
/*                        Sample Banking Application                     */ 
/*                     Branch/Teller Update Transaction                  */ 
/*                     --------------------------------                  */ 
/*                                                                       */ 
/*  This is the server procedure called by the BANKING_SAMPLE_TSK task   */ 
/*  to perform local update of branch and teller data and invoke the     */ 
/*  remote task, BANKING_SAMPLE_ACTUPD_TASK, for update of an account    */ 
/*  on a remote node.                                                    */ 
/*************************************************************************/ 
 
 
#include ssdef 
#include stdio 
#include descrip 
#include string 
#include ACMS$SUBMITTER                          
 
 
/* DECdtm transaction ID structure */ 
struct tid_structure    (1)
    { 
    long int  field_1; 
    long int  field_2; 
    short int field_3; 
    short int field_4; 
    long int  field_5; 
    } tid; 
 
/* SQL Transaction context structure */ 
struct context_structure    (2)
    { 
    long int version; 
    long int type; 
    long int length; 
    struct tid_structure in_tid; 
    long int end; 
    }; 
 
 
char txn_time[9],txn_date[7]; 
$DESCRIPTOR(date_time_dscrptr,"dd-mmm-yyyy hh:mm:ss.hh"); 
 
 
/* Data Structure for ACMS/SI */    (3)
struct ACMS$SUBMITTER_ID submitter_id; 
struct ACMS$PROCEDURE_ID procedure_id; 
struct dsc$descriptor wksp_dscrptr; 
struct dsc$descriptor tsk_dscrptr; 
struct dsc$descriptor appl_dscrptr; 
char appl_name[32]; 
 
struct single_item_list_structure { 
    short int proc_bufsize; 
    short int proc_itmcode; 
    char     *proc_bufaddr; 
    char     *proc_retlen; 
    int       item_last; 
} single_item_list; 
 
struct arg_list_struct { 
    long int count; 
    char *sel_str; 
    char *ext_sts; 
    char *tsk_io; 
    char *ws_1; 
} arg_list; 
 
/* SQL and Database Declaration */ 
EXEC SQL INCLUDE SQLCA; 
EXEC SQL DECLARE SCHEMA FOR FILENAME BANK_SAMPLE; 
EXEC SQL INCLUDE FROM DICTIONARY BANKING_SAMPLE_WORKSPACE;    (4)
 
 
 
/*********************************************************/ 
/*               Banking Sample Transaction.             */ 
/*********************************************************/ 
banking_sample_txn(workspace_area) 
struct banking_workspace *workspace_area; 
{ 
    struct banking_workspace WSBuff; 
    char ActBranchBuff[5],ActAccountBuff[7]; 
    static struct context_structure context={1,1,16,{0,0,0,0,0},0}; 
    char ErrMsgText[300]; 
    int  ErrMsgLength; 
    int  status; 
    $DESCRIPTOR(ErrMsgBuffDsc,""); 
 
 
    /* Copy some data values from workspace to local buffers due */ 
    /* to the lack of pointer support in Embedded SQL for C.     */ 
 
    WSBuff.workspace_branch = workspace_area->workspace_branch; 
    WSBuff.workspace_teller = workspace_area->workspace_teller; 
    strncpy(ActBranchBuff, 
     workspace_area->workspace_account.workspace_acc_branch, 
            4); 
    strncpy(ActAccountBuff, 
            workspace_area->workspace_account.workspace_acc_account, 
            6); 
    WSBuff.workspace_delta  = workspace_area->workspace_delta; 
 
 
    /* Get TID from ACMS and store into SQL context structure */ 
    status = ACMS$GET_TID(&tid);     (5)
    if (!(status & SS$_NORMAL)) return(status); 
    context.in_tid = tid; 
 
 
    EXEC SQL WHENEVER SQLERROR GOTO sql_error_check; 
 
 
    /* Initialization in preparation for ACMS RPC call. */ 
    status = InitializeACMSRPC();     (6)
    if (!(status & SS$_NORMAL)) 
      return(status); 
 
 
transaction_restart: 
    /***********************************************/ 
    /*                Modify Branch.               */ 
    /***********************************************/ 
 
    /* Perform update on the MONEY field for 
       appropriate branch in BRANCH relation. */ 
    EXEC SQL USING CONTEXT :context 
      UPDATE BRANCH B 
        SET B.BR_MONEY_FIELD = 
          B.BR_MONEY_FIELD + :WSBuff.workspace_delta 
        WHERE B.BR_BRANCH = :WSBuff.workspace_branch; 
 
    /***********************************************/ 
    /*                Modify Teller.               */ 
    /***********************************************/ 
 
    /* Perform update on MONEY field for appropriate 
       teller in the TELLER relation.                */ 
    EXEC SQL USING CONTEXT :context 
      UPDATE TELLER T 
        SET T.TEL_MONEY_FIELD = 
          T.TEL_MONEY_FIELD + :WSBuff.workspace_delta 
        WHERE (T.TEL_BRANCH = :WSBuff.workspace_branch) 
          AND (T.TEL_TELLER = :WSBuff.workspace_teller);        
    
    /**********************************************/ 
    /*  Invoke remote task to update Account and  */     (7)
    /*  store History records via ACMS$CALL.      */ 
    /**********************************************/ 
    wksp_dscrptr.dsc$a_pointer = workspace_area; 
    wksp_dscrptr.dsc$w_length  = sizeof(*workspace_area); 
    status = ACMS$CALL (&submitter_id, 
                        &procedure_id, 
                        &arg_list, 
                        &tid); 
 
 
    /* Check return status code - if failure, log error msg */    (8)
 
    if (!(status & SS$_NORMAL)) { 
      error_logging("ACMS$CALL error.", 
                     status, 
                     WSBuff.workspace_branch, 
                     WSBuff.workspace_teller, 
                     ActBranchBuff, 
                     ActAccountBuff, 
                     WSBuff.workspace_delta); 
      return(status); 
      } 
 
 
    /* End of Transaction. */ 
    workspace_area->workspace_delta = 0; 
    status = SS$_NORMAL; 
    return(status); 
 
 
    /* Log error message if SQL error. */     (9)
    sql_error_check: 
 
      ErrMsgBuffDsc.dsc$a_pointer = ErrMsgText; 
      SQL$GET_ERROR_TEXT(&ErrMsgBuffDsc,&ErrMsgLength); 
      ErrMsgText[ErrMsgLength] = '\0'; 
      error_logging(ErrMsgText, 
                 SQLCA.SQLCODE, 
                 WSBuff.workspace_branch, 
                 WSBuff.workspace_teller, 
                 ActBranchBuff, 
                 ActAccountBuff, 
                 WSBuff.workspace_delta); 
 
      return(SQLCA.SQLCODE); 
} 
 
 
 
InitializeACMSRPC() 
{ 
    int status; 
 
    /* initialize ACMS RPC data structure */    
    tsk_dscrptr.dsc$a_pointer = "BANKING_SAMPLE_ACTUPD_TSK";    (10)
    tsk_dscrptr.dsc$w_length  = 25; 
 
    strcpy(&appl_name[0],"SLVSTR::BANK_SAMPLE_APP"); 
    appl_dscrptr.dsc$a_pointer = &appl_name[0]; 
    appl_dscrptr.dsc$w_length  = strlen(&appl_name[0]); 
 
    /* Sign-in to ACMS as a task submitter. */ 
    status = ACMS$SIGN_IN(&submitter_id,0,0,0,0);     (11)
    if (!(status & SS$_NORMAL)) { 
      error_logging("ACMS sign-in error.",status,0,0," "," ",0); 
      return(status); 
      } 
 
    /* Prepare ACMS$GET_PROCEDURE_INFO item list for remote task */    (12)
    single_item_list.proc_bufsize = ACMS$S_PROCEDURE_ID; 
    single_item_list.proc_itmcode = ACMS$K_PROC_PROCEDURE_ID; 
    single_item_list.proc_retlen  = 0; 
    single_item_list.item_last = 0; 
 
    /* Get remote procedure information */ 
    single_item_list.proc_bufaddr = &procedure_id; 
    status = ACMS$GET_PROCEDURE_INFO (&submitter_id, 
                                      &tsk_dscrptr, 
                                      &appl_dscrptr, 
                                      &single_item_list); 
    if (!(status & SS$_NORMAL)) 
      { 
       error_logging("ACMS GET_PROCEDURE_INFO error.",status, 
                      0,0," "," ",0); 
       return(status); 
      } 
 
    /* Prepare ACMS$CALL() argument list for remote task */ 
    arg_list.count   = 4; 
    arg_list.sel_str = 0; 
    arg_list.ext_sts = 0; 
    arg_list.tsk_io  = 0; 
    arg_list.ws_1    = &wksp_dscrptr;    (13)
 
    return(1); 
} 


Previous Next Contents Index