Access Keys:
Skip to content (Access Key - 0)

Data Services


Client API


caGrid Data Services provide a standard query method which all data services have in common. This query method is exposed to the grid via a uniform WSDL using request and response messages of a consistent namespace and type. This allows for a uniform client to invoke any arbitrary data service. Such a client is provided with the data services infrastructure, along with several helper classes. This is also true for data services which support WS-Enumeration or caGrid Bulk Data Transfer.

Creating a Query

Data services in caGrid use CQL to compose queries. A query can be produced programmatically, building up parts of the query using the supplied object model.

    CQLQuery query = new CQLQuery();
    Object target = new Object();
    target.setName(Gene.class.getName());
    Attribute symbolAttribute = new Attribute("symbol", Predicate.LIKE, "IL%");
    target.setAttribute(symbolAttribute);
    query.setTarget(target);

Alternatively, a CQL query can be loaded from a string of XML text, or an XML file and deserialized into the object model.

    // from a string
    CQLQuery query2 = (CQLQuery) Utils.deserializeObject(
         new StringReader("<CQLQuery ... />"), CQLQuery.class);
    // from a file
    CQLQuery query3 = (CQLQuery) Utils.deserializeObject(
         new FileReader(cqlFile), CQLQuery.class);

Standard Data Services

Standard data services are those which support the basic query method. A standard data service client may be used to query any caGrid data service via this method, rather than requiring a separate client for each data service on the grid. This is true of all caGrid data services, and other specialized query methods may be included in addition to this one.

Initializing the Client

The generic data service client can be initialized simply by passing the URL of the remote data service service to the constructor:

    String url = "http://localhost:8080/wsrf/services/cagrid/ExampleDataService";
    DataServiceClient standardClient = new DataServiceClient(url);

The generic data service client has only the query method in its public API. To make use of any additional methods you may have added to your service, the specific client generated by Introduce for your service must be used.

Submitting a Query

A query can be submitted to the standard data service client, which will return the query results when processing is complete. The query method can throw two typed exceptions:

  • Query Processing Exception
    • Thrown when an error occurs in actually processing the query or formulating a result set. This might include database errors and misconfiguration of the query processor.
  • Malformed Query Exception
    • This is generally thrown from the query validation process, and indicates a query which violates the CQL schema, or attempts to reach parts of a domain model which are either excluded from queries or do not exist.
    • Query processor implementations may also throw this exception in response to queries which attempt to use features of CQL which are not supported by that particular implementation (eg. Attribute Results)
    try {
         CQLQueryResults results = standardClient.query(query);
    } catch (QueryProcessingExceptionType ex) {
         // handle processing exception
    } catch (MalformedQueryExceptionType ex) {
         // handle malformed query
    } catch (RemoteException ex) {
         // handle remote exception
    }

Handling the Results

The results of a CQL query are returned in an object model which can hold one of either object results, attribute results, or a count result. These results can be iterated as single items using a specialized implementation of the standard Java Iterator interface.

    Iterator iter = new CQLQueryResultsIterator(results, 
         InvocationExample.class.getResourceAsStream("client-config.wsdd"));
    while (iter.hasNext()) {
         Gene g = (Gene) iter.next();
         System.out.println("Found a gene:");
         System.out.println(g.getFullName() + " -- " + g.getSymbol());
    }

Alternatively, the data service infrastructure provides a simplified handle class which hides the complexity of setting up an iterator to handle the query results.

    try {
         String url = "http://localhost:8080/wsrf/services/cagrid/ExampleDataService";
         DataServiceClient standardClient = new DataServiceClient(url); 
         DataServiceHandle handle = new DataServiceHandle(standardClient);
         Iterator i = handle.query(query);
         // iterate
    } catch (QueryProcessingExceptionType ex) {
         // handle query processing exception
    } catch (MalformedQueryExceptionType ex) {
         // handle malformed query
    } catch (MalformedURIException ex) {
         // malformed URI
    } catch (RemoteException ex) {
         // handle remote exception
    }

Enumeration Data Services

Data Services support use of WS-Enumeration for retrieving query results. Since this is also a standard operation with a consistent WSDL, a single Enumeration Data Service Client can be used to query any data service which supports Enumeration.

Initializing the Client

Just as the standard data service client, the enumeration data services client can be initialized with the URL of your grid service:

    String url = "http://localhost:8080/wsrf/services/cagrid/ExampleDataService";
    EnumerationDataServiceClient client = new EnumerationDataServiceClient(url);

Submitting a Query

The generic enumeration client supports a single method:

    EnumerationResponseContainer responseContainer = client.enumerationQuery(query);

When this method is invoked, the service executes the CQL query as it would for a standard data service, but rather than returning the result directly, an enumeration resource is created to store the results, and an EnumerationResponseContainer instance is returned.

The EnumerationResponseContainer is a caGrid data type which encapsulates both the WS-Enumeration spec EnumerationContext and the endpoint reference (EPR) of the enumeration service context and the resource key of created enumeration resource. More information on caGrid's support for WS-Enumeration is provided here.

Handling the Results

The data service infrastructure provides tooling to assist in creating a ClientEnumIterator instance which can communicate with the remote enumeration service context.

    EnumerationResponseContainer responseContainer = client.enumerationQuery(query);
    InputStream wsddStream = 
         ClassUtils.getResourceAsStream(getClass(), "client-config.wsdd");
    ClientEnumIterator iterator = EnumerationResponseHelper.createClientIterator(
        responseContainer, wsddStream);
    // constraints to retrieve 10 results, no char limit, timeout of 5 sec
    IterationConstraints cons = new IterationConstraints(10, -1, DurationUtils.toDuration(5000));
    iterator.setIterationConstraints(cons);
    while (iterator.hasNext()) {
         SOAPElement elem = (SOAPElement) iterator.next();
         // process element
    }

The contents of each SOAP Element are an XML representation CQLObjectResult instances, and can be deserialized as such.

Alternatively, the whole querying and result handling process can be condensed to a few lines using the supplied EnumDataServiceHandle class:

    EnumDataServiceHandle handle = new EnumDataServiceHandle(client);
    Iterator i = handle.query(query);
    // iterate

BDT Data Services

Data Services also support use of the generic caGrid Bulk Data Transfer (BDT) infrastructure, and provide consistent WSDL to access this query method.

Initializing the Client

As with all service clients, the generic BDT Data Service client can be initialized with the URL of the remote data service:

    String url = "http://localhost:8080/wsrf/services/cagrid/ExampleDataService";
    BDTDataServiceClient client = new BDTDataServiceClient(url);

Submitting a Query

The generic BDT data service client implementation exposes one public query method:

    BulkDataHandlerClient bdtClient = client.bdtQuery(query);

The returned BulkDataHandlerClient can be used to invoke either the WS-Enumeration or WS-Transfer support methods.

    // invoke WS-Enumeration
    EnumerationResponseContainer enumResponse = bdtClient.createEnumeration();
    // handle like an enumeration query
                
    // invoke WS-Transfer
    AnyXmlType transferResponse = bdtClient.get(new EmptyType());
    // any type contains XML of CQLQueryResults instance

Handling the Results

In the case of using enumeration from BDT, the result handling is exactly the same as for a data service with enumeration.

For WS-Transfer's get method, the returned xsd AnyType contains the serialized XML of the CQLQueryResults, and can be handled as it is in a standard data service query.

Other Data Service APIs


The caGrid data services infrastructure supplies many client and service side APIs.

Utility Client APIs

The caGrid Data Services infrastructure provides a number of utility classes to make invocation of remote data services a simpler process for the application developer. The package gov.nih.nci.cagrid.data.utilities contains utilities can invoke standard, enumeration, and BDT data services, as well as tools for handling domain models and working with wsdd and castor mapping files.

Iterating Query Results

When a query is performed using the standard caGrid Data Service client's query method, a CQLQueryResults object is returned. This object is a container for both the results themselves and some information pertaining to their type. This container can contain object results, attribute name/value pairs, or a count of the total number of items in the result set. The difficulty of manipulating a container which may contain such a wide variety of result types stored in it may be handled by an iterator class provided with the data service infrastructure.

The interface DataServiceIterator specifies a single query() method, which takes a CQL Query and returns an instance of java.util.Iterator. The iterator can be used to walk through the results of a query issued to a data service. Three concrete implementations of the DataServiceIterator interface are provided, each for communicating with a different type of data service. The DataServiceHandle class can be used to invoke a standard caGrid Data Service, while the EnumerationDataServiceHandle and BdtDataServiceHandle classes are designed for WS-Enumeration and Bulk Data Transfer supporting data services, respectively.

Additionally, this package contains an Iterator utility for handling CQLQueryResults instances directly. The class CQLQueryResultsIterator implements the java.util.Iterator interface, and has three constructors. The choice of constructor affects the behavior of calling the next() method.

  • CQLQueryResultsIterator(CQLQueryResults)
    • Creates an Iterator over the results which will return materialized objects deserialized using the default type mappings.
  • CQLQueryResultsIterator(CQLQueryResults, boolean)
    • Creates an Iterator over the results, and the value of the Boolean parameter indicates if XML strings should be returned from the next() method.
    • If the Boolean value is true, XML text of each item is returned, otherwise the results will be deserialized using the default type mappings.
  • CQLQueryResultsIterator(CQLQueryResults, InputStream)
    • Creates an Iterator over the results, and expects the InputStream will point to a client or server side wsdd.
      • The contents of this wsdd file will be used to configure deserialization of the objects contained in the results.

The class gov.nih.nci.cagrid.data.utilities.CQLQueryResultsIterator implements the java.util.Iterator interface, and so can be used in a while() loop like any other iterator over a Java collection. Depending on what the query to the data service asked for, calls to the next() method of this iterator will return different types of objects.

  • If the query was for object results, then:
    • The iterator returns objects of the type specified as the target for the query.
    • Objects which require custom serialization and/or deserialization require that the iterator be configured with an InputStream to the client-config.wsdd file containing the type mappings for the objects.
    • Alternatively, the iterator can be configured to return only the XML representation of those objects.
  • If the query was for attribute results, including distinct attributes, then:
    • Each successive call to next() returns an array of TargetAttribute types.
    • These types contain the name of an attribute and its value.
    • The value in the TargetAttribute instance will be null if the value was null on the object satisfying the query.Each array of TargetAttributes corresponds to one object instance which satisfied the CQL query criteria.
  • If the query was for a count of object instances, then:
    • The iterator returns a single java.lang.Long value.

An example usage of this iterator is below:

import gov.nih.nci.cagrid.cqlquery.CQLQuery;
import gov.nih.nci.cagrid.cqlresultset.CQLQueryResults;
import gov.nih.nci.cagrid.data.utilities.CQLQueryResultsIterator;

import java.util.Iterator;

public class SampleDataServiceInvocation {
	
    public static void main(String[] args) {
        try {
	    DataServiceClient client = new DataServiceClient(args[0]);
	    CQLQuery query = new CQLQuery();
	    // build up the query
	    CQLQueryResults results = client.query(query);
	    Iterator iter = new CQLQueryResultsIterator(results, 
	        SampleDataServiceInvocation.class
                    .getResourceAsStream(
                        "client-config.wsdd"));
            while (iter.hasNext()) {
		java.lang.Object result = iter.next();
		// do something with the result object
	    }
	} catch (Exception ex) {
	    ex.printStackTrace();
	    System.exit(1);
	}
    }
}
Last edited by
Knowledge Center (1517 days ago) , ...
Adaptavist Theme Builder Powered by Atlassian Confluence