CaGrid:Tutorials:1.2:DataService:caCORE SDK 4:Invocation

From caGridWiki

Jump to: navigation, search

Once the tutorial caGrid data service has been created and deployed to a service container, it may be invoked by a grid service client. This tutorial covers importing the project into Eclipse, importing CQL queries from XML, and making use of the generic data service client tooling to query the service and print results.

Contents

Importing The Service into Eclipse

All Introduce generated caGrid services contain both a .project and a .classpath file, which Eclipse uses to determine properties for a Java project (source folders, code builders, libraries, etc). While it is not required to use Eclipse for this tutorial, doing so makes it easier to make changes to the source code, and simplifies testing the client.

Enlarge
Enlarge

To import the project into the Eclipse workspace, select Import from the File menu in Eclipse. Then select Existing Projects into Workspace under General. Click Next. Then, select the root directory of the tutorial service using the Browse button. Once the directory is selected, the name of the project (matching that of the service) will appear in the list. Click Finish to import the tutorial service as an Eclipse project.

Creating a Test Class

To begin making use of the tutorial service, we'll need a place to put source code, as well as an implementation file to make use of the generic data service client and handle some query results. While the client class produced with the service itself can be used for basic testing, domain specific logic should never be placed in the client when used in a production level system. This is because the client may be regenerated, or have methods added and removed by the Introduce toolkit as changes are made to the service model.

Create a new Source Package

To create a new package, expand the Sdk4Example project in Eclipse, followed by the folder named src. This directory houses the source code generated by Introduce which implements the grid service. Create a new package named org.cagrid.sdk4.example.tutorial under the source directory. In Eclipse, this is as easy as right-clicking src, and selecting New->Package. Fill in the name of the package, and click Finish.
Enlarge
Alternatively, this new package may be created as a directory on the file system. From the command line, create the directory src/org/cagrid/sdk4/example/tutorial.

Create a new Class

To create some code which can invoke the grid data service, begin by creating a new Java class. In Eclipse, right click the newly created package, and select New->Class. Name the class QueryRunner, and check the box to create a main method, which will make the class executable. Click Finish to create this class and have Eclipse generate the shell of it.
Enlarge
Alternatively, the class can be created as a simple text file named QueryRunner.java in the src/org/cagrid/sdk4/example/tutorial directory. Once created, the class template should look something like this:
package org.cagrid.sdk4.example.tutorial;

public class QueryRunner {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

    }
}

Create a Class Constructor

For the purposes of this tutorial, we'll create a very simple class constructor for the QueryRunner class. Copy and paste the following code into the new java file, immediately after the class declaration and opening bracket:

    private String serviceUrl;
    private String queryFilename;
    
    public QueryRunner(String serviceUrl, String queryFilename) {
        this.serviceUrl = serviceUrl;
        this.queryFilename = queryFilename;
    }

This constructor simply takes two string properties and stores them in instance variables. These values will be used later to create a client to the remote data service, and load a CQL query from the local file system.

Adding a Query Method

Now a method must be added to the class which can handle invocation of the data service. In the new class file, add the following code after the constructor declaration:

    private void performQuery() {
        try {
            // initialize the generic data service client
            DataServiceClient client = new DataServiceClient(serviceUrl);
            // deserialize the CQL query
            CQLQuery query = (CQLQuery) Utils.deserializeDocument(queryFilename, CQLQuery.class);
            // execute the query on the data service
            System.out.println("Querying");
            CQLQueryResults results = client.query(query);
            // create a results iterator
            System.out.println("Iterating");
            Iterator iter = new CQLQueryResultsIterator(results, true);
            // iterate and print XML
            while (iter.hasNext()) {
                String value = (String) iter.next();
                System.out.println("-- RESULT --");
                System.out.println(value);
            }
            System.out.println("Done");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

This code adds a method named performQuery, which creates a generic data service client, loads a CQL query from the file system, invokes the query, and finally prints XML results to the console.

Additionally, some import statements must be added to the class. At the top of the class file, below the package declaration and before the class declaration, add the following:

import java.util.Iterator;

import gov.nih.nci.cagrid.common.Utils;
import gov.nih.nci.cagrid.cqlquery.CQLQuery;
import gov.nih.nci.cagrid.cqlresultset.CQLQueryResults;
import gov.nih.nci.cagrid.data.client.DataServiceClient;
import gov.nih.nci.cagrid.data.utilities.CQLQueryResultsIterator;

These statements make code from caGrid available in this class, including utilities, the data service client, and query and results handling code.

Implement the Main Method

The last addition to the QueryRunner class is the implementation of the main method. We'll use this method to pass a service URL and query file name to the QueryRunner utility, and then to invoke the newly added performQuery() method. Copy the following code into the main method, replacing the text // TODO Auto-generated method stub:

        if (args.length != 2) {
            System.err.println("USAGE: <serviceUrl> <queryFilename>");
            System.exit(1);
        }
        QueryRunner runner = new QueryRunner(args[0], args[1]);
        runner.performQuery();

Once all code has been added to the QueryRunner class, save the java file.

An Example Query

To make use of the QueryRunner class, we'll need a CQL query which can be executed against the data service developed earlier. Create an xml file in the root of the tutorial service directory named exampleCqlQuery.xml. For example, the directory might look like "C:\caGrid\generatedServices\Sdk4Example". Copy and paste into it the following CQL query in XML form:

<ns1:CQLQuery xmlns:ns1="http://CQL.caBIG/1/gov.nih.nci.cagrid.CQLQuery">
 <ns1:Target name="gov.nih.nci.cacoresdk.domain.other.levelassociation.Suit">
  <ns1:Association name="gov.nih.nci.cacoresdk.domain.other.levelassociation.Card">
   <ns1:Group logicRelation="AND">
    <ns1:Association name="gov.nih.nci.cacoresdk.domain.other.levelassociation.Suit" roleName="suit">
     <ns1:Attribute name="name" predicate="EQUAL_TO" value="Spade"/>
    </ns1:Association>
    <ns1:Attribute name="Name" predicate="EQUAL_TO" value="Ace"/>
   </ns1:Group>
  </ns1:Association>
 </ns1:Target>
</ns1:CQLQuery>

This query will return Suit instances from the example service. These have the requirement that they be associated with a Card, which in turn has an association to a Suit whose name is Spades, and also have a Name with the value Ace. More simply, this query returns the suit of the Ace of Spades.

Executing the Test Class

After the QueryRunner class and example CQL query have been created, we can use them together to query the remote data service and retrieve results.

Execution within Eclipse

Since Eclipse automatically compiles classes when the associated java source files are saved, there is no need to recompile the service. If you are not using Eclipse, run the following command to rebuild the service, including the QueryRunner class:

 

 cd C:\caGrid\generatedServices\Sdk4Example
 ant clean all
Once compiled, the QueryRunner can be invoked. In Eclipse, right-click the QueryRunner class file, and select Run As->Run... to bring up the Run dialog. Click the New Launch Configuration icon in the upper right. The name of the run configuration, the project name, and Main class name will automatically be populated.
Enlarge
Change to the Arguments tab to specify the command line arguments which will be passed to the QueryRunner's main method. In the Program arguments text field, enter the following two items, separated by a single space:
  • http://localhost:8080/wsrf/services/cagrid/Sdk4Example
    • This is the URL of the tutorial grid service, as deployed in the local Tomcat installation
    • If the container's port is different than the default, change 8080 to match it
  • exampleCqlQuery.xml
    • This is the CQL query from earlier in the tutorial. It will be executed by the data service.

Finally, click the Run button to save this configuration and execute the QueryRunner class. As the class executes, any messages from it will be printed to the console. When complete, the console should appear similar to the following:

Querying
Iterating
-- RESULT --
<ns2:Suit name="Spade" id="1" xmlns:ns2="gme://caCORE.caCORE/4.0/gov.nih.nci.cacoresdk.domain.other.levelassociation"/>
Done

Execution outside Eclipse

The QueryRunner class can be executed without Eclipse by means of a simple Ant build script. Copy the following into a file named queryRunnerExec.xml and place it in the root directory of the tutorial service:

<?xml version="1.0"?>
<project default="run" name="Query Runner Execution File" basedir=".">

	<!-- Define the environment variable -->
	<property environment="env" />
	
	<!-- globus dir -->
	<property name="ext.globus.dir" value="${env.GLOBUS_LOCATION}" />

	<!-- Important directories and files -->
	<property name="lib.dir" value="${basedir}/lib"/>
	<property name="build.lib.dir" location="${basedir}/build/lib"/>
	<property name="globus.lib.dir" location="${ext.globus.dir}/lib"/>
	
	<target name="run" description="Runs the QueryRunner class from the caGrid 1.2 Data Services with caCORE SDK 4.0 tutorial">
		<java classname="org.cagrid.sdk4.example.tutorial.QueryRunner" args="${service.url} ${cql.file}">
			<classpath>
				<fileset dir="${build.lib.dir}">
					<include name="**/*.jar"/>
				</fileset>
				<fileset dir="${lib.dir}">
					<include name="**/*.jar"/>
				</fileset>
				<fileset dir="${globus.lib.dir}">
					<include name="**/*.jar"/>
				</fileset>
			</classpath>
		</java>
	</target>

</project>

To use the build file, execute the following in the base directory of the service:

 

  ant -f queryRunnerExec.xml -Dservice.url=http://localhost:8080/wsrf/services/cagrid/Sdk4Example -Dcql.file=exampleCqlQuery.xml

When run, the output should be similar to the following:

Buildfile: queryRunnerExec.xml

run:
     [java] The args attribute is deprecated. Please use nested arg elements.
     [java] Arg: http://localhost:8080/wsrf/services/cagrid/Sdk4Example
     [java] Arg: exampleCqlQuery.xml
     [java] Querying
     [java] Iterating
     [java] -- RESULT --
     [java] <ns2:Suit name="Spade" id="1" xmlns:ns2="gme://caCORE.caCORE/4.0/gov.nih.nci.cacoresdk.domain.other.levelassociation"/>
     [java] Done

BUILD SUCCESSFUL
Total time: 7 seconds

Conclusions

These results indicate the data service retrieved a single Suit instance matching the query criteria. Changing the CQL query passed to the QueryRunner will change the output as the data service locates different objects.

Personal tools