Hyrax: Server Side Functions: Difference between revisions

From OPeNDAP Documentation
⧼opendap2-jumptonavigation⧽
Line 1: Line 1:
== Overview ==
== Overview ==


Before you embark upon writing a server side function for DAP2 you should read through this [[DAP2: Constraint Expressions | overview of DAP2 constraint expressions and their use of functions]]
Before you embark upon writing a server side function for DAP2 you should read through this [[DAP2: Constraint Expressions | overview of DAP2 constraint expressions and their use of functions]].
 
<font color="red">Here's another source for information on Server Side FUnctions and Constraint Expressions: http://docs.opendap.org/index.php/UserGuideOPeNDAPMessages#Constraint_Expressions
<br>
You might want to take some of your new text and fold it into this and then reference the User Guide only - I'm just angling here for any way to improve the User Guide ;-) jhrg 2/11/13</font>


== Types of Server Side Functions ==
== Types of Server Side Functions ==

Revision as of 23:10, 11 February 2013

Overview

Before you embark upon writing a server side function for DAP2 you should read through this overview of DAP2 constraint expressions and their use of functions.

Here's another source for information on Server Side FUnctions and Constraint Expressions: http://docs.opendap.org/index.php/UserGuideOPeNDAPMessages#Constraint_Expressions
You might want to take some of your new text and fold it into this and then reference the User Guide only - I'm just angling here for any way to improve the User Guide ;-) jhrg 2/11/13

Types of Server Side Functions

Here we document all three types of Server Side Functions that DAP2 supports. In practice only the first kind of function is very widely used. These functions take a set of variables from the current dataset and return a new value wrapped in a DAP2 variable. The other kinds of functions are used to control constraint expression evaluation and to add new variables to the dataset's DDS, respectively. We have used these to build queryable interfaces for inventories.

BaseType Functions
void(*btp_func)(int argc, libdap::BaseType *argv[], libdap::DDS &dds, libdap::BaseType **btpp)
A BaseType function takes four arguments: an integer (argc), a vector of BaseType *s (argv), the DDS for the dataset for which these function is being evaluated (analogous to the ENVP in UNIX) and a pointer for the function's return value. ARGC is the length of ARGV.
Boolean Functions
void(*bool_func)(int argc, libdap::BaseType *argv[], libdap::DDS &dds, bool *result)
A boolean function takes four arguments, an integer (argc), a vector of BaseType *s (argv), the DDS for the dataset for which these function is being evaluated (analogous to the ENVP in UNIX) and a pointer for the function's return value. Unlike the previous type function, the result is a boolean type, and will be used by the constraint expression evaluator to determine the truth value for the expression. ARGC is the length of ARGV.
Projection Functions
void(*proj_func)(int argc, libdap::BaseType *argv[], libdap::DDS &dds, libdap::ConstraintEvaluator &ce)
A projection function is a function that appears in the projection part of the CE and is executed for its side-effect. Usually adds a new variable to the DDS. These are run during the parse so their side-effects can be used by subsequent parts of the CE.

Writing Server Side Functions

Example: HelloWorld

What we'll do
  • Write a C++ function which uses one of the three server function type signatures
  • Write a very simple subclass of the libdap::Function class and install the your function into the class using the libdap::Function.setFunction() method.
  • Write a very simple subclass of the BESAbstractModule and in the initialize() method, add an instance of your Function class to the libdap::ServerFunctionsList
  • Build and install the module.


Get the example_ssFunction project and follow along:

svn co https://scm.opendap.org/svn/trunk/example_ssFunction

The helloWorld() function

Here is the example_ssf::helloWorld() function from the example_ssFunction project. The example_ssf::helloWorld() function is defined in the file ExampleServerSideFunctions.cc.

void helloWorld(  int argc,   libdap::BaseType *argv[],  libdap::DDS &dds, libdap::BaseType **btpp)
{
    Str *dapResult = new Str("helloWorldFunction_result");
    dapResult->set_value("Howdy Stranger...");
    *btpp = dapResult;
}

We can see from the method's type signature that it is a BaseType function. The helloWorld() function creates a DAP String object, set it's value and return via the return value parameter btpp.

A child class of libdap::Function

Now we need a libdap::Function to provide an API by which the server can learn things about our new function. Here is the example_ssf::HelloWorldFunction class as defined in ExampleServerSideFunctions.h.

class HelloWorldFunction: public libdap::ServerFunction {
public:
    HelloWorldFunction()
    {
        setName("helloWorld");
        setDescriptionString("Returns a DAP String object whose value is the string 'Hello World'.");
        setUsageString("helloWorld()");
        setRole("http://services.opendap.org/dap4/server-side-function/hellowWorld");
        setDocUrl("http://docs.opendap.org/index.php/Hyrax:_Server_Side_Functions");
        setFunction(example_ssf::helloWorld);
        setVersion("1.0");
    }
    virtual ~HelloWorldFunction()
    {
    }

};

The parent class of our new example_ssf::HelloWorldFunction class is libdap::Function. Since libdap::Function is not an abstract we don't need to override any methods to make the child class work, all we do is give the child class state that's specific to our new function.

Now we have

  • a BaseType function, and
  • a class through whose API we can learn about our function.

Now we just need to hook it up to the BES and Hyrax...

A child class of BESAbstractModule

In order to get our new function into Hyrax we need to have the BES load it at startup. This is done by writing a very simple BES module. For this example our module class is called ExampleServerSideFunctions which is declared in the header file ExampleServerSideFunctions.h:

class ExampleServerSideFunctions: public BESAbstractModule {
public:
    ExampleServerSideFunctions() {}
    virtual ~ExampleServerSideFunctions() {}
    virtual void initialize(const string &modname);
    virtual void terminate(const string &modname);
    virtual void dump(ostream &strm) const;
};


and implemented in ExampleServerSideFunctions.cc

void ExampleServerSideFunctions::initialize(const string &modname) {
    BESDEBUG( "ExampleServerSideFunctions", "Initializing ExampleServerSideFunctions:" << endl );

    libdap::ServerFunctionsList::TheList()->add_function(new example_ssf::HelloWorldFunction());

    BESDEBUG( "ExampleServerSideFunctions", "Done initializing ExampleServerSideFunctions" << endl );
}

void ExampleServerSideFunctions::terminate(const string &modname) {
    BESDEBUG( "ExampleServerSideFunctions", "Removing ExampleServerSideFunctions module (this does nothing)." << endl );
}

extern "C" {
    BESAbstractModule *maker() {
        return new ExampleServerSideFunctions;
    }
}

Note carefully the method ExampleServerSideFunctions.initialize() which contains the call to the libdap:ServerFunctionsList.addFunction() method. It is this call that causes a HelloWorld class instance and thus the helloWorld() function to be registered in BES.

And that's pretty much all there is to the C/C++ programming. What remains is to build a little autotools project for your function code and set it up to install into the BES when make install is run.