Submitting a job in C using WS GRAM

The following is a general scenario for submitting a job using the C stubs and APIs. Please consult the C WS Core API, WS-GRAM API documentation for details on the APIs used in this example.

1. Loading the job description

    const char *                        file = "job.xml";
    globus_soap_message_handle_t        message;
    wsgram_CreateManagedJobInputType    input;

    globus_soap_message_handle_init_from_file(&message, file);

    globus_soap_message_deserialize_element_unknown(message, &element);

    if(strcmp(element.local, "job") == 0)
    {
        wsgram_JobDescriptionType *     jd;

        input.choice_value.type = wsgram_CreateManagedJobInputType_job;
        jd = &input.choice_value.value.job;

        wsgram_JobDescriptionType_deserialize(&element, jd, message, 0);
    }
    else if(strcmp(element.local, "multiJob") == 0)
    {
        wsgram_MultiJobDescriptionType *       mjd;

        input.choice_value.type = wsgram_CreateManagedJobInputType_multiJob;
        mjd = &input.choice_value.value.multiJob;

        wsgram_MultiJobDescriptionType_deserialize(&element, mjd, message, 0);
    }
    xsd_QName_destroy_contents(&element);
    globus_soap_message_handle_destroy(message);

This code sets the choice value of the wsgram_CreateManagedJobInputType to be the appropriate type depending on whether the job description is a job or multijob request.

2. Setting the security attributes

globus_soap_message_attr_t               message_attr;

globus_soap_message_attr_init(&message_attr);

/*
 * Set authentication mode to host authorization: other possibilities are
 * GLOBUS_SOAP_MESSAGE_AUTHZ_HOST_IDENTITY or
 * GLOBUS_SOAP_MESSAGE_AUTHZ_HOST_SELF.
 */
globus_soap_message_attr_set(
    message_attr,
    GLOBUS_SOAP_MESSAGE_AUTHZ_METHOD_KEY,
    NULL,
    NULL,
    (void *) GLOBUS_SOAP_MESSAGE_AUTHZ_HOST);

/*
 * Set message protection level. GLOBUS_SOAP_MESSAGE_AUTH_PROTECTION_PRIVACY
 * for encryption.
 */
globus_soap_message_attr_set(
    message_attr,
    GLOBUS_SOAP_MESSAGE_AUTH_PROTECTION_KEY,
    NULL,
    NULL,
    (void *) GLOBUS_SOAP_MESSAGE_AUTH_PROTECTION_PRIVACY);

3. Creating the factory client handle

ManagedJobFactoryService_client_handle_t    factory_handle;

result = ManagedJobFactoryService_client_init(
    &factory_handle,
    message_attr,
    NULL);

4. Querying for factory resource properties

4.1. One at a time

/*
 * localResourceManager, or other resource property names as defined in the
 * WSDL
 */
xsd_QName                                property_name =
{
    "http://www.globus.org/namespaces/2004/10/gram/job",
    "localResourceManager"
};
wsrp_GetResourcePropertyResponseType *   property_response;
int                                      fault_type;
xsd_any *                                fault;

ManagedJobFactoryPortType_GetResourceProperty(
    factory_handle,
    endpoint,
    &property_name,
    &property_response,
    (ManagedJobFactoryPortType_GetResourceProperty_fault_t *) &fault_type,
    &fault);

If this is successful, then property_response's any field will contain the deserialized data in the value field of the first element in the array.

xsd_string * localResourceManager = property_response->any.elements[0].value;

printf("local resource manager is %s\n", *localResourceManager);

5. Creating the notification consumer

The notification consumer can be either passed in as part of the wsgram_CreateManagedJobInputType or through a separate invocation of ManagedJobPortType_Subscribe_epr().

    globus_service_engine_t             engine;
    wsa_EndpointReferenceType           consumer_reference;
    
    globus_service_engine_init(&engine, NULL, NULL, NULL, NULL, NULL);

    globus_notification_create_consumer(
            &consumer_reference,
            engine,
            notify_callback,
            NULL);

6. Creating the job resource

First, prepare the other parts of the wsgram_CreateManagedJobInputType structure.

/*
 * You can set input.InitialTerminationTime to be a timeout if interested.
 * The xsd_dateTime type is a struct tm pointer.
 */
time_t                                  term_time = time(NULL);
globus_uuid_t                           uuid;
wsa_AttributedURI *                     job_id;
wsa_EndpointReferenceType *             factory_epr;
xsd_any *                               reference_property;
wsgram_CreateManagedJobOutputType *     output = NULL;
xsd_QName                               factory_reference_id_qname =
{
    "http://www.globus.org/namespaces/2004/10/gram/job",
    "ResourceID"
};

term_time += 60 * 60; /* 1 hour later */
xsd_dateTime_copy(&input.InitialTerminationTime, gmtime(&term_time));

/*
 * Set unique JobID. This is used to reliably create jobs and check for status.
 */
globus_uuid_create(&uuid);
wsa_AttributedURI_init(&job_id);
job_id->base_value = globus_common_create_string("uuid:%s", uuid.text);

/* To subscribe to notifications at create time, add the consumer's EPR to
 * the input message. Otherwise, use the EPR created above in a 
 * call to  
 */
wsnt_SubscribeType_init(&input.Subscribe);
wsa_EndpointReferenceType_copy_contents(
        &input.Subscribe.ConsumerReference,
        &consumer_reference);

xsd_any_init(&input.Subscribe->TopicExpression.any);
&input.Subscribe->TopicExpression.any->any_info =
        &xsd_QName_contents_info;
xsd_QName_copy(
    (xsd_QName **) &input.Subscribe->TopicExpression.any->any.value,
    &ManagedJobPortType_state_rp_qname);

xsd_anyURI_copy_cstr(
    &input.Subscribe->TopicExpression._Dialect,
    "http://docs.oasis-open.org/wsn/2004/06/TopicExpression/Simple");
xsd_boolean_init(&input.Subscribe->UseNotify);

*(&input.Subscribe->UseNotify) = GLOBUS_TRUE;

/* Construct the EPR of the job factory */
wsa_EndpointReferenceType_init(&factory_epr);
wsa_AttributedURI_init_contents(&factory_epr->Address);
xsd_anyURI_init_contents_cstr(&factory_epr->Address.base_value,
        globus_common_create_string(
                "https://%s:%hu/wsrf/services/%s",
                factory_host,
                factory_port,
                MANAGEDJOBFACTORYSERVICE_BASE_PATH);
wsa_ReferencePropertiesTypeinit(&factory_epr->ReferenceProperties);
reference_property = xsd_any_array_push(
        &factory_epr->ReferenceProperties.any);
reference_property->any_info = &xsd_string_info;
xsd_QName_copy(
        &reference_property->element,
        &factory_reference_id_qname);

xsd_string_copy_cstr(
        (xsd_string **) &reference_property->value,
        "Fork");
        
/* Submit the request to the service container */
ManagedJobFactoryPortType_createManagedJob_epr(
        factory_handle,
        factory_epr,
        input,
        &output,
        (ManagedJobFactoryPortType_createManagedJob_fault_t *) &fault_type,
        &fault);

If this is successful, then the output structure will be initialized with the results of the operation. Of particular interest is the managedJobEndpoint which contains the reference to the newly-created job resource.

7. Subscribing for job state notifications

In order to subscribe for job state change notifications to an existing job resource, initialize the subscribe_input used below in the same way as input.Subscribe was initialized above.

    ManagedJobService_client_handle_t   job_handle;
    wsnt_SubscribeType                  subscribe_input;
    wsnt_SubscribeResponseType *        subscribe_response;

ManagedJobService_client_init(
    &job_handle,
    message_attr,
    NULL);

ManagedJobPortType_Subscribe_epr(
    job_handle,
    output->managedJobEndpoint,
    subscribe_input,
    &subscribe_response,
    (ManagedJobPortType_Subscribe_fault_t *) &fault_type,
    &fault);

8. Releasing any state holds (if necessary)

    wsgram_ReleaseInputType             release;
    wsgram_ReleaseOutputType *          release_response = NULL;

    wsgram_ReleaseInputType_init_contents(&release);

ManagedJobPortType_release_epr(
    job_handle,
    output->managedJobEndpoint,
    &release,
    &release_response,
    (ManagedJobPortType_release_fault_t *) &fault_type,
    &fault);

9. Destroying resources

/* destroy subscription resource */
    SubscriptionManagerService_client_init    subscription_handle;
    wsnt_DestroyType                          destroy;
    wsnt_DestroyResponseType *                destroy_response = NULL;

    SubscriptionManagerService_client_init(
        &subscription_handle,
        message_attr,
        NULL);
    /* if subscription done at job creation time, use
     * output->subscriptionEndpoint in place of 
     * subscribe_response->SubscriptionReference,
     */
    SubscriptionManager_Destroy_epr(
        subscription_handle,
        subscribe_response->SubscriptionReference,
        &destroy,
        &destroy_response,
        (SubscriptionManager_Destroy_fault_t *) &fault_type,
        &fault);

    /* destroy the job resource */
    jobPort.destroy(new Destroy());
    ManagedJobPortType_Destroy_epr(
        job_handle,
        output->managedJobEndpoint,
        &destroy,
        &destroy_response,
        (ManagedJobPortType_Destroy_fault_t *) &fault_type,
        &fault);

10. Building a client

In order to build a client application, certain flags must be passed to the compiler and linker to enable them to be able to locate headers and libraries. The easiest way to do so is to generate a makefile header, which is a fragment of a Makefile which includes all of the necessary flags needed to build the application. To do this, issue the command:

% globus-makefile-header --flavor=gcc32dbg globus_c_gram_client_bindings > Makefile.inc

Then, write your makefile to include this file and use the GLOBUS_CC, GLOBUS_LD, GLOBUS_CFLAGS, GLOBUS_LDFLAGS, and GLOBUS_PKG_LIBS macros. For example:

GLOBUS_FLAVOR_NAME=gcc32dbg

include Makefile.inc

CC = $(GLOBUS_CC)
LD = $(GLOBUS_LD)

CFLAGS = $(GLOBUS_CFLAGS)
LDFLAGS = $(GLOBUS_LDFLAGS) $(GLOBUS_PKG_LIBS)

client: client.c