Advanced Service Sample
=======================

Prior to running this sample make sure you have completed the samples configuration step (:doc:`sampleconfig`).

This sample differs from the :doc:`basicserviceexample` by breaking the "service provider" and "service invoker"
functionality into two distinct scripts. When executed, each of these scripts will contain a unique instance of
a :class:`dxlclient.client.DxlClient`.

Service Provider
****************

The first step is to start the "service provider". This script will remain running and receive
:class:`dxlclient.message.Request` messages from the "service invoker".

To start the "service provider", execute the ``sample\advanced\service_provider_sample.py`` script as follows:

    .. parsed-literal::

        c:\dxlclient-python-sdk-\ |version|\>python sample\\advanced\\service_provider_sample.py

The output should appear similar to the following:

    .. parsed-literal::

        2015-12-30 10:25:32,168 __main__ - INFO - Service Provider - Load DXL config from: c:\dxlclient-python-sdk-\ |version|\\sample/dxlclient.config
        2015-12-30 10:25:32,170 __main__ - INFO - Service Provider - Creating DXL Client
        2015-12-30 10:25:32,187 __main__ - INFO - Service Provider - Connecting to Broker
        2015-12-30 10:25:32,187 dxlclient.client - INFO - Waiting for broker list...
        2015-12-30 10:25:32,188 dxlclient.client - INFO - Checking brokers...
        2015-12-30 10:25:32,190 dxlclient.client - INFO - Trying to connect...
        2015-12-30 10:25:32,190 dxlclient.client - INFO - Trying to connect to broker {Unique id: mybroker, Host name: mybroker.mcafee.com, IP address: 10.84.221.144, Port: 8883}...
        2015-12-30 10:25:32,457 dxlclient.client - INFO - Connected to broker mybroker
        2015-12-30 10:25:32,459 dxlclient.client - INFO - Launching event loop...
        2015-12-30 10:25:32,459 dxlclient.client - INFO - Connected with result code 0
        2015-12-30 10:25:32,460 dxlclient.client - INFO - Subscribing to /mcafee/client/{1b53cd6a-5829-4a76-a913-a120dc59ede7}
        2015-12-30 10:25:32,461 __main__ - INFO - Registering service.
        2015-12-30 10:25:32,463 dxlclient.client - INFO - Message received for topic /mcafee/client/{1b53cd6a-5829-4a76-a913-a120dc59ede7}
           Enter 9 to quit
           Enter value:

The provider will remain running until ``9`` is entered to quit.

Any :class:`dxlclient.message.Request` messages received by the "service invoker" will be displayed in the output.

The code for the provider is very similar to what is being used in the :doc:`basicserviceexample`:

    .. code-block:: python

        # Response callback class to handle DXL Responses to our Asynchronous Requests
        class MyRequestCallback(RequestCallback):
            def on_request(self, request):
                # Extract information from Response payload, in this sample we expect it is UTF-8 encoded
                logger.info("Service Provider - Request received:\n   Topic: %s\n   Request ID: %s\n   Payload: %s",
                            request.destination_topic,
                            request.message_id,
                            request.payload.decode())

                # Create the Response message
                logger.info("Service Provider - Creating Response for Request ID %s on %s",
                            request.message_id, request.destination_topic)
                response = Response(request)

                # Encode string payload as UTF-8
                response.payload = "Sample Response Payload".encode()

                # Send the Response back
                logger.info("Service Provider - Sending Response to Request ID: %s on %s",
                            response.request_message_id, request.destination_topic)
                client.send_response(response)

        # Create DXL Service Registration object
        service_registration_info = ServiceRegistrationInfo(client, "/mycompany/myservice")

        # Add a topic for the service to respond to
        service_registration_info.add_topic(SERVICE_TOPIC, MyRequestCallback())

        # Register the service with the DXL fabric (with a wait up to 10 seconds for registration to complete)
        logger.info("Registering service.")
        client.register_service_sync(service_registration_info, 10)

A :class:`dxlclient.callbacks.RequestCallback` is constructed that will be invoked for a specific topic associated
with the service. The callback will send back a :class:`dxlclient.message.Response` for any
:class:`dxlclient.message.Request` messages that are received.

It then creates a :class:`dxlclient.service.ServiceRegistrationInfo` instance and registers the request
callback with it via the :func:`dxlclient.service.ServiceRegistrationInfo.add_topic` method.

Finally it registers the service with the fabric via the :func:`dxlclient.client.DxlClient.register_service_sync`
method of the :class:`dxlclient.client.DxlClient`.

Service Invoker
***************

The next step is to start the "service invoker". This script must be executed in a separate command prompt (or shell),
leaving the "service provider" running.

To start the "service invoker", execute the ``sample\advanced\service_invoker_sample.py`` script as follows:

    .. parsed-literal::

        c:\\dxlclient-python-sdk-\ |version|\>python sample\\advanced\\service_invoker_sample.py

The output should appear similar to the following:

    .. parsed-literal::

        2015-12-30 10:43:33,627 __main__ - INFO - Service Invoker - Load DXL config from: c:\\dxlclient-python-sdk-\ |version|\\sample/dxlclient.config
        2015-12-30 10:43:33,628 __main__ - INFO - Service Invoker - Creating DXL Client
        2015-12-30 10:43:33,644 __main__ - INFO - Service Invoker - Connecting to Broker
        2015-12-30 10:43:33,645 dxlclient.client - INFO - Waiting for broker list...
        2015-12-30 10:43:33,645 dxlclient.client - INFO - Checking brokers...
        2015-12-30 10:43:33,648 dxlclient.client - INFO - Trying to connect...
        2015-12-30 10:43:33,648 dxlclient.client - INFO - Trying to connect to broker {Unique id: mybroker, Host name: mybroker.mcafee.com, IP address: 10.84.221.144, Port: 8883}...
        2015-12-30 10:43:33,917 dxlclient.client - INFO - Connected to broker mybroker
        2015-12-30 10:43:33,918 dxlclient.client - INFO - Launching event loop...
        2015-12-30 10:43:33,920 dxlclient.client - INFO - Connected with result code 0
        2015-12-30 10:43:33,920 dxlclient.client - INFO - Subscribing to /mcafee/client/{9ee95507-0c73-4696-a628-6a89e2a79194}
           Press 1 to send a Synchronous Request
           Press 2 to send an Asynchronous Request
           Press 9 to quit
           Enter value:

To publish a synchronous :class:`dxlclient.message.Request` message, enter ``1``.

To publish an asynchronous :class:`dxlclient.message.Request` message, enter ``2``.

Information similar to the following should appear in the "service provider" output indicating that the
:class:`dxlclient.message.Request` message was properly received and that a corresponding
:class:`dxlclient.message.Response` message was sent:

    .. code-block:: python

        2015-12-30 10:44:59,832 __main__ - INFO - Service Provider - Request received:
           Topic: /isecg/sample/service
           Request ID: {0e7c1994-b610-4436-ae3b-e2eec9ebdf33}
           Payload: Sample Synchronous Request Payload - Request ID: {0e7c1994-b610-4436-ae3b-e2eec9ebdf33}
        2015-12-30 10:44:59,834 __main__ - INFO - Service Provider - Creating Response for Request ID {0e7c1994-b610-4436-ae3b-e2eec9ebdf33} on /isecg/sample/service
        2015-12-30 10:44:59,835 __main__ - INFO - Service Provider - Sending Response to Request ID: {0e7c1994-b610-4436-ae3b-e2eec9ebdf33} on /isecg/sample/service

Information similar to the following should appear in the "service invoker" output indicating that the
:class:`dxlclient.message.Response` message was properly received:

    .. code-block:: python

        2015-12-30 10:44:59,838 dxlclient.client - INFO - Message received for topic /mcafee/client/{9ee95507-0c73-4696-a628-6a89e2a79194}
        2015-12-30 10:44:59,845 __main__ - INFO - Service Invoker - Synchronous Response received:
           Topic: /mcafee/client/{9ee95507-0c73-4696-a628-6a89e2a79194}
           Payload: Sample Response Payload


The code for making *synchronous requests* is very similar to what is being used in the :doc:`basicserviceexample`:

    .. code-block:: python

        logger.info("Service Invoker - Creating Synchronous Request for topic %s", SERVICE_TOPIC)
        request = Request(SERVICE_TOPIC)

        # Encode string payload as UTF-8
        request.payload = ("Sample Synchronous Request Payload - Request ID: " +
                           str(request.message_id)).encode()

        # Send Synchronous Request with default timeout and wait for Response
        logger.info("Service Invoker - Sending Synchronous Request to %s", SERVICE_TOPIC)
        response = client.sync_request(request)

        # Check that the Response is not an Error Response, then extract
        if response.message_type != Message.MESSAGE_TYPE_ERROR:
            # Extract information from Response payload, in this sample we expect it is UTF-8 encoded
            logger.info("Service Invoker - Synchronous Response received:\n   Topic: %s\n   Payload: %s",
                        response.destination_topic,
                        response.payload.decode())
        else:
            logger.info("Service Invoker - Synchronous Error Response received:\n   Topic: %s\n   Error: %s",
                        response.destination_topic, response.error_message)

A :class:`dxlclient.message.Request` message is constructed and a payload is assigned. The
:func:`dxlclient.client.DxlClient.sync_request` method of the :class:`dxlclient.client.DxlClient` is
invoked which delivers the request message to the fabric. The :class:`dxlclient.message.Response`
message is checked to ensure it is not an error, and its payload is displayed.

The code for making *asynchronous requests* is listed below:

    .. code-block:: python

        # Response callback class to handle DXL Responses from a Service to our Asynchronous Requests
        class MyResponseCallback(ResponseCallback):
            def on_response(self, response):
                # Check that the Response is not an Error Response, then extract
                if response.message_type != Message.MESSAGE_TYPE_ERROR:
                    # Extract information from Response payload, in this sample we expect it is UTF-8 encoded
                    logger.info("Service Invoker - Asynchronous Response received:\n   " +
                                "Topic: %s\n   Request ID: %s\n   Payload: %s",
                                response.destination_topic, response.request_message_id, response.payload.decode())
                else:
                    logger.info("Service Invoker - Asynchronous Error Response received:\n   " +
                                "Topic: %s\n   Request ID: %s\n   Error: %s",
                                response.destination_topic, response.request_message_id, response.error_message)

        # Create the Request
        logger.info("Service Invoker - Creating Asynchronous Request for topic %s", SERVICE_TOPIC)
        request = Request(SERVICE_TOPIC)

        # Encode string payload as UTF-8
        request.payload = 'Sample Asynchronous Request Payload'.encode()

        #Send Asynchronous Request with a timeout of 5 seconds
        logger.info("Service Invoker - Sending Asynchronous Request:\n   Request ID: %s\n   Topic: %s",
                    request.message_id, SERVICE_TOPIC)
        client.async_request(request, MyResponseCallback())

A :class:`dxlclient.callbacks.ResponseCallback` is defined that will receive the :class:`dxlclient.message.Response`
message from the service provider. The callback will display the details of the response message that was
received (including validating that it is not an error response).

A :class:`dxlclient.message.Request` message is constructed and a payload is assigned. The
:func:`dxlclient.client.DxlClient.async_request` method of the :class:`dxlclient.client.DxlClient` is
invoked which delivers the request message to the fabric. Along with the request, an instance of
the previously defined response callback is included which will be invoked when a response is received
from the service provider.
