Chapter 110. Client toolkit activity proxies and exposing data for clients

110.1. Simple types
110.2. Lists of simple types
110.3. Lists of character arrays
110.4. Lists of byte arrays
110.5. Parsing the data
110.6. Activities with multiple outputs of the same name
110.7. How does a client proxy activity get access to the data in a request status?

If an activity has its output connected the DeliverToRequestStatus activity then any data it outputs will be returned from the OGSA-DAI server to the client in the request status.

The client toolkit parses the request status and populates the ActivityOutput objects of such activities that were connected to DeliverToRequestStatus with their data.

This data is then available via the getDataValueIterator method of ActivityOutput method. This returns a sub-class of:

uk.org.ogsadai.client.toolkit.DataIterator

The data can then be pulled from this object. This is not particularly useful for client developers in itself so it is very useful to provide additional methods that allow the information to be exposed in a more useful and usable way.

So, for each output we define two methods. The first allows the client to see if there is still data available:

public boolean hasNextX()
{
    mXOutput.getDataValueIterator().hasNext();
}

The second allows them to get this data. This method has name nextXAsY() and its definition depends on how the data is to be exposed to the client developer. For example, for a string we may have:

public String nextXAsString()
    throws DataStreamErrorException, 
           UnexpectedDataValueException, 
           DataSourceUsageException
{
    mXOutput.getDataValueIterator().nextAsString();
}

These methods need to declare that they throw three exceptions:

// There is an error reading from a data source.
uk.org.ogsadai.client.toolkit.exception.DataSourceUsageException
// There is an error in the data.
uk.org.ogsadai.client.toolkit.exception.DataStreamErrorException
// There is an unexpected value in the data.
uk.org.ogsadai.client.toolkit.exception.UnexpectedDataValueException
[Note]Note
Only activities whose outputs can be connected to DeliverToRequestStatus or output data that is in a form that can be passed from the OGSA-DAI server back to the client need to have hasNextX() and nextXAsY() methods defined.

110.1. Simple types

For basic Java types we can define methods such as the following:

public boolean nextX()
{
    return mXOutput.getDataValueIterator().nextAsBoolean() 
}

public String nextX()
{
    mXOutput.getDataValueIterator().nextAsString() 
}

public Date nextX()
{
    mXOutput.getDataValueIterator().nextAsDate() 
}

public double nextX()
{
    mXOutput.getDataValueIterator().nextAsDouble() 
}

public float nextX()
{
    mXOutput.getDataValueIterator().nextAsFloat() 
}

public int nextX()
{
    mXOutput.getDataValueIterator().nextAsInt() 
}

public long nextX()
{
    mXOutput.getDataValueIterator().nextAsLong() 
}

110.2. Lists of simple types

If the output is an OGSA-DAI list of objects that are, or can be mapped to, simple types then we provide a two methods. One returns an object of type:

uk.org.ogsadai.client.toolkit.DataListIterator

For example:

public DataIterator nextX()
    throws DataStreamErrorException, 
           UnexpectedDataValueException, 
           DataSourceUsageException
{
    return new DataListIterator(mXOutput.getDataValueIterator(), String.class);
}

Note how the expected type of the data is provided as an argument to the DataListIterator constructor.

The other method returns an array of the appropriate type. For example:

public String[] nextXAsArray()
    throws DataStreamErrorException, 
           UnexpectedDataValueException, 
           DataSourceUsageException
{
        if (! hasNextX())
        {
            return null;
        }
        DataIterator iterator = nextData();
        List stringList = new ArrayList();
        while(iterator.hasNext())
        {
            stringList.add((String)iterator.next());
        }
        int listSize = stringList.size();
        String[] outputArray = new String[listSize];
        for(int i = 0; i < listSize; i++)
        {
            outputArray[i]  = (String)stringList.get(i);
        }
        return outputArray;
}

110.3. Lists of character arrays

If the output is a list of char[] then the nextX method should returns a java.io.Reader. For example:

public Reader nextX()
{
    return new DataValueReader(mXOutput.getDataValueIterator(),1);
}

110.4. Lists of byte arrays

If the output is a list of byte[] then the nextX method returns a java.io.InputStream. For example:

public InputStream nextX()
{
    return new DataValueInputStream(mXOutput.getDataValueIterator(),1);
}

110.5. Parsing the data

Of course and apart from the foregoing advice, when giving the client developer access to the data you can parse the data and so provide it in a form that might be more useful to them. One example of this is the client toolkit activity proxy:

uk.org.ogsadai.client.toolkit.activities.transform.TupleToWebRowSetCharArrays

The corresponding server-side activity has an output - result - that outputs OGSA-DAI lists of char[]. Together these character arrays form an XML WebRowSet document representing the results of an SQL query. So our client toolkit proxy provides a method:

ResultSet getResultAsResultSet()

This allows users to obtain the data in a more usable way.

110.6. Activities with multiple outputs of the same name

For activities that support multiple outputs of the same name we need to allow the client developer to specify the index of the output whose data they want to get.This just requires our hasNextX and nextX methods to take the index of the output also. For example:

public boolean hasNextX(int index) 
    throws DataStreamErrorException, 
           UnexpectedDataValueException, 
	   DataSourceUsageException,
           ArrayIndexOutOfBounds
{
    return mXOutput.getDataValueIterator(index).hasNext();  
}
public int nextX(int index) 
    throws DataStreamErrorException,
           UnexpectedDataValueException,
           DataSourceUsageException,
           ArrayIndexOutOfBounds
{
    return mXOutput.getDataValueIterator(index).nextAsInt();
}

110.7. How does a client proxy activity get access to the data in a request status?

If an activity has its output connected the DeliverToRequestStatus activity then any data it outputs will be returned from the OGSA-DAI server to the client in the request status.

The client toolkit parses the request status and populates the ActivityOutput objects of such activities that were connected to DeliverToRequestStatus with their data.

This data is then available via the getDataValueIterator method of ActivityOutput method.

If the activity (activity A, say) is not connected directly to DeliverToRequestStatus, but there is another activity (activity B, say) in between, then if activity B does not alter the data in a way that prevents activity A from parsing the output then activity A can still obtain the data that was written to DeliverToRequestStatus, even though it was not directly connected.

However, to support this your activity must allow a client developer to tell the client proxy for A that it's data can be picked up from the request status, as it won't know this by default (it'll assume that it has no data since it was not connected to DeliverToRequestStatus itself). For example:

ActivityA activityA = new ActivityA();
ActivityB activityB = new ActivityB();
DeliverToRequestStatus deliverToRequestStatus = 
    new DeliverToRequestStatus();
activityB.connectInput(activityA.getOutput());
deliverToRequestStatus.connectInput(activityB.getOutput);

...

// Tell activity A's output that it can get its data from the request
// status.
activityA.getOutput().setResultActivity(
    deliverToRequestStatus);

This is primarily a usability and design issue. For example, a common SQL query workflow is SQLQuery => TupleToWebRowSetCharArrays => ByteArraysResize => DeliverToRequestStatus. ByteArraysResize is purely used to optimise the size of byte arrays to minimize server-client communications overheads. So it makes sense to allow client developers to get the relational data via the TupleToWebRowSetCharArrays proxy rather than the ByteArraysResize proxy. And so we often have:

SQLQuery query = new SQLQuery();
query.setResourceID(mDataResourceID);
query.addExpression(mSQLQuery);
        
TupleToWebRowSetCharArrays tuple = new TupleToWebRowSetCharArrays();
tuple.connectDataInput(query.getDataOutput());
        
CharArraysResize resize = new CharArraysResize();
resize.addSizeInChars(5000);
resize.connectDataInput(tuple.getResultOutput());

DeliverToRequestStatus deliverToRequestStatus = new DeliverToRequestStatus();
deliverToRequestStatus.connectInput(resize.getResultOutput());
        
tuple.getResultOutput().setResultActivity(deliverToRequestStatus);>