Join GitHub today
GSoC:2007 Service Clients Details
Service client idea empowers XMMS2 with unlimited expandability. Different service clients can register themselves to the server so that when normal clients want to tag a music file for example, the service client will know what to do. Or normal clients want to look up some data from MusicBrainz.org, service client can perform the search and return the results to the normal clients. In this way, normal clients can share additional features provided by different service clients without having to implement them in every normal client over and over again.
Here is the basic program flow I have in mind. When a normal client tries to invoke a service, an xmmsipc call will be invoked on the server, and the server will look up the corresponding function in the service function pool by a key. If an entry is found, function signatures will be checked. An error will be returned if any of these does not match. Then the server will call the function on that service client and return whatever it is to the normal client. For normal clients, it is up to them to choose whether to use synchronous or asynchronous service call. Normal clients do not have to know which service client provide what, the server will handle that by looking up in the service table. What all normal clients need to know are the keys of the services.
So in this implementation, we provide normal clients a list of all the available services (method calls). For example, on server-side, each method call is hashed. So even if two method calls have exactly the same signature, they will be stored in distinct entries in method call table. On normal client-side, the user can open a method call list on the client (probably in preferences). The client will ask the server for all the registered method calls and their descriptions. Then the user can choose the one he/she like based on the description. What the client will remember is the hashed key, not the method name. So there will not be any conflicts. An example call will look like: xmmsc_call_that_service_client_thing(hashed_key, args...).
The service client methodology is similar to server's plugin system. The difference are that the plugins do not have to reside on the same physical machine, clients also cannot communicate to plugins (except via config_val hacks) and functionality is restrained. And here raise several problems. If I have my server and client running on the same system, and I have over ten different service clients. I would have to run them every time I start the server, which will become a pain. And there is no way to make neither the server nor normal clients to start service clients automatically, because service clients can be anywhere (so xmms2d startup.d system does not help much in this case). I would have to look into this more.
As I saw on this page Generated_IPC. Using XML to define all service calls is a good start. Say we have this service which takes a string and search it on MusicBrainz.org and return a parsed result, the XML definition would look somewhat like this:` ``Search for information on MusicBrainz.org` ` ``search query` ` ``parsed search result`
However, General IPC will not be available any time soon.
The following use cases describe how to register a service client and the procedures for calling a registered service. Before I start the use cases, there are several modifications that need to take place. In enumerator type xmms_ipc_signals_t, add an extra item called XMMS_IPC_SIGNAL_SERVICE. Do similar changes to enumerator xmms_ipc_cmds_t, the new items will be called XMMS_IPC_CMD_SERVICE_REQUEST and XMMS_IPC_CMD_SERVICE_RETURN. At last, we need to create a hash table to store the list of all the registered services on the server. As soon as these changes are in place, I can start describing the use cases.
Use Case 1
Here is what will happen when a new service client registers to the server.
- service client establishes connection with server just like normal clients do.
- service client sends the service names and descriptions to the server. For each service, the server will generate a hash from the name and description of it. All service clients will be registered as listeners for a dedicated service client signal.
- server generates hash and insert (hash, service object) pair into the hash table.
- server responses to the service client's registration request with a result_t object containing its hash key, so that the service client can bind it to a callback function.
- service client waits for requests.
- For two distinct services, even if they share the same service name, their descriptions should be different. So using the service name combined with its corresponding description to generate a hash key would make the service globally uniquely identifiable. Once this hash is calculated, it is globally available. In other words, this would be the only way to identify that service both on the server and clients. All other approaches we proposed earlier including DraX's global counter method do not solve this problem. Imagine starting the same service client twice. I will have two instances of it trying to register to the server. There is no way for the server to tell them apart.
- I propose registering all services as signal XMMS_IPC_SIGNAL_SERVICE, and the server will further extract the hash ID to identify each service. Otherwise, using a different signal for each service will result in changing current signal implementation. Because current implementation depends on a finite enumerate structure to hold all available signal names.
Use Case 2
After registering to the server, service client is ready for requests. The following shows the procedures to use a service client.
- client sends a message with cmd field set to XMMS_IPC_CMD_SERVICE_REQUEST. Hash value of the service and the arguments intended to pass to the service are serialized and put into the data field.
- server gets the message and sees that it is a service request. Then the server deserialize the hash key from data field and look it up in the hash table. If it is found, the message will be passed to the corresponding service client. Otherwise, an error message will be returned to the client. For successful requests, a unique request ID (like a cookie) will be generated and associated with the client object. Then this pair will be stored in a table.
- service client gets the message and start to deserialize the arguments in data field. After checking their validities, service client can go on to do its job. When the result is ready, service client generates a new message with the request ID and the result serialized in the data field and cmd set to XMMS_IPC_CMD_SERVICE_RETURN.
- server sees the message and extract the client ID from the data field. Then fetch the corresponding client object from the table described in step 2. And pass this message to the right client with cmd field set to XMMS_IPC_CMD_SIGNAL and signalid set to XMMS_IPC_SIGNAL_SERVICE.
- client gets the response. It finds out that the message is a service response, then it tries to extract the hash key from data field and lauches the corresponding handler in function process_msg. client then does whatever it like to the result.
- I think the client can call the service asynchroneously, not necessarily synchroneously
Here are a few possible examples of what service clients would be (more details will be added as the project goes). What they have in common is that they do not have to reside on the same machine as the server's. And they receive information from normal clients and return some kind of results back.
- Last.fm: Since it fits service client, it is a good idea to turn current Last.fm support feature into an individual service client.
- ipod: Say you have an ipod and when you plug it in, the service client will detect it and inform normal clients. Then user can manage songs (importing, exporting, change meta data, etc.) on ipod through the interface provided by normal clients.
- Music file management?: This is just an idea, not necessarily be a real service client. XMMS2 has a very good meta data management system, namely mlib and collection. But it is still the user who manages the files, like organizing the folders and renaming those ill-named music files. It will be nice if this can all be done in some manner by XMMS2 automatically.
In conclusion, the server does not need to care about the correctness of the arguments a client passes to a service client. This job will be left to the service client itself. All the server cares about is the hash key. And all services register themselves as signals. My arguments on this issue is that if we have to include function signatures when registering services, we probably have to recompile the server code everytime a new service client is introduced. Obviously, this is not the way it should be implemented. By not having function signatures on the server, we will not introduce extra computation to the server. In addition, it is intuitive for programmers to check the validity of the arguments passed to the functions. In other words, no matter the server checks function signatures or not, the service client will check them any way. So instead of doing the same work twice, we can leave this to the service client. Otherwise, the server would have to deserialize the whole message, check them, and serialize the whole message again.
For the other aspect, we need to provide some accessor functions to help client developers construct data structures and retrieve data, so that they do not get to touch the underlying data structures. (just like current implementation of media collection)
In order to help client developers use services, we need to publish API documentations for all service clients. So they know what to do with the services. All services will be populated in a dict structure. And the structure has a few fixed fields like service name, description and argument descriptions, disregarding the service it is containing. Thus for all client developers, they can robustly assume that these fields exist and create whatever GUI interface they see fit to represent the service list.