Grassroots Infrastructure
The Grassroots Infrastructure is a suite of computing tools to help users and developers use scientific data infrastructure that can easily be interconnected.
Handlers

Data that a user wishes to access can be potentially be stored on a variety of different systems e.g. local filesystems, http(s) locations, cloud-based storage systems, etc. Regardless of the storage mechanism, the actual operations that may be needed are the same e.g. read from a file, write to a file, create a file, etc. So the Grassroots system abstracts out these actions to an API, the Handlers API and has separate components for each of the storage mechanisms.
This allows developers to code to the Handlers API for doing file access and not need to worry where the underlying files are actually stored.

The currently available Handlers are:

  • File Handler for any mounted filesystem.
  • iRODS Handler for data stored within an iRODS instance.
  • Dropbox Handler for data stored on a Dropbox drive. (partial support)
  • HTTP Handler for data available at HTTP(S) locations.

Usage

Handlers work upon data files represented by the Resource datatype. For accessing via a Handler, the fields of interest in this datatype are re_protocol_s and re_value_s.

typedef struct Resource
{
char *re_protocol_s;
char *re_value_s;
...
} Resource;

re_protocol_s is the value that declares the underlying storage mechanism e.g. http, file, etc. and re_value_s is the path to the file/directory of interest. For example, the uri http://earlham.ac.uk/data/wheat_assembly.fasta would have

re_protcol_s = "http"
re_value_s = "earlham.ac.uk/data/wheat_assembly.fasta"

and the file /opt/data/research.pdf would have

re_protcol_s = "file"
re_value_s = "/opt/data/research.pdf"

Within the handler_utils.h file, there is a function GetResourceHandler that determines the correct Handler for as given Resource

Handler *GetResourceHandler (const Resource *resource_p, const UserDetails *user_p);

Once you have this Handler you can then perform common I/O tasks with routines analogous to those provided by the standard C library such as fread, fwrite, fseek, etc. Further details are provided by the Handler API documentation.

Examples

We will now give some examples that show some standard C file I/O code and the equivalent using Handlers. The first is to open a file, seek 20 bytes in and read 16 characters into a buffer and return it. The first code section uses standard C library calls, and the code below use the Grassroots API equivalents.

char *ReadFileChunk (const char * const filename_s)
{
FILE *in_f = fopen (filename_s, "rb");
if (in_f)
{
/* Scroll 20 bytes into the file */
if (fseek (in_f, 20, SEEK_SET) == 0)
{
/*
* We are going to try and read in 16 bytes so allocate
* the memory needed including the extra byte for the
* terminating \0
*/
const size_t buffer_size = 16;
char *buffer_s = (char *) malloc ((buffer_size + 1) * sizeof (char));
if (buffer_s)
{
/* Read in the data */
if (fread (buffer_s, 1, buffer_size, in_f) == buffer_size)
{
/* Add the terminating \0 */
* (buffer_s + buffer_size) = '\0';
return buffer_s;
} /* if (fread (buffer_s, 1, buffer_size, in_f) == buffer_size) */
else
{
fprintf (stderr, "Failed to read value from %s", filename_s);
}
free (buffer_s);
} /* if (buffer_s) */
else
{
fprintf (stderr, "Failed to allocate memory for buffer when reading %s", filename_s);
}
} /* if (fseek (in_f, 20, SEEK_SET) == 0) */
else
{
fprintf (stderr, "Failed to seek in file %s", filename_s);
}
if (fclose (in_f) != 0)
{
fprintf (stderr, "Failed to close file %s", filename_s);
} /* if (fclose (in_f) != 0) */
} /* if (in_f) */
else
{
fprintf (stderr, "Failed to open %s", filename_s);
}
return NULL;
}

Below is the equivalent code using the Handlers API

char *ReadFileChunk (const char * const filename_s)
{
/* The value that we will return */
char *result_s = NULL;
/*
* Allocate the Resource.
* We know that it's a file so we use the protocol for a file,
* PROTOCOL_FILE_S, and we do not need a title for the Resource
* so we set it to NULL.
*/
Resource res_p = AllocateResource (PROTOCOL_FILE_S, filename_s, NULL);
if (res_p)
{
/*
* Now that we have the Resource, let the Grassroots system find
* the appropriate Handler.
*/
Handler *handler_p = GetResourceHandler (res_p, NULL);
if (handler_p)
{
/* Scroll 20 bytes into the file */
if (SeekHandler (handler_p, 20, SEEK_SET))
{
/*
* We are going to try and read in 16 bytes so allocate
* the memory needed including the extra byte for the
* terminating \0
*/
const size_t buffer_size = 16;
char *buffer_s = (char *) AllocMemory ((buffer_size + 1) * sizeof (char));
if (buffer_s)
{
/* Read in the data */
if (ReadFromHandler (handler_p, buffer_s, buffer_size) == buffer_size)
{
/* Add the terminating \0 */
* (buffer_s + buffer_size) = '\0';
/* Store the value for returning */
result_s = buffer_s;
} /* if (fread (buffer_s, 1, buffer_size, in_f) == buffer_size) */
else
{
PrintErrors (STM_LEVEL_SEVERE, __FILE__, __LINE__, "Failed to read value from %s", filename_s);
/* We failed to read in the value so we can release the buffer memory */
FreeMemory (buffer_s);
}
} /* if (buffer_s) */
else
{
PrintErrors (STM_LEVEL_SEVERE, __FILE__, __LINE__, "Failed to allocate memory for buffer when reading %s", filename_s);
}
} /* if (SeekHandler (handler_p, 20, SEEK_SET)) */
else
{
PrintErrors (STM_LEVEL_SEVERE, __FILE__, __LINE__, "Failed to seek in %s", filename_s);
}
CloseHandler (handler_p);
} /* if (handler_p) */
else
{
PrintErrors (STM_LEVEL_SEVERE, __FILE__, __LINE__, "Failed to find a Handler for %s", filename_s);
}
FreeResource (res_p);
} /* if (res_p) */
else
{
PrintErrors (STM_LEVEL_SEVERE, __FILE__, __LINE__, "Failed to allocate Resource for %s", filename_s);
}
return result_s;
}

The complete list of Handler functions is shown below and clicking upon the function names will take you to the appropriate API documentation.

size_t ReadFromHandler (struct Handler *handler_p, void *buffer_p, const size_t length)
size_t WriteToHandler (struct Handler *handler_p, const void *buffer_p, const size_t length)
bool SeekHandler (struct Handler *handler_p, size_t offset, int whence)
PROTOCOL_FILE_S
const char * PROTOCOL_FILE_S
The protocol for a data object for a mounted file.
Definition: data_resource.h:124
FreeMemory
#define FreeMemory(x)
Free the memory pointed to by x.
Definition: memory_allocations.h:120
Handler::GetHandlerStatus
HandlerStatus GetHandlerStatus(struct Handler *handler_p)
Get the HandlerStatus of a Handler.
HandlerStatus
HandlerStatus
An enumeration of the possible status values of a stream being used by a Handler.
Definition: handler.h:42
Handler::SeekHandler
bool SeekHandler(struct Handler *handler_p, size_t offset, int whence)
Move a Handler to a new position in its stream.
STM_LEVEL_SEVERE
#define STM_LEVEL_SEVERE
A severe, probably terminal error.
Definition: streams.h:49
FileInformation
A datatype to hold a collection of metadata about a file.
Definition: filesystem_utils.h:39
GetResourceHandler
Handler * GetResourceHandler(const DataResource *resource_p, struct GrassrootsServer *server_p, const User *user_p)
Get the appropriate Handler for a given Resource.
Handler::CloseHandler
bool CloseHandler(struct Handler *handler_p)
Close a Handler.
Handler::CalculateFileInformationFromHandler
bool CalculateFileInformationFromHandler(struct Handler *handler_p, FileInformation *info_p)
Calculate the FileInformation for the current Resource in use by a Handler.
Handler::WriteToHandler
size_t WriteToHandler(struct Handler *handler_p, const void *buffer_p, const size_t length)
Write data from a buffer into a Handler.
OutputStream::PrintErrors
int PrintErrors(const uint32 level, const char *const filename_s, const int line_number, const char *message,...)
Print to the error OutputStream.
Handler
A Handler is a datatype for accessing data.
Definition: handler.h:72
Handler::ReadFromHandler
size_t ReadFromHandler(struct Handler *handler_p, void *buffer_p, const size_t length)
Read data from a Handler into a buffer.
AllocMemory
#define AllocMemory(x)
Allocate the memory for, x, the given number of bytes.
Definition: memory_allocations.h:104