The three object handlers are normally implemented in three separate
programs. Each program has a main() function that looks like:
#include <iostream>
#include <string>
#include "DDS.h"
#include "cgi_util.h"
#include "DODSFilter.h"
extern void read_descriptors(DDS &dds, const string &filename)
throw (Error);
int
main(int argc, char *argv[])
{
DDS dds;
DODSFilter df(argc, argv);
try {
if (!df.OK()) {
df.print_usage();
return 1;
}
if (df.version()) {
df.send_version_info();
return 0;
}
// Read the netCDF file dataset descriptor in memory
read_descriptors(dds, df.get_dataset_name());
df.read_ancillary_dds(dds);
df.send_dds(dds, true);
}
catch (Error &e) {
set_mime_text(cout, dods_error,
df.get_cgi_version());
e.print(cout);
return 1;
}
return 0;
}
Most of the software is boilerplate. The first two lines of
main() are shown here:
DS dds; DODSFilter df(argc, argv);
These declare an instance of DDS, to be used a little later as well as an
instance of DODSFilter. The latter is used to parse command line arguments
fed to the program by the dispatch script. By using this class in concert
with the dispatch script, you can assume that the correct options and
arguments will be passed into your program. The instance of DODSFilter,
df, contains accessors for all the switches that the dispatch script
might use, so by passing argc and argv to its constructor, you're
sure to parse them all.
try {
if (!df.OK()) {
df.print_usage();
return 1
}
if (df.version()) {
df.send_version_info();
return 0;
}
This code calls the DODSFilter invariant to check that the handler was invoked correctly. If a malformed request was made to the server, this will be flagged here and the server will return an error message describing how to submit a correctly formed URL. This also tests to see if the request is for version information. If so, the DODSFilter object prints the server's version number and the handler exits. Just about every server built with our code includes these lines verbatim.
// Read the netCDF file dataset descriptor in memory read_descriptors(dds, df.get_dataset_name()); df.read_ancillary_dds(dds); df.send_dds(dds, true);
These lines are the heart of the handler. Exactly what's going on here
will be covered in more detail later. However, each of the three
object handlers contains similar code that builds the object to
returned as the response and then passes that object to the
DODSFilter::send_dds, send_das or send_data
method, depending on the type of object to be returned.
catch (Error &e) {
set_mime_text(cout, dods_error, df.get_cgi_version());
e.print(cout);
return 1;
}
return 0;
Rounding out the program is a catch block that picks up exceptions thrown by
the any of DAP library code. The DAP library throws two types of exceptions,
Error and InternalErr. The latter is a subclass of Error,
so catching just Error will get everything. Note that you should also catch
bad_alloc exceptions at this level (the library does not) unless you
catch them inside the function or method that builds the DDS, DAS or DataDDS.
If e is an InternalErr, then when it prints, you'll see
information about the file and line number where the problem was detected.
Regular Error objects print something that's more useful to users. By
calling the set_mime_text function (see the file cgi_util.cc) you're
sure that the error message will be returned to the client in a form that
both web browsers and more sophisticated clients can use.