Your client toolkit proxy needs to set up objects that represent the activity's inputs and outputs. Your activity should define a member variable for each input and each output. These should be named as follows:
mXInput mXOutput
Where X is the name of the input. If an input is
called Input then the name
mInput can be used and likewise,
mOutput if an output is called
Output.
The inputs and outputs should be of type:
uk.org.ogsadai.client.toolkit.activity.ActivityInput uk.org.ogsadai.client.toolkit.activity.ActivityOutput
We provide two implementations of these interfaces each of which take, in their constructor the name of the activity inputs and outputs as they are recorded in the server-side activity implementation. The classes are:
uk.org.ogsadai.client.toolkit.activity.SimpleActivityOutput uk.org.ogsadai.client.toolkit.activity.SimpleActivityOutput
So for example, our server-side activity
uk.org.ogsadai.activity.sql.SQLQueryActivity
has an input called expression and
an output called data. So for the
client proxy we define:
private final ActivityInput mExpressionInput =
new SimpleActivityInput("expression");
private final ActivityOutput mDataOutput =
new SimpleActivityOutput("data");
Note if an input is optional then the following can be used:
private final ActivityInput mMyExampleInput =
new SimpleActivityInput("myExample", SimpleActivityOutput.OPTIONAL);
If an activity supports multiple inputs with the same name (activity input with multiple occurences) or multiple outputs with the same name (activity output with multiple occurrences) then we need a way for the client developer to specify how many of these they require.
For such activity inputs we define a method,
setNumberOfXInputs,
where X is the name of the input. For example:
public void setNumberOfXInputs(int count)
{
mXInput.setNumberOfInputs(count);
}
For such activity outputs we define a method,
setNumberOfXOutputs,
where X is the name of the output. For example:
public void setNumberOfXOutputs(int count)
{
mXOutput.setNumberOfOutputs(count);
}
What happens, behind the scenes, is that the client toolkit views each
ActivityInput as a collection of
SingleActivityInput objects. For most
activity inputs there will be just one
SingleActivityInput object in the
collection. For multiple inputs of the same there will be 1 to N. And,
similarly for ActivityOutput objects
there are associated
SingleActivityOutput objects.
Workflows are formed when activities are connected to other activities. You therefore need to provide methods that allow client developers to connect activity proxies together and so define their workflows.
For each non-multiple input that an activity has the client-side activity class should provide a method called:
connectXInput
where X is the name of the input. If the input is
called Input then the name
connectInput can be used.
These methods typically have the following definition:
public void connectXInput(ActivityOutput output);
{
mXInput.connect(output);
}
where mXInput is the
ActivityInput object for this input.
And, of course, we need a way to get the outputs of an activity to connect them to the inputs. For each output an activity has we need to provide a method called:
getXOutput
where X is the name of the output. If the output is
called Output then the name
getOutput can be used.
These methods typically have the following definition:
public SingleActivityOutput getXOutput()
{
return mXOutput.getSingleActivityOutputs()[0];
}
where mXOutput is the
ActivityOutput object for this
output.
For activities that support multiple inputs of the same name we need
to allow the client developer to specify the index of the input they
want to connect to. Our connectXInput
methods are therefore defined as:
public void connectXInput(int index, SingleActivityOutput output)
{
mXInput.connect(index, output);
}
Likewise for activities that support multiple outputs of the same name
we need to allow the client developer to specify the index of the
output they want to connect to. Our
getXOutput methods are therefore
defined as:
public SingleActivityOutput getXOutput(int count)
{
return mXOutput.getSingleActivityOutputs()[count];
}
where mXOutput is the
ActivityOutput object for this
output.
It is useful to allow clients to provide the input values for activities directly rather than everything coming from the output from another activity. The most common example of this is allowing the client to provide a query expression for an SQL query or XPath activity.
If the server-side activity expects values that are primitive types e.g.:
java.lang.String char[] byte[] java.lang.Boolean java.lang.Float java.lang.Integer java.lang.Long java.lang.Double java.lang.Date java.lang.Calendar
Then these can be provided by the client directly. These are provided
via methods called
addXInput
where X is the name of the input. If the input is
called Input then the name
addInput can be used.
How these methods are defined depends upon the primitive types to be supported.
To allow the client to provide input values of the following types
java.lang.String java.lang.Boolean java.lang.Float java.lang.Integer java.lang.Long java.lang.Double java.lang.Date java.lang.Calendar
we wrap the values in associated classes from the package:
uk.org.ogsadai.data
Here are examples of add methods for
each type:
public void addX(boolean x)
{
mXInput.add(new BooleanData(x));
}
public void addX(Date x)
{
mXInput.add(new DateData(x));
}
public void addX(double x)
{
mXInput.add(new DoubleData(x));
}
public void addX(float x)
{
mXInput.add(new FloatData(x));
}
public void addX(int x)
{
mXInput.add(new IntegerData(x));
}
public void addX(long x)
{
mXInput.add(new LongData(x));
}
public void addX(String x)
{
mXInput.add(new StringData(x));
}
If the input is an OGSA-DAI list of objects of objects then we provide
two methods that take the list of objects and add these, as well as
objects representing OGSA-DAI list begin and end markers to the
ActivityInput objects. The list
markers are denoted using the following:
uk.org.ogsadai.data.ListBegin uk.org.ogsadai.data.ListEnd
The first method takes a
java.util.Iterator containing objects
of the required type. It adds the list begin marker, then traverses
the iterator wrapping the values in the appropriate wrapper as already
described, then adds the list end marker. So, for example, for an
OGSA-DAI list of java.lang.String
we'd have:
public void addX(Iterator data)
{
mXInput.add(ListBegin.VALUE);
while (data.hasNext())
{
Object value = data.next();
if (! (value instanceof String))
{
throw new IllegalArgumentException("A non-String was found!");
}
mXInput.add(new StringData((String)data.next));
}
mDataInput.add(ListEnd.VALUE);
}
The second method takes an array of objects of the required type and operates in much the same way. For example:
public void addX(String[] data)
{
mXInput.add(ListBegin.VALUE);
for (int i = 0; i < data.length; i++)
{
mDataInput.add(new StringData(data[i]));
}
mDataInput.add(ListEnd.VALUE);
}
If the input is a list of char[] then
we need to provide an
addX method that
takes a java.io.Reader as input and
reads chunks of char[] and adds them
to the input. We provide a class that does the bulk of this work:
uk.org.ogsadai.client.toolkit.Utilities;
So the method simply becomes:
public void addX(Reader reader) throws IOException
{
mXInput.add(ListBegin.VALUE);
Utilities.readCharData(mXInput, reader, 2048);
mXInput.add(ListEnd.VALUE);
}
The final argument to readCharData is
just a suitable size for each character array in the list.
If the input is a list of byte[] then
we need to provide an
addX method that
takes an InputStream as input and
reads chunks of byte[] and adds them
to the input. We provide a class that does the bulk of this work:
uk.org.ogsadai.client.toolkit.Utilities;
So the method simply becomes:
public void addX(InputStream stream) throws IOException
{
mXInput.add(ListBegin.VALUE);
Utilities.readBinaryData(mXInput, stream, 2048);
mXInput.add(ListEnd.VALUE);
}
The final argument to readBinary is
just a suitable size for each byte array in the list.
For activities that support multiple inputs of the same name we need
to allow the client developer to specify the index of the input they
want to connect to. This just requires our
addXInput methods to take the index of
the input and this to be passed with the input value itself to the
ActivityInput. For example:
public void addXInput(int index, String x)
{
mXInput.add(index, new StringData(x));
}
The base client toolkit activity classes will do most of the client toolkit activity input and output validation before sending the request to the server. If however you need to do validation across the inputs then there is a method that can be implemented within which this can be done. For example, an activity might be defined to have one of inputs A or B but never both. Validating such conditions client-side means that the round-trip time of submitting the request to the server only to have it fail is avoided.
To validate activity inputs and outputs requires overriding the method:
protected void validateIOState() throws ActivityIOIllegalStateException;
Very few activities need this type of validation so typically the method is empty.