Skip to content

GSoC:2007 Service Clients Tutorial

Erik Massop edited this page Nov 4, 2017 · 1 revision

Overview

Service clients and clients are essentially clients, but they differ from each other in the sense that the former interact with all the other clients. This page will show you what the essential steps are for creating a service client and how a client can use a service client's method. Hopefully, this will give you a better understanding of what service clients are.

For details of service client API, you should always consult XMMS2 Doxygen Documentation. If you need some working code examples, service client tutorial is a good place to start with. Service Client Manager can also be consulted if you need a full featured service client example.

Note: When registering a service method, you are actually subscribed to a special broadcast. And this is the same when client requests a service method. Thing passing between service clients and clients goes through this special broadcast channel, so you should never do xmmsc_result_wait() on the result returned by xmmsc_service_method_register() nor xmmsc_service_request(), which means that it should be in asynchronous mode. It is bad to mix asynchronous and synchronous modes in a single instance, so keep it asynchronous if you are using service client feature.

Service Client

Since a service client differs from a normal client in the way that it interacts with other clients by providing services and corresponding methods, we need a way to let the server know what our service client provides and how others can use them. This is done by registering the services and methods to the server.

After registration, the server will have enough information to tell clients how to call those methods. Upon method requesting, the callback function you set earlier during registration will be invoked on service client. If you have set any arguments for the method, it is time to get them and do what needs to be done and return data to the callee. Among these steps, fetching the arguments and returning data are covered in this section.

Registration

Registration must happen before any method can be called or even seen as callable by the clients. Even if Service Client Manager has the information of the method, as long as the server does not have a registered copy of it, the method remains "offline".

There are three major steps for registering a method, registering the service it belongs to, creating the method structure and registering the method. You have to provide all information about the service when you register it, including the name of the service, description, major and minor versions. Here is an example.

char service[] = "service.name"; result = xmmsc_service_register (connection, service, "service description", 1, 0); xmmsc_result_unref (result);

Then you have to create the method structure and push the types of the arguments and return values to it. If you do not have any arguments or return values for the method, simply ignore the function calls to push types.

xmmsc_service_method_t *method; method = xmmsc_service_method_new ("method_name", "method description", callback, NULL); if (!xmmsc_service_method_arg_type_add (method, "argument", XMMSC_SERVICE_ARG_TYPE_STRING, 0))     fprintf (stderr, "failed to push type\n"); if (!xmmsc_service_method_ret_type_add (method, "return", XMMSC_SERVICE_ARG_TYPE_STRING, 0))     fprintf (stderr, "failed to push type\n");

Finally, it is time to register the method. Notice that the callback function set earlier will be attached to the broadcast by calling xmmsc_result_notifier_set() automatically, you do not need to do this again.

result = xmmsc_service_method_register (connection, service, method); xmmsc_result_unref (result);

So we are done, the method is registered to the server if no error occurs. But do not hurry to free the method structure. It will be needed later, and it will be freed automatically when it is no longer needed.

A service only needs to be registered once at the beginning if you have multiple methods associate with this service.

Inside a Method

Upon invocation of the method callback function, connection, result, method structure and user data is provided to the callback function as arguments. We can extract arguments from the result if there are any. All arguments passed from client are packed into a dictionary with the names for each argument provided on registration as keys. So you always use xmmsc_result_get_dict_entry_{type}() functions to extract them, like this,

if (!xmmsc_result_get_dict_entry_string (result, "argument", &arg))     xmmsc_service_method_error_set (method, "failed to get argument");

The function xmmsc_service_method_error_set() is used to set error message in the method structure so that it can be returned to the callee. In this case, no return values will be sent.

If all goes well, it is time to return data to the client. For the method we registered in the previous section, we should do something like this,

xmmsc_service_method_ret_add_string (method, "return", "returned string"); res = xmmsc_service_return (connection, result, method); xmmsc_result_unref (res);

It is valid to send return values back to the same client for as many times as you like without having the client making multiple requests. Simply push the new values and call xmmsc_service_return() will suffice.

xmmsc_service_return() is called automatically upon the returning of the service method (Don't worry, all the values you added are deep copied). So the method is no longer available in the new API. Consequently, you cannot send return values back to the same client for as many times as you like without having the client making multiple requests.

Client

Client can list all available services and methods, describe details of each of them and call methods. Before calling a method, the client needs to know the detailed information of the method. So it is necessary to describe the method and its argument and return types before making the request.

Listing

The listing feature I am going to describe here is to list all available (registered in other words) services/methods, not those services and methods in Service Client Manager which are installed but may not be registered. To list all available services, you can do the following,

result = xmmsc_service_list (connection); xmmsc_result_wait (result); if (xmmsc_result_iserror (result))     fprintf (stderr, "%s\n", xmmsc_result_get_error (result));

The result will contain a string list if no errors occur. You can use your usual list handling functions to deal with it.

Listing methods are similar to this. So I am not going to describe in details.

Describing

Listing the names of all available services or methods might not be enough. Sometimes you need detailed information to provide to the user. Then you will need to describe each service and method when needed. To describe a service and extract the detail information, you can do

result = xmmsc_service_describe (connection, "service.name"); xmmsc_result_wait (result); if (xmmsc_result_iserror (result))     fprintf (stderr, "%s\n", xmmsc_result_get_error (result)); else {     xmmsc_result_get_dict_entry_string (result, "description", &description);     xmmsc_result_get_dict_entry_uint (result, "major_version", &major_version);     xmmsc_result_get_dict_entry_uint (result, "minor_version", &minor_version);     xmmsc_result_get_dict_entry_uint (result, "count", &count); } xmmsc_result_unref (result);

Describing a method is similar to this except that the result is not a dictionary. You have to get the resulting method structure by calling xmmsc_result_get_service_method(). The following shows how to describe a method, then create a method structure from the results. Remember that if you need to use the method later, then you have to reference it before unreferencing the result.

result = xmmsc_service_method_describe (connection, "service.name", "method_name"); xmmsc_result_wait (result); if (xmmsc_result_iserror (result)) {     fprintf (stderr, "%s\n", xmmsc_result_get_error (result));     return 1; } xmmsc_result_get_service_method (result, &method); xmmsc_service_method_ref (method); xmmsc_result_unref (result);

Requesting

Once you have described the method and its types, you will have a method structure. Then you can call the method and pass arguments to it if there is any.

xmmsc_service_method_arg_add_string (method, "argument", "hello world"); result = xmmsc_service_request (connection, "service.name", method); xmmsc_result_notifier_set (result, callback, NULL); xmmsc_result_unref (result);

You have to setup a callback function to handle the return value, because it is a broadcast.

void callback (xmmsc_result_t *res, void *data) {     char *arg;     if (xmmsc_result_iserror (res)) {         fprintf (stderr, "%s\n", xmmsc_result_get_error (res));         return;     }     if (!xmmsc_result_get_dict_entry_string (result, "return", &arg))         fprintf (stderr, "failed to get return value\n);     printf ("return value: %s\n", arg); }

After a successful request to the method, the client is subscribed to the broadcast. The service client method can send return values to the client many times whenever it feels like to, unless the service client decides to stop sending, or the client disconnects to the server or simply ignore them. For the following two reasons, it is highly recommended that error checking is always performed at the beginning of the callback function as in the example above. One scenario is that the requested service method might return error messages to the caller. The other one is that if the service method is unregistered unexpectedly on requesting, an error message will be sent to the caller indicating that.

Go back to Service Client Summary.

Clone this wiki locally