Skip to content

Tutorial 04 05 Add Dispatcher Classes

Matt Linder edited this page Jun 9, 2023 · 23 revisions

Harmony Core Logo

Tutorial 4: Adding Dispatcher Classes for Routines

For each traditional Synergy routine exposed by Traditional Bridge, there must be a dispatcher, which is a class that

  • deserializes in and inout parameters from JSON-RPC request messages into suitable variables and prepares variables for out parameters and function return values.
  • calls the underlying traditional Synergy subroutine or function, passing parameter variables.
  • serializes inout parameters, out parameters, and the function return value into a JSON-RPC response message.

Each dispatcher class must inherit from the RoutineStub class, which is defined in Traditional Bridge library code and has helper methods for serializing/deserializing, exception handling, and logging (see RoutineDispatcher.dbl). Additionally, we recommend organizing all dispatcher classes in a project folder and a namespace path.

If you create a Traditional Bridge environment from scratch, you will need to write routine dispatcher classes. But with xfServerPlus migration, code generation creates these classes for you, as we'll see in the steps that follow.

Generic Dispatcher Code

The following is generic code for a dispatcher class for a Synergy routine with two in parameters and one out parameter. This code includes some placeholders: "<type>" represents the data type for a parameter (e.g., d28.10), "<routine_name>" represents the name of the traditional Synergy routine that the dispatcher is for, and "<interface_name>" represents the name of the (SMC) interface that includes definitions for the Synergy routine.

import Json
import Harmony.TraditionalBridge
import System.Collections
import TraditionalBridge.Models

.ifdef DBLV11
import System.Text.Json
.define JSON_ELEMENT @JsonElement
.else
.define JSON_ELEMENT @JsonValue
.endc

namespace TraditionalBridge.Dispatchers.<interface_name>

    ;;-------------------------------------------------------------------------
    ;;; <summary>
    ;;; Dispatcher for method BridgeMethods.AddTwoNumbers
    ;;; </summary>
    public class <routine_name>_Dispatcher extends RoutineStub

        public method <routine_name>_Dispatcher
        proc
            ;;Initialize the meta data for any data objects that are used by parameters to the method
        endmethod

        protected override method DispatchInternal, void
            required in name,       string
            required in callFrame,  JSON_ELEMENT
            required in serializer, @DispatchSerializer
            required in dispatcher, @RoutineDispatcher
            record
                requestId,          int
                arguments,          JSON_ELEMENT
                argumentDefinition, @ArgumentDataDefinition

                ;;Argument 1 (REQUIRED IN number1 <type>)
                arg1,               d28.10
                ;;Argument 2 (REQUIRED IN number2 <type>)
                arg2,               d28.2
                ;;Argument 3 (REQUIRED OUT result <type>)
                arg3,               d28.10
            endrecord
        proc
            ;;------------------------------------------------------------
            ;;Prepare variables for arguments

            arguments = callFrame.GetProperty("params")

            ;;Argument 1 (REQUIRED IN number1 <type>)

            arg1 = dispatcher.GetImplied(arguments[1])

            ;;Argument 2 (REQUIRED IN number2 <type>)

            arg2 = dispatcher.GetImplied(arguments[2])

            ;;Argument 3 (REQUIRED OUT result <type>)

            ;;------------------------------------------------------------
            ;; Call the underlying routine

            xcall <routine_name>(arg1,arg2,arg3)

            ;;--------------------------------------------------------------------------------
            ;;Argument 3 (REQUIRED OUT result d28.10)
            
            serializer.ArgumentData(3,arg3,FieldDataType.ImpliedDecimalField,28,10,false)
        endmethod

    endclass

endnamespace

Code for Inbound Parameters

As you can see in the generic code above, the arguments variable is used to represent the arguments that are passed to the routine and is populated by the code arguments = callFrame.GetProperty("params"). You can use one of several helper methods that are provided by the RoutineDispatcher class (represented here by the dispatcher variable) to access the data associated with each parameter.

TODO: Document all the helper methods and provide example code here.

Add Dispatcher Classes

With xfServerPlus migration, dispatcher classes are generated from definitions in an SMC. In Add Traditional Synergy Routines, we selected an interface from our sample SMC, so now the code generation feature of the Harmony Core GUI tool will generate dispatchers from methods in the selected interface.

  1. Open a Windows command prompt and navigate to the directory with the .sln file for your Harmony Core solution. Then enter the following command to open the Harmony Core GUI tool:

    harmonycore gui
    
  2. When the "Loading Solution" message disappears, select Codegen > Regen from the menu to generate code for the solution.

  3. In Visual Studio Solution Explorer, right-click the Dispatchers folder in the TraditionalBridge project (TraditionalBridge > Source > Dispatchers) and select Add > Existing Item from the context menu.

  4. In the Add Existing Item window, navigate to the Dispatchers folder, select the BridgeMethodsMethodDispatchers.dbl file, and click Add.

Examining the Generated Routine Dispatchers

  1. Now open BridgeMethodsMethodDispatchers.dbl in the Visual Studio editor. Notice that this file has one dispatcher class for each method in the BridgeMethods interface, which means it has a dispatcher for each of our traditional Synergy routines in the Methods folder.

GetEnvironment_Dispatcher

Let's start by scrolling down to the GetEnvironment_Dispatcher class. This is a good place to start because the traditional Synergy routine it is for (GetEnvironment) has no parameters. GetEnvironment is a function that simply returns an alpha value, which means the dispatcher class must

  • declare a variable to store the return value from the function.
  • call the function and assign the return value to the variable.
  • serialize the return value into the JSON-RPC response to the caller.

Notice that the data division of the DispatchInternal method has a new string variable named returnValue:

returnValue,        string

This method also includes a call to the Synergy function:

returnValue = %GetEnvironment

And there is code to serialize outbound return values:

;;--------------------------------------------------------------------------------
;;Function return value
serializer.ArgumentData(0,%atrim(returnValue),FieldDataType.StringField,,0,false)

This code essentially adds the return value to the JSON-RPC response that will be returned to the caller. The parameters to the ArgumentData method are the following:

  • The argument number, 0, representing the return value of the function
  • The actual value to be returned
  • The data type of the argument, using the ENUM FieldDataType
  • The length of the data being returned
  • The number of decimal places (for implied decimal fields)
  • A Boolean value indicating whether the value contains any binary data

GetLogicalName_Dispatcher

In this case, the routine being called has one inbound parameter and is a function that returns an alpha value. This means the dispatcher class must

  • declare variables for the logical name (to pass to the function) and for the function's return value.
  • call the function, passing the parameter variable and assigning the return value to the return value variable.
  • serialize the return value into the JSON-RPC response to the caller.

Notice that in the data division of the DispatchInternal method, there is a string variable (arg1) for the logical name and a string variable named returnValue:

;;Argument 1 (REQUIRED IN aLogicalName string)
arg1,               string
returnValue,         STRING

The method also has code to extract the value of the inbound parameter from the received JSON-RPC message and store the value in arg1:

;;Argument 1 (REQUIRED IN aLogicalName string)

arg1 = dispatcher.GetText(arguments[1])

There is code to call to the Synergy function:

returnValue = %GetLogicalName(arg1)

And there is code to serialize any outbound return value and/or parameters:

;;Function return value
serializer.ArgumentData(0,%atrim(returnValue),FieldDataType.StringField,,0,false)

AddTwoNumbers_Dispatcher

The AddTwoNumbers routine has three parameters, all defined as numeric, and there are two in parameters and one out parameter. This means the dispatcher class needs to

  • declare variables for the three parameter values.
  • call the subroutine.
  • serialize the out parameter into the JSON-RPC response to the caller.

In the data division of the DispatchInternal method, there are three decimal variables (arg1, arg2 and arg3):

record
    requestId,          int
    arguments,          JSON_ELEMENT
    argumentDefinition, @ArgumentDataDefinition

    ;;Argument 1 (REQUIRED IN number1 d28.10)
    arg1,               d28.10
    ;;Argument 2 (REQUIRED IN number2 d28.2)
    arg2,               d28.2
    ;;Argument 3 (REQUIRED OUT result d28.10)
    arg3,               d28.10
endrecord

The method also has code to extract the values of the inbound parameters from the received JSON-RPC message and store the values in the arg1 and arg2 variables:

;;Argument 1 (REQUIRED IN number1 d28.10)

arg1 = dispatcher.GetImplied(arguments[1])

;;Argument 2 (REQUIRED IN number2 d28.2)

arg2 = dispatcher.GetImplied(arguments[2])

There is code to call to the underlying subroutine:

xcall AddTwoNumbers(arg1,arg2,arg3)

And there is code to serialize the outbound return value:

;;--------------------------------------------------------------------------------
;;Argument 3 (REQUIRED OUT result d28.10)
        
serializer.ArgumentData(3,arg3,FieldDataType.ImpliedDecimalField,28,10,false)

Next topic: Adding the Main Dispatcher Class


Clone this wiki locally