OGSA-C Programmer’s Guide

Globus Toolkit 3.0 - Last Updated 06/27/2003

Contents

Introduction
Download
Implementing a Client
Gram Client Wrapper API

Introduction

This document is a guide to writing C clients which interact with Grid Services. It covers the core framework, security, and the gram client wrapper API.

Basic knowledge of C is assumed in this guide. We also assume that you are familiar with the basic OGSA environment described in the User's Guide. The gSOAP User's Guide will also be a valuable reference.

 

Download

Bundles

Please refer to the Globus Toolkit download page for further information on downloading the latest release bundles. In order to browse the source of the OGSA-C packages from the GT3 installer, go to the gt3-installer/BUILD directory. The packages that make up the OGSA-C bundle are listed here:

CVS

To get the latest OGSA-C source code from CVS, use the module name "ogsa-c", and follow the instructions on the CVS Howto. Building the source from CVS can be done using the ogsa-c/build_tools/cvs-build-ogsa script. Usually you can just run:


./cvs-build-ogsa gcc32dbg

The -help option gives further information about using that script. There a few pre-requisites before running the script:

Writing a Client

The following steps are involved in writing a C client:

Step 1. Provide a gSOAP Service Description File

The C client framework uses gSOAP as the underlying SOAP implementation. gSOAP generates client side stubs for a service from a gSOAP definition file (usually suffixed as ".gsoap"). The definition file has similar formatting to a C header file, but it should not be included in any C code or compiled by a C compiler. Consider it a separately formatted schema file similar in function to WSDL. Here is an example gSOAP definition file for a simple counter service, included in the GT3 distribution. This coincides with the GWSDL schema file for the counter service. See the gSOAP documentation for complete documentation on the formatting of the gSOAP definition file. A few OGSA-C specific features of the gSOAP definition file should be noted:

Step 2. Creating a GPT package for a gSOAP definitions file

The easiest way to generate and use the bindings code from your gSOAP definition file, is to include it in a GPT package, using the counter sample package as a template. The list of files required to generate a GPT package for the counter sample are (located in ogsa-c/samples/counter/source):
Makefile.am*
configure.in
bootstrap
globus_ogsa_samples_counter_bindings.gsoap*
globus_ogsa_samples_counter_bindings_namespaces.c*
pkgdata/pkg_data_src.gpt.in*
dirt.sh
The files with (*) need to be modified to work with your service name. Be sure to replace all instances of globus_ogsa_samples_counter with your own service's name. In order to build the package, the same prerequisites defined in the CVS portion of the Download section must be met. From the package directory, the build steps are:
./bootstrap
$GPT_LOCATION/sbin/gpt-build <flavor>
This will run the gSOAP stub/skeleton compiler that will generate the bindings for your service, as well as compile the resulting C source code into a library, and install it into $GLOBUS_LOCATION.

Step 3. Generate Support Code

This section provides details on the support code that must be written for a C client to interact with a Grid Service. We will use the counter example with notifications enable as example code throughout. The code can be found at: ogsac/impl/samples/counter/test/notification_counter_test.c. In the bundles, its available in BUILD/globus_ogsa_samples_counter_test-0.1/. You can run the sample test by doing:

$GLOBUS_LOCATION/test/globus_ogsa_samples_counter_test/test-counter-notify \
http://10.0.0.1:8080/ogsa/services/samples/counter/notification/CounterFactoryService 10
The GSH should be modified for your host and port as appropriate.

This section has the following subsections:

Activating Modules
Creating a Service Instance
Signing the createService Request
Sending Operation Requests
Signing or Encrypting Operation Requests
Querying Service Data
Subscribing to Notifications

Activating Modules

Since the OGSA-C libraries build on the Globus Toolkit, the Globus model of activating modules is used. For all OGSA-C clients, the GLOBUS_OGSI_CORE_MODULE should be activated:

   result = (globus_result_t) globus_module_activate(
        GLOBUS_OGSI_CORE_MODULE);
    if(result != GLOBUS_SUCCESS)
    {
        /* handle error */
    }
A module should be activated before any functions from that module are called. Some frequently used OGSA-C modules are: Modules can be deactivate all at once:
    result = (globus_result_t) globus_module_deactivate_all();
or they can be deactivated in the order they were activated.

Creating a Service Instance

As a first step for a Grid Service client, the client should perform the createService operation. A simple createService operation for the counter service looks like this:

#include <globus_ogsi_core.h>

...

    result = globus_ogsi_core_createService(
        contact, NULL, NULL, NULL, NULL,
        &gsr, &gsh, NULL, NULL, NULL, 0);
Further info can be found in the API documentation. The contact is the GSH of the factory that creates the service. The gsr and gsh parameters are returned from the createService call. In this case, the service parameters included in the call are empty. In order to add service parameters that get serialized as the call is sent, a callback should be passed in:
globus_result_t
service_params_callback(
    const char *                        tag,
    void *                              value,
    char **                             buffer,
    size_t *                            buff_length,
    void *                              user_data);

...

    result = globus_ogsi_core_createService(
        contact, NULL, NULL, 
        service_params_callback, service_params_args,
        &gsr, &gsh, NULL, NULL, NULL, 0);
The service_params_callback gets called during serialization of the createService call, and is passed the tag of the service parameters element. The service_params_args should be user data that can be cast to (void *), and appears in the callback as the value argument. The result of the callback should be that *buffer is allocated and filled with the serialized XML of the service parameters. *buff_length should be set to the length of *buffer. If the user doesn't want to keep track of allocated memory, they should use the soap_malloc function, which will deallocate all memory associated with that gSOAP handle, when the handle is destroyed.

Signing the createService Request

If the createService call needs to be signed by the client's proxy certificate, just pass a nonzero value to the last parameter of globus_ogsi_core_createService.

Sending Operation Requests

In order to use any of the generated bindings, a gSOAP handle needs to be initialized. We provide a convenience function for this purpose:

#include <globus_ogsa_utils.h>

...

    globus_ogsa_utils_gsoap_handle_init(
        &gsoap_handle,
        COUNTER_DEFAULT_NS,
        1,
        globus_ogsa_samples_counter_bindings_namespaces);
Once the gSOAP handle has been initialized, operation requests can be made:
    soap_send_counter__add(gsoap_handle, GSH, NULL, 10);
This sends the add request to the counter service, which adds 10 to its current value. In order to received the response the service returns from the add operation, the client needs to do the followiing:
    int                                 new_counter_service_value;

    soap_recv_counter__addResponse(gsoap_handle, &addResponse);

    new_counter_service_value = addResponse.returnValue;

Signing or Encrypting Operation Requests

If the counter service in the above example were secure, the request sent from the client would need to be signed or encrypted before being sent. To do this, the following calls should be made:

    result = globus_ogsa_security_authentication_init(
        security_attrs,
        GSH,
        &gss_context,
        &context_id);
    if(result != GLOBUS_SUCCESS)
    {
        /* do something with error */
    }

    soap_result = soap_register_plugin(
        gsoap_handle,
        &globus_ogsa_security_wsse_gssapi_register);
    if(soap_result != SOAP_OK)
    {
        /* do something with error */
    }

    gssapi_handle = soap_lookup_plugin(
        gsoap_handle,
        GLOBUS_OGSA_SECURITY_WSSE_GSSAPI_PLUGIN_ID);
    if(!gssapi_handle)
    {
        /* error */
    }

    result = globus_ogsa_security_wsse_gssapi_add_gss_context(
        gssapi_handle,
        context_id,
        gss_context);
    if(result != GLOBUS_SUCCESS)
    {
        /* do something with error */
    }

    globus_libc_free(context_id);
This code chunk initializes a GSS context, by negotiating with the grid service via Secure Conversation messages. Once the context is initialized, it is added to the GSSAPI plugin of the gSOAP handle. This allows all messages sent with the gSOAP handle to be encrypted or signed with the GSS context.

Querying Service Data

Before doing a query of service data, the service type (specifying the service data elements) needs to be specified. This is done using the callbacks that were generated from the gSOAP service description:

#include <globus_ogsi_service_plugin.h>

...

    globus_ogsi_service_plugin_handle_attr_init(
        &service_attrs,
        globus_counter__CounterService_s_init,
        globus_counter__CounterService_s_destroy,
        globus_counter__CounterService_s_deserialize,
        globus_counter__CounterService_s_serialize);
This initializes a service attributes handle, which can then be passed to the findServiceData function:
#include <globus_ogsi_core.h>
  
...

    globus_list_t *                     service_data_elements;    

...

    result = globus_ogsi_core_findServiceData(
        GSH,
        globus_ogsa_sample_counter_namespaces,
        "CounterUpdate",
        service_attrs,
        NULL,
        0,
        &service_data_elements);
   if(result != GLOBUS_SUCCESS)
   {
       /* do something with error */
   }
The result is an allocated globus_list_t that holds a list of counter__CounterService types. Each element in the list refers to a service data element returned from the findServiceData request.

Subscribing to Notifications

In order to receive service data notifications, a notification handle needs to be initialized as a first step:

#include <globus_ogsi_notification.h>
#include <globus_ogsi_core.h>

...

    result = globus_ogsi_service_plugin_handle_attr_init(
        &service_attr,
        globus_counter__CounterService_s_init,
        globus_counter__CounterService_s_destroy,
        globus_counter__CounterService_s_deserialize,
        globus_counter__CounterService_s_serialize);
    if(result != GLOBUS_SUCCESS)
    {
        goto exit;
    }

...

    result = globus_ogsi_notification_handle_init(
        &sink_handle,
        globus_ogsa_samples_counter_notify_callback,
        NULL,
        service_attr,
        NULL,
        0,
        "CounterUpdate",
        expireTime,
        globus_ogsa_samples_counter_bindings_namespaces);
    if(result != GLOBUS_SUCCESS)
    {
        /* handle error */
    }
The service attributes are initialized and passed to the notification handle initialization function, as they were with the findServiceData operation discussed previously. globus_ogsa_samples_counter_notify_callback specifies the callback function that will be called when notifications are received from the service. Once the notification handle is initialized, a simple notification sink service can be created that listens for incoming notifications:
    result = globus_ogsi_notification_sink_register_serve(
        sink_handle);
    if(result != GLOBUS_SUCCESS)
    {
        /* handle error */
    }
Now you're ready to perform the subscription and start receiving notifications:
    result = globus_ogsi_notification_subscribe(
        sink_handle,
        NULL,
        GSH);
    if(result != GLOBUS_SUCCESS)
    {
        /* handle error */
    }
Once this call returns, notifications will be sent to listening notification sink, and the specified user callback will be called. The prototype definition for the callback is:
void
globus_ogsa_samples_counter_notify_callback(
    globus_ogsi_notification_handle_t   handle,
    globus_list_t *                     elements,
    void *                              data);
In the callback, the elements parameter contains a list of counter__CounterService types, each referring to a service data element being notified on. There are matching unsbuscribe and unregister functions in the notification api. See the API documentation for further details.

Gram Client Wrapper API

The OGSA-C bundle includes a globus_gram_client API that works with GT3 Managed Job Services. This is useful for anyone who has applications built on the GT2 globus_gram_client API. For this implementation of the API, the function prototypes are identical, but the underlying implementation interacts with a GT3 Managed Job Services, instaed of GT2 jobmanagers. This implementation of the globus_gram_client API can be linked into legacy GT2 applications to enable the application to work with GT3 Managed Job services. Users wishing to do this should take notice of the following: