Chapter 20. Developing an OGSA-DAI client

20.1. Introduction
20.2. Basics
20.3. Prerequisites
20.3.1. Set the CLASSPATH
20.4. Assumptions
20.5. The client
20.6. Connecting to a server
20.7. Activities
20.8. An SQL query activity
20.9. A transformation activity
20.10. A transformation activity for efficiency
20.11. A delivery activity
20.12. Workflows
20.13. Working with DeliverToRequestStatus
20.14. Putting it all together

20.1. Introduction

This is a guide that explains how to begin using the Client Toolkit (CTk) to develop clients for OGSA-DAI 3.0. The CTk is a Java API which contains the building blocks from which a range of clients can be developed all the way from simple clients that submit queries to OGSA-DAI services to more sophisticated clients that can do data integration and transformation. The purpose of the CTk is to aid developers by allowing them to focus on the core logic of their process rather than the lower level details of interacting with OGSA-DAI servers. The CTk also removes the need for a client developer to deal with server-side implementation issues.

The guide shows how to develop a simple SQL query client which submits an OGSA-DAI workflow composed of three activities to an OGSA-DAI web service and gets the result.

This guide assumes you have an OGSA-DAI binary distribution and have deployed OGSA-DAI or have access to an OGSA-DAI server.

20.2. Basics

The basic notion is that the client will build a workflow of activities which will be executed on the OGSA-DAI server. There are two key elements here, the activities and the workflow.

Activities are the basic building blocks of OGSA-DAI workflows, they are discrete packages which do one logical step in a process. Activities are broken into several different categories, some of which are resource specific and others which are not. The categories types of activity are:

  • Input data delivery - activities that retrieve input from remote OGSA-DAI data sources.

  • Data access and collection - activities that access data typically held in resources such as databases.

  • Data write - activities that write data to resources such as databases.

  • Data transform - activities that transform data from one format to another. Data processing and data aggregation can be considered to be data transforms.

  • Output data delivery - activities that send data to remote OGSA-DAI data sinks.

  • Management and control - activities that manage the OGSA-DAI server.

Each category contains activities which are focussed around a specific task and are only concerned with doing that task, for instance, the SQLQuery activity deals with the task of querying an SQL database and getting a set of results.

The client toolkit provides support for specifying the activities in a workflow. The OGSA-DAI server actually contains the activity implementations i.e. the functionality of the activities to be executed.

For this simple SQL query client, the main stages which will need to be addressed through activities are:

  1. Querying the database.

  2. Transforming the results to a more useable format.

  3. Transforming the results to a more efficient format.

  4. Delivering the results back to the client.

Each of these stages is a discrete step in the process and forms an activity which is part of the workflow.

OGSA-DAI workflows are made up of activities that can be connected together such that an output of one activity becomes the input of another activity. A set of connected activities are known as a pipeline.

Pipelines can be executed in parallel or sequentially as parts of a larger workflow. The example here uses a contains just a single pipeline.

A detailed implementation-independant overview of activities, workflows and pipelines is given in Section 6.4, “Data-centric workflows”.

20.3. Prerequisites

You first need to set your CLASSPATH. You can do this as follows (or alternatively see Section C.1.18, “ What do I need in my CLASSPATH to be able to run OGSA-DAI clients or compile OGSA-DAI client examples? ”).

20.3.1. Set the CLASSPATH

To set the CLASSPATH in an OGSA-DAI binary distribution:

  • If running under Solaris or Linux run:

    $ source setenv.sh
    

  • If running under Windows run:

    $ setenv.bat
    

The FAQ "Section C.1.18, “ What do I need in my CLASSPATH to be able to run OGSA-DAI clients or compile OGSA-DAI client examples? ”" describes what is needed in the CLASSPATH to run OGSA-DAI clients if the above are not used.

20.4. Assumptions

The code examples assume that the OGSA-DAI server is setup with a default URL prefix (or base services URL) of:

  • OGSA-DAI GT Default: http://localhost:8080/wsrf/services/dai/

This URL is used in the example client code below, and should be changed in your code to reflect the actual setup of the OGSA-DAI server you are using.

It is also assumed that the OGSA-DAI server you are using exposes a relational data resource called MySQLDataResource and that this is associated with a database which contains a littleblackbook table. If this is not the case then replace the resource ID and queries in the code examples with the resource ID and a query compliant with your data resource and database.

If you want to create a littleblackbook please see Chapter 11, Creating test databases.

20.5. The client

The example will be a Java client built using components from the CTk. The example will be in a class called SQLClient. Each step will give example code, and conclude with the overall code at that stage. The example client is split into a number of methods, some of which could be replaced in a more advanced case or the method could be added to. The reason for splitting up what could be a single method into a set of methods is to make explanation of the various steps in developing the example client easier to follow. It also will help when dealing with exceptions and their sources.

This example will execute the following query

SELECT * FROM littleblackbook WHERE id <10;

on a database table littleblackbook held in a database known to an OGSA_DAI server.

It will return the results of the query and output the results as a table.

20.6. Connecting to a server

The first step in any client is to connect to an OGSA-DAI server. To facilitate this, the CTk provides a ServerProxy which implements a Server interface. The requirement is that known OGSA-DAI URLs are provided to the Server.

Server mServer;

public void setupServer(String serverURLStr) throws ClientToolkitException, MalformedURLException
{
    mServer = new ServerProxy();
    mServer.setDefaultBaseServicesURL(new URL(serverURLStr));
}

This proxy is a client-side representation of an OGSA-DAI server. Through it can be obtained proxies to each web service, and so each resource, deployed on the OGSA-DAI server. The base services URL is the prefix for all OGSA-DAI services available.

The Server class provides methods for an instance to access proxies representing the different resources available on the OGSA-DAI server. The proxies, which correspond to the six types of OGSA-DAI resource, include:

DataRequestExecutionResource
DataSinkResource
DataSourceResource
DataResource
RequestResource
SessionResource

This example will use some of these resources to carry out its operation. The first of these is a DataRequestExecutionResource proxy for a DRER. A Data Request Execution Resource is an OGSA-DAI resource that accepts workflows from clients, executes these and possibly returns results. A DRER can be viewed as a workflow management and execution resource. It is typically the first point of contact for clients. The DRER proxy can be obtained by the following code:

DataRequestExecutionResource mDRER;

public void setupDRER(ResourceID drerID) 
    throws ServerException, ClientToolkitException
{
    mDRER = mServer.getDataRequestExecutionResource(drerID);
}

This resource is what will execute the workflows, building a request from the client and instructing the server on what to do. This will be expanded upon when the example deals with workflows and execution.

The code is split into different methods, so that the exceptions which can be thrown at each stage are explicitly shown.

20.7. Activities

Now that the basic connection has been prepared, the activities can be created for the task at hand. In this example, four activities are needed:

SQLQuery
TupleToWebRowSetCharArrays
CharArraysResize
DeliverToRequestStatus

When creating a workflow, all of the inputs and outputs of the activities must be properly connected. If this is not done, then an error concerning an unconnected pipe will be generated. (see Section C.1.9, “When using the client toolkit I get an error about an unconnected pipe”).

20.8. An SQL query activity

The first activity to create is the SQLQuery activity. This activity acts on a relational data resource, issuing an SQL query and receiving a result from the resource. The following code is a method for creating an SQLQuery activity for a SQL query and a data resource. In OGSA-DAI a Data Resource is an OGSA-DAI abstraction of a database or other type of data resource which resides on the OGSA-DAI server. Resource-specific activities - such as those that execute SQL queries - can be targeted at these.

public SQLQuery makeSQLQuery(ResourceID dataResourceID, String sqlStr)
{
    SQLQuery tempQuery = new SQLQuery();
    tempQuery.setResourceID(dataResourceID);
    tempQuery.addExpression(sqlStr);
    return tempQuery;
}

The parameters of the method are the ResourceID for the data resource that is to be targeted, e.g. a MySQL database exposed by the OGSA-DAI server, and the SQL query represented by the string sqlStr.

[Note]Note

The SQLQuery activity does not check the SQL statement for validity prior to execution.

This activity requires that the setResourceID method be used to target a DataResource.

20.9. A transformation activity

In this example the transformation activity we shall use is the TupleToWebRowSetCharArrays. This activity converts tuples, such as those output from a query activity to a web row set format. This activity does not have to be targeted at a resource. It requires that another activity provides the input. At this stage, only the instance is created using a similar method to that for SQLQuery.

public TupleToWebRowSetCharArrays makeTupleToWebRowSetCharArrays()
{
    return new TupleToWebRowSetCharArrays();
}

20.10. A transformation activity for efficiency

In this example we shall add another transformation activity. TupleToWebRowSetCharArrays outputs character arrays which can cause efficiency problems when used with SOAP over HTTP - the medium used for server-client communications in OGSA-DAI.

The CharArraysResize activity can restructure the arrays into a size that results in improved efficiency when transporting them via SOAP over HTTP. The size of the restructured arrays can be specified by the client and, if necessary, tuned.

This activity does not have to be targeted at a resource. It requires that another activity provides the input. At this stage, only the instance is created using a similar method to that for SQLQuery.

public CharArraysResize makeCharArraysResize()
{
    CharArraysResize resize = new CharArraysResize();
    resize.addArraySizeInput(5000);
    return resize;
}

20.11. A delivery activity

Delivery from OGSA-DAI can be carried out in a number of ways, through FTP, GridFTP, SMTP or, like in this example, via a DeliverToRequestStatus activity. This activity ensures that any data it receives are placed in the request status which is returned to a client by a DRER when their workflow completes execution. Again, note that like the transformation activity above, this activity does not need to be explicitly targeted at a resource.

public DeliverToRequestStatus makeDeliverToRequestStatus()
{
    return new DeliverToRequestStatus();
}

All of the above code fragments focus on generating instances of activities and do not connect them together or place them into a workflow. The transform and delivery activities could be instantiated inline in a main listing but for this example splitting them into methods allows for easier integration into the main code.

Now we can build our workflow using a PipelineWorkflow object.

20.12. Workflows

A workflow is a sequence of activities which are related to each other, these activities form a path through which data and control will flow. In this example, a sequential pipeline workflow will be used. There are parallel workflows available and also the ability to have workflows with child workflows (for full details see Section 6.4.8, “Activities and workflows execution order”).

The following method takes as parameters the activities already discussed and connected their input and outputs appropriately and adds them to a PipelineWorkflow. The SQLQuery output is connected to the input of the TupleToWebRowSetCharArrays which in turn has its output connected to the CharArraysResize which in turn has its output connected to the DeliverToRequestStatus. These activities are then all added to the workflow using the add method.

[Note]Note

The order in which the activities are added does not matter as long as the pipes from activity to activity are connected correctly. Otherwise an unconnected pipe error will occur (see Section C.1.9, “When using the client toolkit I get an error about an unconnected pipe”).

public PipelineWorkflow makePipelineWorkflow(SQLQuery query,
                                             TupleToWebRowSetCharArrays transform,
                                             CharArraysResize resize,
                                             DeliverToRequestStatus delivery)
{
    // Create a new pipeline.
    PipelineWorkflow tempFlow = new PipelineWorkflow();
     
    // Connect the query output to the transform input.
    transform.connectDataInput(query.getDataOutput());

    // Connect the transform output to the transform input.
    resize.connectDataInput(transform.getResultOutput());

    // Connect the transform output to the delivery input.
    delivery.connectInput(resize.getOutput());
    
    // Add the activities to the pipeline.
    tempFlow.add(query);
    tempFlow.add(transform);
    tempFlow.add(resize);
    tempFlow.add(delivery);
    
    return tempFlow;
}

Once a workflow pipeline has been created the next step is to execute the pipeline using the DataRequestExecutionResource.

public RequestResource executePipeline(PipelineWorkflow pipe) 
      throws ServerCommsException, 
             ServerException, 
             ResourceUnknownException, 
             ClientException, 
             RequestException, 
             ClientToolkitException
{
    return mDRER.execute(pipe, RequestExecutionType.SYNCHRONOUS);
}

The CTk basically takes the workflow and sends this across to an OGSA-DAI data request execution service, a web service that exposes the DRER. The DRER executes the workflow and returns a request status. The use of a DeliverToRequestStatus activity means the query data returns in the request.

This method returns a RequestResource into which the CTk will deposit the request status from the OGSA-DAI server. Things to note in this method are the exceptions which can be thrown and the type of RequestExecutionType. The RequestExecutionType can be either ynchronous, where the execute method does not return until the DRER on the OGSA-DAI server has completed the workflow execution or asynchronous where the method returns immediately but the DRER on the OGSA-DAI server will continue on with the workflow execution. In such cases the DRER server-side will create a request resource which the client can poll via the RequestResource object returned by this method.

20.13. Working with DeliverToRequestStatus

The DeliverToRequestStatus activity will return the status of the workflow and the results from its connected input to the RequestStatus object. This is obtained by using the following code:

RequestResource rr = sqlClient.executePipeline(pipeline);
RequestStatus   rs = rr.getRequestStatus();

In this example, the results should contain the output of the TupleToWebRowSetCharArrays (the effects of CharArraysResize are transparent in terms of the content of the data) which should look like this:

Request id="ogsadai-113440d0c59"
Request status="uk.org.ogsadai.resource.request.status.COMPLETED"
Result activityInstanceName="ogsadai-1134428a47d" resultName="result"
Data:
--DataItem--->[
--DataItem---><webRowSet xmlns="http://java.sun.com/xml/ns/jdbc" xmlns:xsi="http://www.w3.
org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/jdbc http://java.sun.com/xml/ns/jdbc/webrow
set.xsd">
<properties>
<command></command>
<concurrency></concurrency>
<datasource></datasource>
<escape-processing>true</escape-processing>
<fetch-direction>1000</fetch-direction>
<fetch-size>0</fetch-size>
<isolation-level>0</isolation-level>
<key-columns></key-columns>
<map></map>
<max-field-size>0</max-field-size>
<max-rows>0</max-rows>
<query-timeout>0</query-timeout>
<read-only>true</read-only>
<rowset-type>1003</rowset-type>
<show-deleted>false</show-deleted>
<table-name></table-name>
<url></url>
<sync-provider>
<sync-provider-name/>
<sync-provider-vendor/>
<sync-provider-version/>
<sync-provider-grade/>
<data-source-lock/>
</sync-provider>
</properties>
<metadata>
<column-count>4</column-count>
<column-definition>
<column-index>1</column-index>
<auto-increment></auto-increment>
<case-sensitive></case-sensitive>
<currency></currency>
<nullable>1</nullable>
<signed></signed>
<searchable></searchable>
<column-display-size>11</column-display-size>
<column-label>id</column-label>
<column-name>id</column-name>
<schema-name></schema-name>
<column-precision>11</column-precision>
<column-scale>0</column-scale>
<table-name></table-name>
<catalog-name></catalog-name>
<column-type>4</column-type>
<column-type-name>INTEGER</column-type-name>
</column-definition>
<column-definition>
<column-index>2</column-index>
<auto-increment></auto-increment>
<case-sensitive></case-sensitive>
<currency></currency>
<nullable>1</nullable>
<signed></signed>
<searchable></searchable>
<column-display-size>64</column-display-size>
<column-label>name</column-label>
<column-name>name</column-name>
<schema-name></schema-name>
<column-precision>64</column-precision>
<column-scale>0</column-scale>
<table-name></table-name>
<catalog-name></catalog-name>
<column-type>12</column-type>
<column-type-name>VARCHAR</column-type-name>
</column-definition>
<column-definition>
<column-index>3</column-index>
<auto-increment></auto-increment>
<case-sensitive></case-sensitive>
<currency></currency>
<nullable>1</nullable>
<signed></signed>
<searchable></searchable>
<column-display-size>128</column-display-size>
<column-label>address</column-label>
<column-name>address</column-name>
<schema-name></schema-name>
<column-precision>128</column-precision>
<column-scale>0</column-scale>
<table-name></table-name>
<catalog-name></catalog-name>
<column-type>12</column-type>
<column-type-name>VARCHAR</column-type-name>
</column-definition>
<column-definition>
<column-index>4</column-index>
<auto-increment></auto-increment>
<case-sensitive></case-sensitive>
<currency></currency>
<nullable>1</nullable>
<signed></signed>
<searchable></searchable>
<column-display-size>20</column-display-size>
<column-label>phone</column-label>
<column-name>phone</column-name>
<schema-name></schema-name>
<column-precision>20</column-precision>
<column-scale>0</column-scale>
<table-name></table-name>
<catalog-name></catalog-name>
<column-type>12</column-type>
<column-type-name>VARCHAR</column-type-name>
</column-definition>
</metadata>
<data>
--DataItem---><currentRow>
<columnValue>1</columnValue>
<columnValue>Ally Antonioletti</columnValue>
<columnValue>101 Antonioletti Road, San Jose</columnValue>
<columnValue>087192027</columnValue>
</currentRow>
--DataItem---><currentRow>
<columnValue>2</columnValue>
<columnValue>Amy Atkinson</columnValue>
<columnValue>70 Atkinson Crescent, Southampton</columnValue>
<columnValue>0105931111</columnValue>
</currentRow>
--DataItem---></data>
</webRowSet>
--DataItem--->]
Activity instanceName="ogsadai-1134428a47e" status="COMPLETED"
Activity instanceName="ogsadai-1134428a47b" status="COMPLETED"
Activity instanceName="ogsadai-1134428a47d" status="COMPLETED"
Activity instanceName="ogsadai-1134428a47f" status="COMPLETED"
</output>

This output is quite unwieldy for someone to look at but in applications it would be passed from the TupleToWebRowSetCharArrays activity to another client, or to a method, for formatting the output in a more usable format.

The status of the execution of the request can be checked by calling the getExecutionStatus method on the RequestStatus object. This code will simply print out the execution status,

RequestStatus rs = rr.getRequestStatus();
System.out.println(rs.getExecutionStatus());

and in most cases this should output:

uk.org.ogsadai.resource.request.status.COMPLETED

If an error has occurred then the status can return as COMPLETED_WITH_ERROR.

20.14. Putting it all together

As a simple example of using all of the above code, a main method can be added and used with the query stated before. Remember that, as explained in Section 20.4, “Assumptions”, you may have to change the base services URL on line 3, the SQL statement on line 5 and data resource ID on line 6 depending on your server setup and relational data resource.

public static void main(String[] args) throws Exception
{
    String serverURL = "http://localhost:8080/dai/services/";
    ResourceID drerID = new ResourceID("DataRequestExecutionResource");
    String sqlStr = "SELECT * FROM littleblackbook WHERE id<3;";
    ResourceID dataResourceID = new ResourceID("MySQLDataResource");

    SQLClient iSQLClient = new SQLClient();

    iSQLClient.setupServer(serverURL);
    iSQLClient.setupDRER(drerID);
       
    SQLQuery sqlQ = iSQLClient.makeSQLQuery(dataResourceID, sqlStr);
    DeliverToRequestStatus dtrs = iSQLClient.makeDeliverToRequestStatus();
    TupleToWebRowSetCharArrays ttwrsca =   iSQLClient.makeTupleToWebRowSetCharArrays();
    CharArraysResize car =   iSQLClient.makeCharArraysResize();
    PipelineWorkflow pwf = iSQLClient.makePipelineWorkflow(sqlQ, ttwrsca, car, dtrs);
    RequestResource rr = iSQLClient.executePipeline(pwf);
    RequestStatus rs = rr.getRequestStatus();
    System.out.println(rs.getExecutionStatus());    
}

The above code does not take into account exceptions at this point. The first few lines set up the SQL query string and the resource IDs. The first of these is the string for the base services URL for the OGSA-DAI server.

The client can be run from your working directory:

$ java SQLClient

Before moving on to consider the exceptions, the output from the TupleToWebRowSetCharArrays can be used in a number of ways and, to finish off this example client, a way to obtain a tabular output will be shown. This can access the activity output directly and get access to the metadata for resultant output to format the table.

First a small method that gives TupleToWebRowSetCharArrays a direct reference to the request status so it can parse it. This is a consequence of using CharArraysResize for improving efficiency:

private static void setRequestStatus(TupleToWebRowSetCharArrays
                                     tupleToWebRowSet,
                                     DeliverToRequestStatus
                                     deliverToRequestStatus)
{
    tupleToWebRowSet.getResultOutput().setDeliverToRequestStatusActivity(deliverToRequestStatus);
}

Then a small method to assist in padding out strings:

private static String pad(String base, int width)
{
    StringBuffer baseBuffer = new StringBuffer(base);
    int padLength = width - base.length();
    for (int i = 0; i < padLength; i++)
    {
        baseBuffer.append(" ");
    }
    return baseBuffer.toString();
}

This method will pad any string out to the stated width in the parameters.

Then to write out the table of results, the TupleToWebRowSetCharArrays is accessed directly after the request has finished executing and written out as a table using the metadata and results. The following method will use the pad method and write the table to output.

public void printTable(TupleToWebRowSetCharArrays tupleToWebRowSet)
    throws SQLException,
           DataStreamErrorException,
           UnexpectedDataValueException,
           WebRowSetResultSetParseException,
           DataSourceUsageException
{
    if (tupleToWebRowSet.hasNextResult())
    {
        // Get ResultSet.
        ResultSet rs = tupleToWebRowSet.nextResultAsResultSet();

        // Get ResultSet metadata.
        ResultSetMetaData md = rs.getMetaData();

        // Get column names and initial column widths.
        int numColumns = md.getColumnCount();
        String[] columns = new String[numColumns];
        int[] widths = new int[numColumns];

        for (int i = 0; i < numColumns; i++)
        {
            String column = md.getColumnLabel(i + 1);
            columns[i] = column;
            widths[i] = column.length();
        }

        // Get ResultSet rows and update column widths also.
        Vector rows = new Vector();
        while (rs.next()) 
        {
            String[] fields = new String[numColumns];
            for (int i = 0; i < numColumns; i++)
            {
                fields[i] = rs.getString(i + 1);
                if (fields[i] == null)
                {
                     fields[i] = "null";
                }
                widths[i] = Math.max(widths[i], fields[i].length());
            }
            rows.add(fields);
        }
        rs.close();

        // Print column names.
        String tableHeading = "| ";
        for (int i = 0; i < numColumns; i++)
        {
            tableHeading += (pad(columns[i], widths[i]) + " | ");
        }
        System.out.println(tableHeading);

        // Print rows.
        for (int j = 0; j < rows.size(); j++)
        {
            String[] row = (String[])rows.get(j);
            String rowString = "| ";
            for (int i = 0; i < numColumns; i++)
            {
                rowString += (pad(row[i], widths[i]) + " | ");
            }
            System.out.println(rowString);
        }
    }
} 

The output from a short query should look similar to this:

| id | name                 | address                           | phone      |
| 1  | Ally Antonioletti    | 101 Antonioletti Road, San Jose   | 087192027  |
| 2  | Amy Atkinson         | 70 Atkinson Crescent, Southampton | 0105931111 |

The final code without exception handling should look like this. Remember that, as explained in Section 20.4, “Assumptions”, you may have to change the base services URL, the SQL statement and data resource ID in the main method depending on your server setup and relational data resource.

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Vector;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;

import uk.org.ogsadai.client.toolkit.DataRequestExecutionResource;
import uk.org.ogsadai.client.toolkit.DataValueIterator;
import uk.org.ogsadai.client.toolkit.PipelineWorkflow;
import uk.org.ogsadai.client.toolkit.RequestExecutionType;
import uk.org.ogsadai.client.toolkit.RequestResource;
import uk.org.ogsadai.client.toolkit.Server;
import uk.org.ogsadai.client.toolkit.ServerProxy;
import uk.org.ogsadai.client.toolkit.activities.block.CharArraysResize;
import uk.org.ogsadai.client.toolkit.activities.delivery.DeliverToRequestStatus;
import uk.org.ogsadai.client.toolkit.activities.sql.SQLQuery;
import uk.org.ogsadai.client.toolkit.activities.transform.TupleToWebRowSetCharArrays;
import uk.org.ogsadai.client.toolkit.exception.ClientException;
import uk.org.ogsadai.client.toolkit.exception.ClientToolkitException;
import uk.org.ogsadai.client.toolkit.exception.RequestException;
import uk.org.ogsadai.client.toolkit.exception.ResourceUnknownException;
import uk.org.ogsadai.client.toolkit.exception.ServerCommsException;
import uk.org.ogsadai.client.toolkit.exception.ServerException;
import uk.org.ogsadai.client.toolkit.exception.DataSourceUsageException;
import uk.org.ogsadai.client.toolkit.exception.DataStreamErrorException;
import uk.org.ogsadai.client.toolkit.exception.UnexpectedDataValueException;
import uk.org.ogsadai.converters.webrowset.WebRowSetResultSetParseException;
import uk.org.ogsadai.resource.ResourceID;
import uk.org.ogsadai.resource.request.RequestStatus;

/**
 * Example SQL Client
 */
public class SQLClient
{
    private Server mServer;
    private DataRequestExecutionResource mDRER;
    
    /**
     * Constructor
     */
    public SQLClient()
    {
        mServer = null;
        mDRER = null;
    }

    /**
     * Method will retrieve an OGSA-DAI server from the parameter
     * serverURLstr.
     *
     * @param serverURLStr
     * @throws ClientToolkitException
     * @throws MalformedURLException
     */  
    public void setupServer(String serverURLStr) 
        throws ClientToolkitException, MalformedURLException
    {
        mServer = new ServerProxy();
        mServer.setDefaultBaseServicesURL(new URL(serverURLStr));
    }

    /**
     *  Method will get the DRER identified by the ResourceID from the server.
     *  
     * @param drerID

     * @throws ClientToolkitException
     */
    public void setupDRER(ResourceID drerID) 
        throws ServerException, ClientToolkitException
    {
        mDRER = mServer.getDataRequestExecutionResource(drerID);
    }
   
    /**
     * Method creates a SQLQuery activity for the identified resource
     * and given query 
     * @param dataResourceID
     * @param sqlStr
     * @return SQLQuery
     */
    public SQLQuery makeSQLQuery(ResourceID dataResourceID, String sqlStr)
    {
        SQLQuery tempQuery = new SQLQuery();
        tempQuery.setResourceID(dataResourceID);
        tempQuery.addExpression(sqlStr);
        return tempQuery;
    }
   
    /**
     * Creates a new DeliverToRequestStatus activity
     * @return DeliverToRequestStatus
     */
    public DeliverToRequestStatus makeDeliverToRequestStatus()
    {
        return new DeliverToRequestStatus();
    }
   
    /**
     * Creates a new TupleToWebRowSetCharArrays activity
     * @return TupleToWebRowSetCharArrays
     */
    public TupleToWebRowSetCharArrays makeTupleToWebRowSetCharArrays()
    {
        return new TupleToWebRowSetCharArrays();
    }

    /**
     * Creates a new CharArraysResize activity
     * @return CharArraysResize
     */
    public CharArraysResize makeCharArraysResize()
    {
        CharArraysResize resize = new CharArraysResize();
        resize.addArraySizeInput(5000);
        return resize;
    }

    /**
     * 
     * Creates a new pipelineworkflow for the three activities.
     * Connects the activities, query-transform-delivery
     * @param query
     * @param transform
     * @param delivery
     * @return PipelineWorkflow
     */
    public PipelineWorkflow makePipelineWorkflow(SQLQuery query,
                                                 TupleToWebRowSetCharArrays transform,
                                                 CharArraysResize resize,
                                                 DeliverToRequestStatus delivery)
    {
        PipelineWorkflow tempFlow = new PipelineWorkflow();
        transform.connectDataInput(query.getDataOutput());
        resize.connectDataInput(transform.getResultOutput());
        delivery.connectInput(resize.getOutput());
        tempFlow.add(query);
        tempFlow.add(transform);
        tempFlow.add(resize);
        tempFlow.add(delivery);
        return tempFlow;
    }

    /**
     * Executes a pipelineworkflow in a synchronous mode and returns
     * a requestResource
     * 
     * @param pipe
     * @return RequestResource
     * @throws ServerCommsException
     * @throws ServerException
     * @throws ResourceUnknownException
     * @throws ClientException
     * @throws RequestException
     * @throws ClientToolkitException
     */
    public RequestResource executePipeline(PipelineWorkflow pipe) 
        throws ServerCommsException, 
               ServerException, 
               ResourceUnknownException, 
               ClientException, 
               RequestException, 
               ClientToolkitException
    {
        return mDRER.execute(pipe, RequestExecutionType.SYNCHRONOUS);
    }
   
    /**
     * Prints a table format to standard output using the data from the
     * transform activity passed in as parameter.
     * 
     * @param tupleToWebRowSet
     * @throws SQLException
     * @throws DataStreamErrorException
     * @throws UnexpectedDataValueException
     * @throws WebRowSetResultSetParseException
     * @throws DataSourceUsageException
     */
    public void printTable(TupleToWebRowSetCharArrays tupleToWebRowSet)
       throws SQLException,
              DataStreamErrorException,
              UnexpectedDataValueException,
              WebRowSetResultSetParseException,
              DataSourceUsageException
    {
        if (tupleToWebRowSet.hasNextResult())
        {
            // Get ResultSet.
            ResultSet rs = tupleToWebRowSet.nextResultAsResultSet();
            // Get ResultSet meta data.
            ResultSetMetaData md = rs.getMetaData();
            // Get column names and initial column widths.
            int numColumns = md.getColumnCount();
            String[] columns = new String[numColumns];
            int[] widths = new int[numColumns];
            for (int i = 0; i < numColumns; i++)
            {
                String column = md.getColumnLabel(i + 1);
                columns[i] = column;
                widths[i] = column.length();
            }
            // Get ResultSet rows and update column widths also.
            Vector rows = new Vector();
            while (rs.next()) 
            {
                String[] fields = new String[numColumns];
                for (int i = 0; i < numColumns; i++)
                {
                    fields[i] = rs.getString(i + 1);
                    widths[i] = Math.max(widths[i], fields[i].length());
                }
                rows.add(fields);
            }
            rs.close();
            // Print column names.
            String tableHeading = "| ";
            for (int i = 0; i < numColumns; i++)
            {
                tableHeading += (pad(columns[i], widths[i]) + " | ");
            }
            System.out.println(tableHeading);
            // Print rows.
            for (int j = 0; j < rows.size(); j++)
            {
                String[] row = (String[])rows.get(j);
                String rowString = "| ";
                for (int i = 0; i < numColumns; i++)
                {
                    rowString += (pad(row[i], widths[i]) + " | ");
                }
                System.out.println(rowString);
            }
        }
    } 

    /**
     * Pads out a string to the width given as parameter
     * @param base
     * @param width
     * @return String
     */
    private static String pad(String base, int width)
    {
        StringBuffer baseBuffer = new StringBuffer(base);
        int padLength = width - base.length();
        for (int i = 0; i < padLength; i++)
        {
            baseBuffer.append(" ");
        }
        return baseBuffer.toString();
    }

    /**
     * Allow TupleToWebRowSetCharArrays access to the request status.
     * 
     * @param transform
     * @param delivery
     */
    private static void setRequestStatus(TupleToWebRowSetCharArrays
                                         tupleToWebRowSet,
                                         DeliverToRequestStatus
                                         deliverToRequestStatus)
    {
        tupleToWebRowSet.getResultOutput().setDeliverToRequestStatusActivity(deliverToRequestStatus);
    }

    public static void main(String[] args) throws Exception
    {
        // Setup
        String serverURL = "http://localhost:8080/dai/services/";
        ResourceID drerID = new ResourceID("DataRequestExecutionResource");
        SQLClient iSQLClient = new SQLClient();
        ResourceID dataResourceID = new ResourceID("MySQLDataResource");
        String sqlStr = "SELECT * FROM littleblackbook WHERE id<3;";

        // Server
        iSQLClient.setupServer(serverURL);
        iSQLClient.setupDRER(drerID);
       
        // Activities Creation
        SQLQuery sqlQ = iSQLClient.makeSQLQuery(dataResourceID, sqlStr);
        DeliverToRequestStatus dtrs = iSQLClient.makeDeliverToRequestStatus();
        TupleToWebRowSetCharArrays ttwrsca =   iSQLClient.makeTupleToWebRowSetCharArrays();
        CharArraysResize car =   iSQLClient.makeCharArraysResize();

        // Workflow and Execution
        PipelineWorkflow pwf = iSQLClient.makePipelineWorkflow(sqlQ, ttwrsca, car, dtrs);
        RequestResource rr = iSQLClient.executePipeline(pwf);
        RequestStatus rs = rr.getRequestStatus();

        // outputs
        System.out.println(rs.getExecutionStatus());
        iSQLClient.setRequestStatus(ttwrsca, dtrs);
        iSQLClient.printTable(ttwrsca);
    }
}

The last line of the main method will print the results table to standard output.

That is the outline of constructing a basic SQL client to OGSA-DAI.