Developing a COM API client

Scope

Symphony COM API supports synchronous message input and output.

Symphony COM API is intended for clients written with VB6.0, VB Script, or VBA, and services written with C++, Java, or .NET.

Goal

This tutorial walks you through the sample client application code, then guides you through the process of building, packaging, and deploying the associated service.

This tutorial is based on the VBA client code in the SoamExcelSample.xls spreadsheet. The sample code demonstrates how to connect to an application using the COM API.

To help grasp the concepts, the spreadsheet actually contains two code samples. The first sample shows how a simple calculation is performed locally within the spreadsheet itself. The second sample extends the first sample to a grid-ready version by implementing the computational logic as a C++ service that can run on compute hosts in a Symphony cluster.

At a glance

  1. Build the sample client and service

  2. Package the sample service

  3. Add the application

  4. Run the sample client and service

  5. Walk through the code

Prerequisites

  • Ensure you have installed and started Symphony Developer Edition.

Where to find the documentation

Note:

%SOAM_HOME% is an environment variable that represents the Symphony DE installation directory; for example, C:\SymphonyDE\DE51.

Additional documentation is included in the %SOAM_HOME%\docs directory, as follows:

  • COM API Reference: %SOAM_HOME%\docs\symphonyde\5.1\com\api_reference

  • Platform Symphony Reference: %SOAM_HOME%\docs\symphonyde\5.1\reference_sym

  • Error Reference: %SOAM_HOME%\docs\symphonyde\5.1\error_reference

  • Platform Symphony DE Knowledge Center: %SOAM_HOME%\docs\symphonyde\5.1\index.html

Build the sample service

  1. Open the Visual C++ solution file ExcelSampleService_vc71.sln from the location %SOAM_HOME%\5.1\samples\COM\service
  2. Build the solution by pressing ctrl+ shift+B.

    Compiled executables and libraries are in the %SOAM_HOME%\5.1\samples\COM\output directory.

Package the sample service

You must package the files required by your service to create a service package.

  1. Go to the directory that contains the file for the service package:

    cd %SOAM_HOME%\5.1\samples\COM\output

  2. Locate the ExcelSampleService.exe file. Add the file to an archive using a compression program such as gzip. Save the archive as ExcelSampleService.exe.gz in the current directory.

Add the application

When you add an application through the DE PMC, you must use the Add Application wizard. This wizard defines a consumer location to associate with your application, deploys your service package, and registers your application. After completing the steps with the wizard, your application should be ready to use.

  1. Click Symphony Workload > Configure Applications.

    The Applications page displays.

  2. Select Global Actions > Add/Remove Applications.

    The Add/Remove Application page displays.

  3. Select Add an application, then click Continue.

    The Adding an Application page displays.

  4. Select Use existing profile and add application wizard, and browse to your application profile.
  5. Select your application profile xml file, then click Continue.

    For ExcelSample, you can find your profile in the following location:

    • Windows—%SOAM_HOME%\5.1\samples\COM\ExcelSample.xml

    The Service Package location window displays.

  6. Browse to the service package you created in .zip or .gz format and select it, then select Continue.

    The Confirmation window displays.

  7. Review your selections, then click Confirm.

    The window displays indicating progress. Your application is ready to use.

  8. Click Close.

    The window closes and you are now back in the Platform Management Console. Your new application is displayed as enabled.

Run the sample client and service

To run the service, run the client application. The service that a client application uses is specified in the application profile.

Note:

Make sure you have local administrator privileges to register the COM API assembly. Before running the service, log on as local administrator to register COM API.

  1. Register Platform.Symphony.Soam.COM.dll, which is at the location %SOAM_HOME%\5.1\win32-vc7\lib\COM or %SOAM_HOME%\5.1\w2k3_x64-vc7-psdk\lib\COM with regsvr32:

    regsvr32 Platform.Symphony.Soam.COM.dll

    Note:

    For Windows Vista, right-click Command Prompt and select Run as Administrator before entering the command.

  2. Go to the directory that contains SoamExcelSample.xls

    %SOAM_HOME%\5.1\samples\COM\client

  3. In the SoamExcelSample.xls spreadsheet, click Tools>Macro>Visual Basic Editor or press Alt+F11.
  4. In the Visual Basic window, click Tools>References and select Platform.Symphony.Soam.COM 1.0 Type Library.
  5. Click Browse and open Platform.Symphony.Soam.COM.dll from the location %SOAM_HOME%\5.1\win32-vc7\lib\COM or %SOAM_HOME%\5.1\w2k3_x64-vc7-psdk\lib\COM. After clicking OK on the References window, close the Visual Basic window.
  6. On the SoamExcelSample.xls spreadsheet, click the Run Sample on Symphony button to run the application.
    Note:

    If you are using Excel 2000, make sure you set the Excel Macro security level to medium or low.

    The client starts and the system starts the corresponding service. The client displays results in the spreadsheet indicating that it is running.

Walk through the code

You will review the sample client application code to learn how you can create a synchronous client application that makes calls to the Symphony COM API.

Locate the code samples

Client

%SOAM_HOME%\5.1\samples\COM\client\SoamExcelSample.xls

Input/output object

%SOAM_HOME%\5.1\samples\COM\service\MyMessage.cpp

Service

%SOAM_HOME%\5.1\samples\COM\service\ExcelSampleService_vc71.sln

Application profile

The service and application parameters are defined in the application profile:

%SOAM_HOME%\5.1\samples\COM\ExcelSample.xml

What the samples do

The first sample implements computational logic that is processed locally in the spreadsheet. It calculates the standard deviation of 32 sets of values and populates the spreadsheet with the results.

The second sample features a synchronous client that sends 32 input messages to a Symphony service via the COM API. The service takes the input data, performs the calculations, and returns the results.

Since both samples implement the same computational logic, the results are identical.

Local sample

This sample executes locally on the Excel spreadsheet and all the code is contained within the spreadsheet.

Step 1: Get the input data

The CommandButton2_Click event encapsulates the client logic. Follow these steps to locate the CommandButton2_Click event code in the spreadsheet:

  1. Select Tools > Macro > Visual Basic Editor.

  2. In the Project Explorer, double-click Sheet1.

  3. In the Object list box, select CommandButton2.

The taskToSend variable represents the number of input values for the standard deviation algorithm.

We initialize a range of cells where the results will be displayed on the spreadsheet. Next, we declare and initialize a two-dimensional array (values) to hold the results. The array indices correspond to the rows and columns for displaying the results on the spreadsheet.

A for loop increments the row index of the array and cycles through the inputs for the standard deviation calculations, which are read from the spreadsheet. An amplification factor is included in the input to the StandardDeviation() function to increase the input value, if required. The return value of the function is assigned to the array which, in turn, is assigned to the range object that displays the results in the spreadsheet cells.

...
Private Sub CommandButton2_Click()
    Dim k As Integer
    Dim r As Range
    
    Dim taskToSend As Long
    taskToSend = 32
    
    Dim amplification As Integer
    amplification = Range("B8").Value
    
    Set r = Range("D11", "D42")
    Dim values(0 To 32, 0 To 1)
    
    'Cleanup the cells
  For k = 0 To taskToSend - 1
    values(k, 0)=""
    Next k
    r.value2 = values
  
    For k = 0 To taskToSend - 1
        Dim numberOfSamples As Double
        numberOfSamples = CStr(Range("A" & (k + 11)).Value)
  
        values(k, 0) = StandardDeviation(numberOfSamples *
        amplification)
        r.value2 = values
    Next k
End Sub
...

Step 2: Implement the computational logic

The StandardDeviation() function contains the algorithm for calculating the standard deviation. The data set that the algorithm works on is derived from each input value. The result is passed back to the CommandButton2_Click event code and displayed on the spreadsheet.

...
Private Function StandardDeviation(ByVal numberOfSamples As Double)
    Dim i As Long
  Dim mean As Double
  
    StandardDeviation = 0
    mean = 0
  
    For i = 0 To numberOfSamples - 1
        mean = mean + i
    Next i
  
    mean = mean / numberOfSamples
    
    For i = 0 To numberOfSamples - 1
    StandardDeviation = StandardDeviation + (i - mean) * (i -
        mean)
  Next i
    
    StandardDeviation = StandardDeviation / numberOfSamples
    StandardDeviation = Sqr(StandardDeviation)
End Function
...

Symphony sample

This sample invokes the COM API to access a Symphony service.

Step 1: Connect to the application

The CommandButton1_Click event encapsulates the client logic. Follow these steps to locate the CommandButton1_Click event code in the spreadsheet:

  1. Select Tools > Macro > Visual Basic Editor.

  2. In the Project Explorer, double-click Sheet1.

  3. In the Object list box, select CommandButton1.

To send data to a service for processing, you must first connect to an application. You specify an application name, a user name, and password. The application name must match the one defined in the application profile. In this sample, the application name is read from the spreadsheet.

For Symphony Developer Edition, there is no security checking and login credentials are ignored—you can specify any user name and password, such as "Guest". Security checking is done however, when your client application submits workload to the actual grid. The default security callback encapsulates the callback for the user name and password.

...
Private Sub CommandButton1_Click()
    On Error GoTo ReturnFailure
    Dim connection As CSoamConnection
    Range("F43").Value="Test Started...."
    
    'Initialize the Soam context
    Set soamApi = New CSoamAPI
    soamApi.Initialize
  
    'Get the Symphony application name from the spreadsheet
    Dim AppName As String
    AppName = CStr(Range("B7").Value)
    
   'Provide the credentials for the grid
  Dim callback As IDefaultConnectionSecurityCallback
    Set callback = New CDefaultConnectionSecurityCallback
    Call callback.Init("Guest", "Guest")
    
    'Connect to the grid
    Set connection = soamApi.Connect(AppName, callback)
  
    Range("F43").Value="Connection ID " & connection.Id
...

Step 2: Create a session to group tasks

A session is a way of logically grouping tasks that are sent to a service for execution. In this sample, the tasks are sent and received synchronously.

When creating a session, you need to specify the session attributes by using the CSoamSessionCreationAttributes object. We create a CSoamSessionCreationAttributes object called attributes and set three parameters in the object.

                    ...
  'Set the session attributes
  Dim attributes As CSoamSessionCreationAttributes
  Set attributes = New CSoamSessionCreationAttributes
  attributes.SessionName="ShortRunningTasks"
  attributes.SessionType="ShortRunningTasks"
  attributes.SessionFlags = SessionFlags.ReceiveSync
  
  'Create a session on the grid
  Dim session As CSoamSession
  Set session = connection.CreateSession(attributes)
  Range("F43").Value="Session created. Session ID " & session.Id
...

In this example, note that:

  • The first parameter is the session name. This is optional. It can be any descriptive name you want to assign to your session. It is for information purposes.

  • The second parameter is the session type. The session type is optional. You can leave this parameter blank and system default values are used for your session.

    Important:

    The session type must be the same session type as defined in your application profile.

    In the application profile, you define characteristics for the session with the session type.

  • The third parameter is the session flag. When creating a synchronous session, set the flag to SessionFlags.ReceiveSync. This flag indicates to Symphony that this is a synchronous session.

Step 3: Send input data to be processed

The taskToSend variable represents the number of individual data sets (messages) that will be sent to the service. Next, we initialize a range of 32 cells where the results will be displayed on the spreadsheet. We declare and initialize a two-dimensional array (values) to hold the results. The array indices correspond to the rows and columns for displaying the results on the spreadsheet.

The next step is to create the input messages to be processed by the service. We call the MyMessage constructor and pass four input parameters. Note the input parameters:

  • The first parameter is the number of samples. This value is the input data for each standard deviation calculation performed by the service.

  • The second parameter is the line number. This value represents the row in the spreadsheet. As each message is sent, the row value is incremented until all the messages are sent.

  • The third parameter is the column number, which represents the column in the spreadsheet. This value is fixed at 0 since the results will be displayed in a single column. The column value is echoed back to the client in each output message from the service.

  • The fourth parameter is an input message string. It is not used in this sample.

When a message is sent, a task input handle is returned. This task input handle contains the ID for the task that was created for this input message.

...
    Dim k As Integer
    Dim r As Range
  
    Dim taskToSend As Long
    taskToSend = 32
    
    Dim amplification As Integer
    amplification = Range("B8").Value
  
    Set r = Range("F11", "F42")
    Dim values(0 To 32, 0 To 1)
    
    'Cleanup the cells
  For k = 0 To taskToSend - 1
      values(k, 0)=""
    Next k
    r.value2 = values
    'Start calculations
    For k = 0 To taskToSend - 1
        Dim numberOfSamples As Double
        numberOfSamples = CStr(Range("A" & (k + 11)).Value)
       
        Dim message As MyMessage
        Set message = New MyMessage
         
      message.numberOfSamples = numberOfSamples * amplification
        message.line = k
        message.column = 0
        message.StringMessage=""
        Dim inputHandler As CSoamTaskInputHandle
        Set inputHandler = session.SendTaskInput(message)
       
      Range("F43").Value="Sent Message number " & k & "Task ID
        " & inputHandler.Id
    Next k
...

Step 4: Retrieve the output

For each message that is sent to the service, call the FetchTaskOutput() method to retrieve the output message that was produced by the service. By passing a "1" to the method, we are retrieving only one result at a time. Consequently, the return value is an enumeration containing only one completed task result. This was done for demonstration purposes only. Typically, you would pass a value to the FetchTaskOutput() method that represents the total number of tasks sent to the service.

Check that the output for each task was successful before using the PopulateTaskOutput() method to extract the message; otherwise an exception is thrown. Load the result into the array and assign the array to the range object so that the result can be displayed in the appropriate spreadsheet cell.

...
    Range("F43").Value="Waiting for results...."
    Debug.Print "Fetching results."
    
    For k = 0 To taskToSend - 1
       Dim taskEnum As CSoamEnum
       Set taskEnum = session.FetchTaskOutput(1)
       Range("F43").Value="Retrieved task " & taskEnum.Count
       Dim output As CSoamTaskOutputHandle
       For Each output In taskEnum
           
       Range("F43").Value="Retrieved task with ID " & output.Id
          If output.IsSuccessful Then
             
           Dim outMessage As MyMessage
           Set outMessage = New MyMessage
           Call output.PopulateTaskOutput(outMessage)
             Debug.Print "Retrieved message " &
             outMessage.StringMessage
                         
             Dim i, j As Integer
             i = outMessage.line
             j = outMessage.column
               values(i, j) = outMessage.StringMessage
          Else
        
             Debug.Print output.Id & " Task failed."
           Dim exception As CSoamCOMException
             Set exception = output.GetException()
                                     
             Dim reason As String
             reason=""
             Call exception.What(reason)
             Debug.Print output.Id & " task failed: " & reason
           Range("F43").Value = output.Id & " task failed. 
             Reason for failure: " & reason
           End If
     Next output
       r.value2 = values
    Next k
 ...

Step 5: Define a service container

In this sample, calculations on input data are performed by a program that is implemented as a service. A service can be deployed on numerous compute hosts and run as concurrent service instances. For a service to be managed by Symphony, it needs to be in a container object. This is the service container.

In ExcelSampleService.cpp, MyServiceContainer inherits from the base class ServiceContainer.

...
class MyServiceContainer : public ServiceContainer
{
...

Step 6: Process the input

The middleware triggers the invocation of the ServiceContainer's onInvoke() handler every time a task input is sent to the service to be processed. You must implement the onInvoke method to process the task input, perform your computation, and return the result of the computation to the client.

To gain access to the input message from the client, you call the populateTaskInput() method on the task context. The middleware is responsible for placing the input into the taskContext object.

The task context contains all information and functionality that is available to the service during an onInvoke() call in relation to the task that is being processed.

Create the output message and set the following message properties. The values for these properties are read from the input message and echoed back to the client.

  • The first property is the input data for the calculation performed by the service.

  • The second property is the row number for the spreadsheet.

  • The third property is the column number for the spreadsheet.

Pass the input data (m_number_of_samples) to the StandardDeviation() method and format the output message string with the result. Passing the output message to the SetTaskOutput() method generates the task output message that is sent to the client.

...
virtual void onInvoke (TaskContextPtr& taskContext)
    {
      // get the input that was sent from the client
      MyMessage inMsg;
    taskContext->populateTaskInput(inMsg);
      // We simply echo the data back to the client
    MyMessage outMsg;
      outMsg.setnumberOfSamples(inMsg.getnumberOfSamples());
      outMsg.setLine(inMsg.getLine());
      outMsg.setColumn(inMsg.getColumn());
      m_numberOfSamples = inMsg.getnumberOfSamples();
      char buffer[128];
      sprintf(buffer, "%.2f",
      StandardDeviation(m_numberOfSamples));
      outMsg.setString(buffer);
      // set our output message
      taskContext->setTaskOutput(outMsg);
    }
...

Step 7: Run the container

The service is implemented within an executable. As a minimum, we need to create an instance of the service container within our main function and run it.

...
int main(int argc, char* argv[])
{
    // return value of our service program
    int retVal = 0;
    try
    {
        // Create the container and run it
        MyServiceContainer myContainer;
        myContainer.run();
    }
...

Step 8: Catch exceptions

Catch exceptions in case the container fails to start running.

...
    catch (SoamException& exp)
    {
        // report exception to stdout
        cout << "exception caught ... " << exp.what() << endl;
      retVal = -1;
    }
...

Step 9: Create the computational logic of the service

The computational logic of the service is implemented in the StandardDeviation() method. The method generates a data set of integers in the range of 0 up to the numberOfSamples value and applies a standard deviation algorithm to it.

...
 private:
  double StandardDeviation(double numberOfSamples)
  {
      double standardDeviation = 0;
      double mean = 0;
        
      for(int i = 0; i < numberOfSamples; i++)
      {
          mean = mean + i;
      }
      
      mean = mean / numberOfSamples;
        
    for(int i = 0; i < numberOfSamples; i++)
      {
        standardDeviation = standardDeviation + (i - mean) * (i  
          - mean);
    }
      standardDeviation = standardDeviation / numberOfSamples;
      return sqrt(standardDeviation);
  }
...