How to Write a Data Resource Accessor

Data resource accessors allow activities to access data resources. More generally data resource accessors hold state and provide methods that can be accessed by multiple activities and multiple instances of those activities. For example, the default relational data resource accessor JDBCDataResourceAccessor holds state specifying how to connect to a relational database and provides methods to obtain and release connections to this database.

Data resources accessors are an extensibility point of OGSA-DAI. This is particularly useful if you need to provide access to unusual or domain-specific data resources or for developing data federation or data integration functionality.

There now follows a walkthrough explaining the development of a data resource accessor. The How to Compile and Run the Examples section of the Client Toolkit Tutorial contains information on how to compile all of our examples.

Example Scenario

Imagine a scenario where you wished to develop OGSA-DAI activities to access data from a temperature sensor. In this case the temperature sensor is a new type of data resource for which OGSA-DAI does not provide a data resource accessor.

Development of data resource accessor

There are four stages involved in writing a data resource accessor. These are:

  1. Write the interface used by activities
  2. Define the configuration files
  3. Define the resource properties
  4. Write the data resource accessor class

1. Write the interface used by activities

The first task when developing a data resource accessor is to define the interface through which activities will access the data resource. For our example temperature data resource this is very simple. Activities can only obtain the current temperature. Thus we define the following interface:

package uk.org.ogsadai.examples.dataResourceAccessor;
  
interface TemperatureProvider 
{
    // Returns the current temperature in degrees Celsius
    float getTemperature();
}

2. Define the configuration files

Most data resources accessor will need to obtain some initial configuration from configuration files. You need to define the name and format of these files. For a temperature sensor the configuration would have to specify all the parameters needed to access the sensor within a Java program.

We do not actually have a temperature sensor so we will simulate one by calculating a random number within a predefined range. Our single configuration file will specify this range. We decide that the configuration file will be called range.txt and will contain two floating point numbers, one on each of the first two lines, that define the range. For example:

-20.0
50.0

3. Define the resource properties

Data service resources may have any number of resource properties that are visible to clients. Properties of the data service resource are unique to that data service resource and there is only ever a single instance of each property in the data service resource.

Properties can be of two types:

In this example we will create two properties that together define the range of the temperature sensor. We will implement both as static properties.

Properties are identified by XML qualified names so we define the qualified names of our two properties to be:

 {https://ogsadai.org.uk/demoNamespace}minValue
 {https://ogsadai.org.uk/demoNamespace}maxValue

The minValue property will give the minimum value of the sensor range and the maxValue property will give the maximum value of the sensor range.

4. Write the data resource accessor class

We must now write a data resource accessor class. All data resource accessors must implement the DataResourceAccessor interface. If the data resource accessor is to read (and possibly save) its configuration using files then it must also implement the PersistInFiles interface. Finally the class must also implement the TemperatureProvider interface defined above so that activities can use these methods. We therefore have to complete the following skeleton class:

package uk.org.ogsadai.examples.dataResourceAccessor;

import java.io.File;
import java.io.BufferedReader;
import java.io.FileReader;

import java.util.Random;

import javax.xml.namespace.QName;

import uk.org.ogsadai.common.properties.Property;
import uk.org.ogsadai.dataresource.DataResourceAccessor;
import uk.org.ogsadai.dataresource.DataResourceAccessorConfigException;
import uk.org.ogsadai.dataresource.DataResourceAccessorMetaDataException;
import uk.org.ogsadai.dataresource.DataResourceAccessorSetupException;
import uk.org.ogsadai.dataresource.PersistInFiles;
import uk.org.ogsadai.exception.DAIException;
import uk.org.ogsadai.examples.dataResourceAccessor.TemperatureProvider;

import uk.org.ogsadai.common.properties.StaticProperty;

import javax.xml.namespace.QName;

public class TemperatureDataResourceAccessor
    implements DataResourceAccessor, PersistInFiles, TemperatureProvider
{
    // Constructor
    public TemperatureDataResourceAccessor()
    {
    }

    public void setResourceName(String resourceName)
    {
        // Add code here
    }

    public void restoreFromConfig(File directory) 
        throws 
            DataResourceAccessorConfigException, 
            DataResourceAccessorMetaDataException, 
            DataResourceAccessorSetupException, 
            IllegalArgumentException
    {
        // Add code here
    }

    public Property[] getProperties()
    {
        // Add code here
    }

    synchronized public float getTemperature()
    {
        // Add code here
    }
}

The first method to implement is the setResourceName method. This method is called after the data resource accessor has been constructed but before any other methods of the class are called. The method simply lets the data resource accessor know the name of the associated data service resource. It can be very useful to include this name in any error messages generated by the data resource accessor. To store the name we add the following member variable:

private String dataServiceResourceName;

and the setResourceName method is implemented as:

public void setResourceName(String resourceName) 
{
    dataServiceResourceName = resourceName;
}

The next method to implement is the restoreFromConfig method which is defined in the PersistInFiles interface. If implemented this method is called after the setResourceName method but before any other methods of the class are called. It is in this method that the data resource accessor should read its configuration. The directory parameter specifies the directory in which this data resource accessor's configuration files are kept.

The restoreFromConfig method should therefore read the temperature range from the range.txt configuration file and store it in member variables.

We add the following member variables:

private float minValue;
private float maxValue;

and the restoreFromConfig method is implemented as:

public void restoreFromConfig(File directory) 
    throws 
        DataResourceAccessorConfigException, 
        DataResourceAccessorMetaDataException, 
        DataResourceAccessorSetupException, 
        IllegalArgumentException
{
    // Open a reader to the configuration file
    File rangeFile = new File(directory,"range.txt");
    BufferedReader in = 
        new BufferedReader(new FileReader(rangeFile));
        
    // Read the min and max values from the config file
    String line;
    line = in.readLine();
    minValue = Float.parseFloat(line);
    line = in.readLine();
    maxValue = Float.parseFloat(line);
}

The getProperties method has to return the properties so it makes sense to also construct these in the restoreFromConfig method. We add the following member variables:

private StaticProperty minValueProp;
private StaticProperty maxValueProp;

Creating the properties in the restoreFromConfig method is fairly easy:

// Create the properties
QName minValueQName = 
    new QName("https://ogsadai.org.uk/demoNamespace","minValue");
minValueProp = new StaticProperty();
minValueProp.setName(minValueQName);
minValueProp.setValue(""+minValue);

QName maxValueQName = 
    new QName("https://ogsadai.org.uk/demoNamespace","maxValue");
maxValueProp = new StaticProperty();
maxValueProp.setName(maxValueQName);
maxValueProp.setValue(""+maxValue);

The setValue method of the StaticProperty interface allows the resource property value to be set to either a String or an XML Element. Here we are simply setting it to a String containing the minimum or maximum range value.

We can now write the getProperties() method:

public Property[] getProperties()
{
    return new Property[] { minValueProp, maxValueProp };
}

In the getTemperature we will just randomly simulate a temperature value so we need a new member variable:

private Random rand = new Random();

The method is very simple because we are only simulating the sensor. Typically the method would obtain the current temperature from the sensor.

synchronized public float getTemperature()
{
    return rand.nextFloat() * (maxValue-minValue) + minValue;
}

Note that this method is synchronized. This is very important because multiple activities in different sessions may well be calling this method at the same time. Making the whole method synchronized may not always be best for performance. When developing your own data resource accessor you should analyse the concurrency issues and implement your synchronization at a suitable level of granularity.

The complete code including error handling is available at examples/src/uk/org/ogsadai/examples/dataResourceAccessor/TemperatureDataResourceAccessor.java .

Development of activities to use the data resource accessor

Once you have written your data resource accessor you will need some activities that interact with it. If your data resource accessor implements an interface that is already used by existing activities then these activities should also work with your new data resource accessor. Otherwise you will wish to write some new activities.

The How To Write a Basic Activity tutorial describes how to write an activity. To interact with your new data resource accessor you will need to get the object from the ActivityContext and cast it to your TemperatureProvider interface. For example:

TemperatureProvider tempProvider = 
    (TemperatureProvider) mContext.getDataResourceAccessor();

Deployment of the data resource and activities

This section describes how to deploy a data service resource that uses your new data resource accessor. We assume the following:

To deploy the data service resource do the following:

  1. From the OGSA-DAI binary distribution directory run the following command:
    ant deployPlugInResource
      -Ddai.container=/path/to/Web/services/container
      -Ddai.resource.id=RESOURCE-ID 
      -Ddai.resource.impl=DATA-RESOURCE-ACCESSOR-CLASS
    

  2. The above process will have created a directory within the container named after your data service resource (e.g. myTemperatureSensor).

    Locate this directory and copy your range.txt configuration file to it.

  3. Edit the activityConfig.xml file in the directory located in step 2 so that it contains the details for any activity you have written to work with this data service resource.

  4. Use the ANT target exposeResource to expose the data service resource via the data service as described in deployment pages.

  5. Copy your jar file containing the data resource accessor and activity code to the container's lib directory.

  6. Restart your container.

Extensions to this scenario

If you wish to extend this scenario then you could write another activity that alters the configuration of this data resource accessor. For example you could have an activity that alters the range of the temperatures that the sensor can return. You may wish to do this by writing another interface called RangeSetter that has methods to set the minimum and maximum range values. The TemperatureDataResourceAccessor would implement this interface and the activities will use this interface to interact with the data resource accessor.

This extensions show how activities can be used to dynamically alter the configuration of a data resource accessor. This is the approach OGSA-DAI intends to move towards in future releases.

Further scenarios

The data resource accessor extensibility point is also very useful for implementing data federation or data integration solutions. In these situations the configuration may specify of set of other OGSA-DAI services over which a query is to be executed. Activities can we written to distribute the query to these other services in some way and combines the results.