Prerequisites

Before proceeding with the tutorial, please install the following software:

  • Microsoft Visual Studio 2013 or 2015 (any version i.e. Community, Professional, or Enterprise)
  • Oracle VM VirtualBox (V5.0.8r103449 or later) and Oracle VM VirtualBox Extension Pack (matching version).
  • HashiCorp Vagrant (V1.8.1 or later).
  • Avid Platform Virtual Machine (avid-platform-vagrant-vm-YYYY-MM-DD-XXXX.zip)
  • Avid Connector API for C++ (avid-connector-api-cpp-TTT-XXXX.zip)

Note: All of the code and files for this tutorial, including Visual Studio solution files, can be found in the examples\cpp-service folder (part of the avid-connector-api-cpp-TTT-XXXX.zip file).

ExampleService.sln is a Visual Studio 2015 solution file and ExampleService-vc12.sln is a Visual Studio 2013 file.

The Windows C++ Connector API is available as a NuGet package and is included in the examples\cpp-service\win\ExampleService\libs folder.

After opening the already completed example source code solution file, missing NuGet packages and the Connector API NuGet package need to be installed.

To install the dependencies and C++ Connector API NuGet package in Visual Studio 2013:

  • Right-click on the top-most Solution node in the Solution Explorer. Select Manage NuGet Packages for Solution…
  • First, press/choose the “Restore” button to install all of the the dependencies.
  • Press/choose the “Online” item on the left-hand side and then press/choose “BuildFeed”.
  • Select/choose the “Include Prelease” in the middle drop-down list.
  • Find the “Avid Connector API for C++ (vc120) in the list with an Id of avid-acs-proxybal-cpp-vc120. Press the Install button. In the subsequent window, install the package into all of the projects. Wait for a green check mark to appear.
  • Close/exit the Manage NuGet Pagkages window.

To install the dependencies and C++ Connector API NuGet package in Visual Studio 2015:

  • Right-click on the top-most Solution node in the Solution Explorer. Select Manage NuGet Packages for Solution…
  • First, press/choose the “Restore” button to install all of the the dependencies.
  • With “Package source” drop-down set to BuildFeed and the “Include prerelease” drop-down/check-box selected, select Browse. Select avid-acs-proxybal-cpp by Avid Technology, Inc. in the list of packages.
  • On the right-hand side select all of the projects and press/choose the Install button. Wait for “========== Finished ==========” to be displayed in the Output window.

Once all of the packages are installed, you should be able to build the project files via Build -> Build Solution.

Creating the Project Structure

  1. Create a new Windows Console project by selecting File->New->Project....

  2. In the New Project dialog, navigate to Templates->Other Languages->Visual C++->Win32 and select the Win 32 Console Application template.

    Note: If Win32 does not appear in the Visual C++ category, install the “Visual C++ Tools for Windows Desktop” as prompted.

  3. Set the project name to Tutorial and click OK to begin the project creation process.

  4. In the Win 32 Application Wizard that appears, click Next.

  5. On the Application Settings page of the wizard, ensure the following items are selected:

    • Console Application
    • Precompiled Header
    • Security Development Lifecycle (SDL) Checks
  6. Click Finish to create the project and close the wizard.

  7. In the Solution Explorer pane, rename the Tutorial.cpp file to main.cpp.

  8. In the Solution Explorer pane right click on Header Files->Add->New Item and select the Header File(.h) template. Name this ExampleService.h.

  9. Add the Avid-supplied libs folder as a NuGet repository as follows.

    1. Select Tools->NuGet Package Manager->Package Manager Settings.
    2. In the Options dialog that appears, choose Package Sources. In the Available Package Sources area of the dialog, click the plus button.
    3. Name it Avid C++, and for the Source value navigate to the libs folder (extracted from the avid-connector-api-cpp-XXXX.zip).
    4. Click OK to perform the operation and close the Options window.
  10. Add the avid-acs-proxybal-cpp package to solution as follows.

    1. Select Project->Manage NuGet Packages....
    2. Check the Include prerelease checkbox.
    3. Select the Browse option and search for avid-acs-proxybal-cpp.
      Make sure the Package source indicates Avid C++.
    4. When the avid-acs-proxybal-cpp package appears in the results list, select the latest version (v3.4.0-build-XXXX at time of writing).
    5. Click the Install button.
    6. In the Preview dialog that appears, click OK to accept the changes.
    7. In the License Acceptance dialog that appears, click the I Accept button.

    Note: If it becomes necessary to update a package, it is good practise to eliminate dependency issues by uninstalling all packages associated with a project prior to the update. To uninstall a package associated with a project, right-click the project in the Solution Explorer and select Manage Nuget Packages for Solution... from the popup menu.

  11. Close the package manager pane.

Connecting to the Platform

  1. Add the following content to main.cpp:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include "stdafx.h"	
    #include <iostream>

    #include "BusAPI.h"
    #include "BusAccess.h"

    int main(int argc , char* argv[] ) {
    boost::shared_ptr<acs::ConnectionInfo> connectionInfo(ACSCreateConnectionInfo0(), ACSDestroyConnectionInfo);
    boost::shared_ptr<acs::BusAccess> bus(ACSCreateBusAccess7(connectionInfo.get()), ACSDestroyBusAccess);

    ACSResult result = bus->connect();
    if (result) {
    boost::shared_ptr<const char> msg(ACSErrorMessage(result), ACSFreeStr);
    std::cerr << "Failed to connect to the broker (" << msg << ")";
    return -1;
    }

    std::cout << "Connected to the Bus!" << std::endl;

    return 0;
    }
  2. In order to connect to the Avid Platform Virtual Machine you need to set two environment variables:

    1
    2
    ACS_GATEWAY_PORT=9500
    ACS_GATEWAY_HOST=avid-platform-zone1
  3. Build by selecting Build->Rebuild Solution.

  4. Run the solution by selecting Debug->Start Debugging.

    You should see the following output:

    1
    Connected to the Bus!

Creating a Service

  1. Add the following to the ExampleService.h file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
       #ifndef _EXAMPLE_SERVICE_H
    #define _EXAMPLE_SERVICE_H

    #include <iostream>

    #include "BusAPI.h"
    #include "BusAccess.h"

    #define SERVICE_TYPE "avid.tutorial.cpp.service"
    #define SERVICE_VERSION 1
    #define SERVICE_REALM "global"

    class ExampleService : public acs::DispatchOperation {
    public:

    ExampleService();
    ~ExampleService();
    virtual void ACSBUS_CALL invoke(const char* operation, acs::Parameters* parameters, const acs::OperationContext& operationContext);
    bool submitJob(acs::Parameters *parameters, acs::ResultsSP results, std::string &errorCode, acs::ParametersSP errorParameters);
    int getParameterAsInt(acs::Parameters *parameters, const char *name);
    acs::ServiceInfoSP getServiceInfo();
    acs::ServiceInfoSP createServiceInfo();

    private:
    acs::ServiceInfoSP serviceInfo;
    const std::string serviceType;
    const std::string serviceRealm;
    const int serviceVersion;

    public:
    static const char* INVALID_JOB_ID;
    static const char* INVALID_EXECUTION_TIME;
    static const char* UNABLE_TO_UPDATE_NUMBER_OF_SUBMITTED_JOBS;

    private:
    acs::ErrorCode errorCodes[3];
    };

    #endif

  2. Add the following to the ExampleService.cpp file:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
        #include <iostream>
    #include <boost/shared_ptr.hpp>
    #include <boost/algorithm/string/predicate.hpp>

    #include "ExampleService.h"
    #include "BusAPI.h"
    #include "BusAccess.h"

    const char* ExampleService::INVALID_JOB_ID = "InvalidJobId";
    const char* ExampleService::INVALID_EXECUTION_TIME = "InvalidExecutionTime";
    const char* ExampleService::UNABLE_TO_UPDATE_NUMBER_OF_SUBMITTED_JOBS = "UpdateFailed";

    acs::ErrorCode ExampleService::errorCodes[] = {
    { INVALID_JOB_ID, 500, ACS_SEV_ERROR, "Invalid jobId: %{jobId}" },
    { INVALID_EXECUTION_TIME, 500, ACS_SEV_ERROR, "Invalid execution time: %{execTime}" },
    { UNABLE_TO_UPDATE_NUMBER_OF_SUBMITTED_JOBS, 500, ACS_SEV_ERROR, "Unable to update the number of submitted jobs." }
    };


    ExampleService::ExampleService() :serviceType(SERVICE_TYPE), serviceRealm(SERVICE_REALM), serviceVersion(SERVICE_VERSION) {
    serviceInfo = createServiceInfo();
    };

    ExampleService::~ExampleService();

    void ExampleService::invoke(const char *operation, acs::Parameters *parameters, const acs::OperationContext& operationContext) {
    if (boost::iequals(operation, "submitJob"))
    submitJob(parameters, operationContext);
    };

    int ExampleService::getParameterAsInt(acs::Parameters *parameters, const char *name) {
    acs::Parameter *param = parameters->find(name);
    if (!param) return -1;
    const ACSVariant *var = param->value();
    return ACSVariant_cast<int>(var);
    };

    void ExampleService::submitJob(acs::Parameters *parameters, const acs::OperationContext& operationContext)
    {
    acs::ParametersSP errorParameters = acs::ParametersSP(ACSCreateParameters(), ACSDestroyParameters);
    int jobId = getParameterAsInt(parameters, "jobId");
    int execTime = getParameterAsInt(parameters, "execTime");

    if (jobId < 1) {
    errorParameters->addParameter("jobId", acs::Variant(jobId));
    operationContext.error(INVALID_JOB_ID, "", errorParameters.get());
    } else if (execTime <= 0) {
    errorParameters->addParameter("execTime", acs::Variant(execTime));
    operationContext.error(INVALID_EXECUTION_TIME, "", errorParameters.get());
    } else {
    operationContext.respond("status", acs::Variant("submitted"));
    }
    };

    acs::ServiceInfoSP ExampleService::getServiceInfo() {
    return serviceInfo;
    };

    acs::ServiceInfoSP ExampleService::createServiceInfo() {
    acs::ServiceInfoSP serviceInfo = boost::shared_ptr<acs::ServiceInfo>(
    ACSCreateServiceInfo3(serviceType.c_str(), serviceRealm.c_str(), serviceVersion, "An example c++ service!", sizeof(this->errorCodes) / sizeof(errorCodes[0]), errorCodes),
    ACSDestroyServiceInfo);

    serviceInfo->addOperation("submitJob",
    "Submit job for Execution, respond with info whether job submitted successfully or not",
    "submitJob",
    (std::string("{") +
    "\"serviceType\":\"" + serviceType + "\","
    "\"serviceRealm\": \"" + serviceRealm + "\","
    "\"serviceVersion\": 1,"
    "\"op\": \"submitJob\","
    "\"paramSet\": {"
    "\"jobId\": 12345,"
    "\"execTime\": 5"
    "}}").c_str(),
    "example/submitJob/{jobId}", ACS_HTTP_POST, 0, "");
    return serviceInfo;

    };

  3. Modify the main.cpp file to create and start the service:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    #include "stdafx.h"		
    #include <iostream>
    #include <boost/shared_ptr.hpp>


    #include "BusAPI.h"
    #include "BusAccess.h"
    #include "ExampleService.h"

    class AsyncRegisterServiceResultImpl : public acs::AsyncRegisterServiceResult
    {
    public:
    virtual void onSuccess()
    {
    // do something successful
    }

    virtual void onError(const acs::OperationError* error)
    {
    // do somethign with the error
    }

    virtual void addRef() const
    {
    }

    virtual void release() const
    {
    }
    };

    int main(int argc , char* argv[] ) {
    boost::shared_ptr<acs::ConnectionInfo> connectionInfo(ACSCreateConnectionInfo0(), ACSDestroyConnectionInfo);
    boost::shared_ptr<acs::BusAccess> bus(ACSCreateBusAccess7(connectionInfo.get()), ACSDestroyBusAccess);

    ACSResult result = bus->connect();
    if (result) {
    boost::shared_ptr<const char> msg(ACSErrorMessage(result), ACSFreeStr);
    std::cerr << "Failed to connect to the broker (" << msg << ")";
    return -1;
    }

    std::cout << "Connected to the Bus!" << std::endl;

    acs::ServiceContext *serviceContext = 0;
    ExampleService exampleService;

    result = bus->registerService(exampleService.getServiceInfo().get(), &serviceContext, arsr.get());
    if (result) {
    boost::shared_ptr<const char> msg(ACSErrorMessage(result), ACSFreeStr);
    std::cerr << "Failed to register the service: " << msg;
    return -1;
    }

    std::cout << "Starting the ExampleService!" << std::endl;
    bus->runService(serviceContext, static_cast<acs::DispatchOperation*>(&exampleService));


    return 0;
    }
  4. Rebuild the project and run it. You should see the following output:

    1
    2
    Connected to the Bus!
    Starting the ExampleService!
  5. Test that the service is working:

    • Open the ACS Monitor in a web browser and scroll down to locate the avid.tutorial.cpp.service.
    • Expand the Service Operations List for the service. The list should include the submitJob operation.
    • Click the ‘submitJob’ link. Its corresponding message should appear in the code editor area, under the Request to field.
    • Click the ‘Query’ button. Your service’s response appears in the Response field.
  6. Press Ctrl+C in the terminal to terminate the service, or stop it in Visual Studio.

Congratulations! You have successfully registered your service on the Platform.

Sending Requests to Another Service

In this step you will improve the submitJob method and add a new method, getNumberOfSubmittedJobs, to update and request the number of submitted jobs. In addition, you will add methods and interfaces to handle asynchronous requests. Modify ExampleService.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
class SetNumberOfSubmittedJobsResult
: public acs::ReferenceCounter<SetNumberOfSubmittedJobsResult>
, public acs::AsyncOpResult
{
public:
typedef boost::intrusive_ptr<SetNumberOfSubmittedJobsResult> pointer;

SetNumberOfSubmittedJobsResult() {
}

SetNumberOfSubmittedJobsResult(int numberOfSubmittedJobs, acs::OperationContextSP operationContext) : m_numberOfSubmittedJobs(numberOfSubmittedJobs), m_opContext(operationContext) {
}

~SetNumberOfSubmittedJobsResult() {
}

virtual void ACSBUS_CALL onSuccess() {
acs::ResultsSP results(ACSCreateResults(), ACSDestroyResults);
results->addResult("jobStatus", ACSCreateVariant("submitted"));
results->addResult("numberOfSubmittedJobs", ACSCreateVariant(m_numberOfSubmittedJobs));
m_opContext->respond(results.get());
}

virtual void ACSBUS_CALL onError(const acs::OperationError* error);

virtual void ACSBUS_CALL addRef() const {
intrusive_ptr_add_ref(this);
}

virtual void ACSBUS_CALL release() const {
intrusive_ptr_release(this);
}

private:
int m_numberOfSubmittedJobs;
acs::OperationContextSP m_opContext;
};



class GetNumberOfSubmittedJobsResult
: public acs::ReferenceCounter<GetNumberOfSubmittedJobsResult>
, public acs::AsyncQueryResult
{
public:
typedef boost::intrusive_ptr<GetNumberOfSubmittedJobsResult> pointer;

GetNumberOfSubmittedJobsResult() {
}

GetNumberOfSubmittedJobsResult(acs::OperationContextSP operationContext) : m_opContext(operationContext) {
}

~GetNumberOfSubmittedJobsResult() {
}

virtual void ACSBUS_CALL onSuccess(acs::BusMessage* response) {
std::cout << "We got a response from attributes " << std::endl;

int numberOfSubmittedJobs = 0;
if (response->getResults()->size()) {
const ACSVariant *result = response->getResults()->find("value")->value();
numberOfSubmittedJobs = ACSVariant_cast<int>(result);
}
++numberOfSubmittedJobs;

acs::BusMessageSP request(ACSCreateBusMessage2("avid.acs.attributes", "global", 3, "store"));
request->getParameters()->addParameter("name", ACSCreateVariant("cpp.service.store"));
request->getParameters()->addParameter("value", ACSCreateVariant(numberOfSubmittedJobs));

SetNumberOfSubmittedJobsResult::pointer setNumber(new SetNumberOfSubmittedJobsResult(numberOfSubmittedJobs, m_opContext));
m_opContext->getBusAccess().send(request.get(), setNumber.get());
}

virtual void ACSBUS_CALL onError(const acs::OperationError* error);
virtual void ACSBUS_CALL onTimeout();

virtual void ACSBUS_CALL addRef() const {
intrusive_ptr_add_ref(this);
}

virtual void ACSBUS_CALL release() const {
intrusive_ptr_release(this);
}

private:
acs::OperationContextSP m_opContext;;
};

And in ExampleService.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void ExampleService::submitJob(acs::Parameters *parameters, const acs::OperationContext& operationContext)
{
acs::ParametersSP errorParameters = acs::ParametersSP(ACSCreateParameters(), ACSDestroyParameters);
int jobId = getParameterAsInt(parameters, "jobId");
int execTime = getParameterAsInt(parameters, "execTime");

if (jobId < 1) {
errorParameters->addParameter("jobId", acs::Variant(jobId));
operationContext.error(INVALID_JOB_ID, "", errorParameters.get());
} else if (execTime <= 0) {
errorParameters->addParameter("execTime", acs::Variant(execTime));
operationContext.error(INVALID_EXECUTION_TIME, "", errorParameters.get());
} else {
getNumberOfSubmittedJobs(operationContext);
}
};

void ExampleService::getNumberOfSubmittedJobs(const acs::OperationContext& operationContext) {
acs::BusMessageSP request(ACSCreateBusMessage2("avid.acs.attributes", "global", 3, "fetch"));
request->getParameters()->addParameter("name", acs::Variant("cpp.service.store"));


acs::OperationContextSP opContext(ACSCopyOperationContext(operationContext), ACSDestroyOperationContext);
GetNumberOfSubmittedJobsResult::pointer result(new GetNumberOfSubmittedJobsResult(opContext));

operationContext.getBusAccess().query(request.get(), result.get());
};


void ACSBUS_CALL GetNumberOfSubmittedJobsResult::onError(const acs::OperationError* error)
{
m_opContext->error(ExampleService::UNABLE_TO_UPDATE_NUMBER_OF_SUBMITTED_JOBS, error->message());
}

void ACSBUS_CALL GetNumberOfSubmittedJobsResult::onTimeout()
{
m_opContext->error(ExampleService::UNABLE_TO_UPDATE_NUMBER_OF_SUBMITTED_JOBS, "Timed-out");
}

void ACSBUS_CALL SetNumberOfSubmittedJobsResult::onError(const acs::OperationError* error)
{
m_opContext->error(ExampleService::UNABLE_TO_UPDATE_NUMBER_OF_SUBMITTED_JOBS, error->message());
}

Posting Events when the Job is Submitted and Completed

To post to a channel when a job has started/completed, edit ExampleService.

  1. Update ExampleService.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class ChannelPostResult
: public acs::ReferenceCounter<ChannelPostResult>
, public acs::AsyncOpResult
{
public:
typedef boost::intrusive_ptr<ChannelPostResult> pointer;

virtual void ACSBUS_CALL onSuccess()
{
}

virtual void ACSBUS_CALL onError(const acs::OperationError* error)
{
std::cerr << "Post to channel failed: " << error->message() << std::endl;
}

virtual void ACSBUS_CALL addRef() const
{
intrusive_ptr_add_ref(this);
}

virtual void ACSBUS_CALL release() const
{
intrusive_ptr_release(this);
}

private:
std::string m_error;
};

// Add these public declations to ExampleService class.
void postJobStartToChannel(const acs::OperationContext& operationContext, const int jobId, const int execTime);
void postJobCompletedToChannel(const int jobId, const int execTime);

// And this before the declaration of the ExampleService class.
struct JobCompletedData
{
JobCompletedData(const acs::OperationContext& operationContext, const int jobId, const int execTime)
: operationContext(ACSCopyOperationContext(operationContext), ACSDestroyOperationContext)
, jobId(jobId)
, execTime(execTime)
{}

~JobCompletedData()
{
}

acs::OperationContextSP operationContext;
const int jobId;
const int execTime;
};

  1. Update ExampleService.cpp:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
       // Add these definitions to ExampleService.cpp
    // Make sure to include boost/thread/thread.hpp

    void ExampleService::postJobStartToChannel(const acs::OperationContext& operationContext, const int jobId, const int execTime) {
    acs::ChannelMessageSP startJobMessage(ACSCreateChannelMessage("cpp.service.jobs", "submit"), ACSDestroyChannelMessage);
    startJobMessage->dataSet()->add("jobId", acs::Variant(jobId));

    ChannelPostResult::pointer postResult(new ChannelPostResult);
    operationContext.getBusAccess().postToChannel(startJobMessage.get(), postResult.get());

    JobCompletedData data(operationContext, jobId, execTime);

    boost::thread t(&ExampleService::postJobCompletedToChannel, this, data);
    };

    void ExampleService::postJobCompletedToChannel(const int jobId, const int execTime) {

    boost::this_thread::sleep(boost::posix_time::seconds(data.execTime));

    acs::ChannelMessageSP jobDoneMessage(ACSCreateChannelMessage("cpp.service.jobs", "complete"), ACSDestroyChannelMessage);
    jobDoneMessage->dataSet()->add("jobId", acs::Variant(data.jobId));

    ChannelPostResult::pointer postResult(new ChannelPostResult);
    data.operationContext->getBusAccess().postToChannel(jobDoneMessage.get(), postResult.get());
    }

    // Update submitJob in ExampleService.cpp
    void ExampleService::submitJob(acs::Parameters *parameters, acs::Responder *responder)
    {
    acs::ParametersSP errorParameters = acs::ParametersSP(ACSCreateParameters(), ACSDestroyParameters);
    int jobId = getParameterAsInt(parameters, "jobId");
    int execTime = getParameterAsInt(parameters, "execTime");

    if (jobId < 1) {
    errorParameters->addParameter("jobId", acs::Variant(jobId));
    operationContext.error(INVALID_JOB_ID, "", errorParameters.get());
    } else if (execTime <= 0) {
    errorParameters->addParameter("execTime", acs::Variant(execTime));
    operationContext.error(INVALID_EXECUTION_TIME, "", errorParameters.get());
    } else {
    postJobStartToChannel(operationContext, jobId, execTime);
    getNumberOfSubmittedJobs(operationContext);
    }
    };
  2. Rebuild the project to verify it builds without error.

  3. Verify the service in the ACS Monitor.

Congratulations! You have successfully published events to the Platform.

At this point service behavior in the ACS Monitor is unchanged. Next, we create a consumer of our service events.

Subscribing to Service Events

Other services and clients may wish to listen on the channel to which our service posts, so they can receive event information. We demonstrate this functionality by creating a new client that listens on the channel.

  1. Create a new Console Application project by right-clicking on the solution in the Solution Explorer and selecting Add->New Project... from the pop-up menu. Select Visual C++->Windows->Win32->Win32 Console Application, giving it the name ExampleClient. Click the OK button to save your changes.

  2. Add the following to ExampleClient.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
        #include "stdafx.h"
    #include <iostream>
    #include <boost/shared_ptr.hpp>

    #include "BusAPI.h"
    #include "BusAccess.h"

    /**
    * When listening on a channel we must pass in a ChannelEventHandler which will
    * delegate what we do with the information received on the channel.
    */

    class JobStartedEventHandler : public acs::ChannelMessageSubscriber
    {
    public:
    virtual void onChannelMessage(const acs::ChannelMessageContext& channelContext)
    {
    const acs::DataSet *data = channelContext.getChannelMessage().dataSet();
    int jobId = ACSVariant_cast<int>(data->value("jobId"));

    std::cout << "The job " << jobId << " has been submitted" << std::endl;
    }

    };

    class JobCompletedEventHandler : public acs::ChannelMessageSubscriber {
    public:
    virtual void onChannelMessage(const acs::ChannelMessageContext& channelContext)
    {
    const acs::DataSet *data = channelContext.getChannelMessage().dataSet();
    int jobId = ACSVariant_cast<int>(data->value("jobId"));

    std::cout << "The job " << jobId << " has been completed" << std::endl;
    }
    };


    class OperationResult
    : public acs::ReferenceCounter<OperationResult>
    , public acs::AsyncOpResult
    {
    public:
    typedef boost::intrusive_ptr<OperationResult> pointer;

    OperationResult(const std::string& op) : m_op(op) {}

    virtual void ACSBUS_CALL onSuccess()
    {
    std::cout << m_op << " succeeded." << std::endl;
    }

    virtual void ACSBUS_CALL onError(const acs::OperationError* error)
    {
    std::cerr << m_op << " failed: " << error->message() << std::endl;
    }

    virtual void ACSBUS_CALL addRef() const
    {
    intrusive_ptr_add_ref(this);
    }

    virtual void ACSBUS_CALL release() const
    {
    intrusive_ptr_release(this);
    }

    private:
    const std::string m_op;
    };

    int main(int argc, char* argv[])
    {
    boost::shared_ptr<acs::ConnectionInfo> connectionInfo(ACSCreateConnectionInfo0(), ACSDestroyConnectionInfo);
    boost::shared_ptr<acs::BusAccess> bus(ACSCreateBusAccess7(connectionInfo.get()), ACSDestroyBusAccess);

    ACSResult result = bus->connect();
    if (result) {
    boost::shared_ptr<const char> msg(ACSErrorMessage(result), ACSFreeStr);
    std::cerr << "Failed to connect to the broker (" << msg << ")";
    return -1;
    }

    std::cout << "Connected to the Bus!" << std::endl;

    JobStartedEventHandler jobStartedEventHandler;
    acs::ChannelBindingsSP submittedBinding(ACSCreateChannelBindings(), ACSDestroyChannelBindings);
    submittedBinding->addBinding("submit");

    OperationResult::pointer subscribeSubmitResult(new OperationResult("Subscribe to channel (\"cpp.service.jobs/submit\")"));
    bus->subscribeToChannel("cpp.service.jobs", submittedBinding.get(), &jobStartedEventHandler, subscribeSubmitResult.get());

    JobCompletedEventHandler jobCompletedEventHandler;
    acs::ChannelBindingsSP completedBinding(ACSCreateChannelBindings(), ACSDestroyChannelBindings);
    completedBinding->addBinding("complete");

    OperationResult::pointer subscribeCompleteResult(new OperationResult("Subscribe to channel (\"cpp.service.jobs/complete\")"));
    bus->subscribeToChannel("cpp.service.jobs", completedBinding.get(), &jobCompletedEventHandler, subscribeCompleteResult.get());

    std::cout << "Enter any key to quit client" << std::endl;
    char key;
    std::cin >> key;

    return 0;
    }

  3. Set up the Tutorial solution to build both the Service and Client applications by right-click on the solution name in the Solution Explorer and selecting Properties from the pop-up menu.

  4. In the Solution Properties Pages dialog that appears, select Startup Project and Multiple startup projects.

  5. In addition, in the Action column, Select Start or Start without debugging for both ExampleService and ExampleClient.

  6. Build the solution using the “Rebuild Solution” option by selecting Build->Rebuild Solution.

  7. Run the solution by selecting Debug->Start Debugging.

  8. Go to ACS Monitor

  9. Find cpp.tutorial.service and send submitJob a few times with different values
    for jobId.

  10. Test the service to verify it is working:

    • Open the ACS Monitor, and filter the services list by providing cpp\.service regular expression in the box. You should now see now only one service in the list: avid.tutorial.cpp.service
    • Send multiple submitJob messages to your service with different values for jobID.
  11. Return to Visual Studio. In the client terminal you should see the following output for each job submitted:

    1
    2
    3
    The job <jobId> has been submitted
    # After <execTime> seconds
    The job <jobId> has completed

Congratulations! You have created a client that subscribes to the events being posted by your service.

Sending REST Requests to Your Service

  1. Log in to avid-platform-zone1 with ssh (username: root; password: vagrant):

    1
    ssh root@avid-platform-zone1
  2. Send POST requests to your service using the curl command, use the avidAccessToken generated previously:

    1
    2
    3
    curl -H "Content-Type: application/json" --data '{"execTime":15000}' "http://avid-platform-zone1:8080/apis/avid.tutorial.cpp.service;version=1/jobs/10?_avidAccessToken=ZWEwZDhlOTItYmJhZS00YTQ5LWFiN2QtNTAyYWMwZjhjZjJj"

    #NOTE: request should be routed via NGINX and use HTTPS protocol: https://avid-platform-zone1/apis/...

    Given HTTP request will send following message to your service:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "serviceType": "avid.tutorial.cpp.service",
    "serviceRealm": "global",
    "serviceVersion": 1,
    "op": "submitJob",
    "paramSet": {
    "jobId": 10,
    "execTime": 15000
    }
    }
  3. Press Ctrl+C in the shells to terminate both service and client.

Congratulations! You have completed this tutorial.