This document focuses on the BES framework itself: the code that boots the server, accepts a request, parses BES XML, builds an execution plan, dispatches work to handlers, and transmits a response. It intentionally stops short of the format-specific modules in modules/.
At framework level, BES is a plugin host plus a request execution engine.
- The plugin host loads modules named in
BES.modules, initializes them, and lets them register request handlers, response handlers, catalogs, container stores, services, and transmitters. - The execution engine accepts a BES XML request, converts it into one or more command objects, builds a
BESDataHandlerInterfacefor each command, executes the bound response handler, and streams the result back to the caller.
The main framework directories involved in that flow are:
dispatch/: core registries, request state, default framework services, container and definition storage, error handling, logging, and plugin loading.server/: the daemon/listener application and the per-connection execution loop.ppt/: the socket protocol layer used between OLFS and BES.xmlcommand/: XML parsing and the command builders that turn<request>documents into executable plan objects.http/: shared HTTP client infrastructure used by framework and modules for remote resource access.standalone/: an in-process execution path used for testing and command-line work without PPT/network transport.
server/ServerApp.cc is the main listener process (beslistener).
At startup it:
- resolves the BES config file location,
- reads listener configuration such as TCP port, bind address, Unix socket, and secure mode,
- initializes the default framework module,
- initializes the default XML command set,
- loads all configured shared-object modules through
BESModuleApp, - creates listening sockets,
- constructs a
PPTServer, - enters the signal-aware accept loop.
In typical deployments, a small supervisor process (besdaemon, implemented in server/daemon.cc) starts the master beslistener, watches its exit status, and restarts it on a requested restart or abnormal termination. This keeps the listener lightweight while still providing a watchdog and control plane for start/stop/restart and config reloads.
dispatch/BESModuleApp.cc is the generic module loader used by both server and standalone.
Its job is:
- read
BES.modules, - map module names to shared libraries via
BES.module.<name>, - load each module through the plugin factory,
- call each module's
initialize(modname), - later call
terminate(modname)in reverse order.
Before optional modules load, dispatch/BESDefaultModule.cc seeds the framework with the base services needed to process any request.
It registers:
- default response handlers such as help, version, status, service, stream, container, definition, catalog, and context operations,
- the basic transmitter,
- info builders for text, HTML, and XML error/info output,
- default definition storage backends,
- the core
besdebug context.
Separately, xmlcommand/BESXMLDefaultCommands.cc registers the XML commands that the framework itself understands, including:
show,get,setContextandsetContexts,setContainer,define,- container and definition deletion commands,
- path-info and BES-key inspection helpers.
Together these two initializers create the base grammar and base runtime needed before data-format modules are loaded.
ppt/PPTServer.cc owns the protocol-level handshake on top of the socket listener.
Its responsibilities are:
- accept a socket,
- validate that the client is speaking PPT,
- optionally negotiate secure mode,
- wrap the socket as a connection,
- hand the connection to the server-side request handler.
The framework keeps PPT separate from request execution. The transport knows how to exchange chunks and extensions; it does not know how to interpret BES XML.
server/BESServerHandler.cc is the bridge from PPT connection to request execution.
It supports two process models controlled by BES.ProcessManagerMethod:
single: one process handles requests directly.multiple: the listener forks a child per client connection.
In both modes, each connected client gets its own process space, either because the listener is single-threaded or because a per-connection child process is forked.
Inside the worker loop, BES:
- receives PPT chunks until a full command arrives,
- handles protocol exit messages,
- wraps the connection output stream in
PPTStreamBuf, - creates a
BESXMLInterface, - calls
execute_request(from), - calls
finish(status), - emits error extensions if needed,
- either keeps serving the same client or exits on fatal conditions.
This separation matters: the server layer manages sockets, process lifetime, and protocol status; the interface layer manages request semantics.
dispatch/BESInterface.cc is the generic execution shell used by BESXMLInterface.
It provides the common lifecycle:
- attach the output stream to the request state,
- set request metadata like source and PID,
- select the basic transmitter,
- build the request plan,
- set request timeout,
- execute the plan,
- clear timeout,
- convert exceptions into error responses,
- finish logging and cleanup.
Its central request-state object is dispatch/BESDataHandlerInterface.h.
That object carries:
- the active
BESResponseHandler, - the container list and current container cursor,
- action and action-name strings,
- a generic string map for request metadata,
- the output stream,
- the response object reachable through the response handler,
- any generated
BESInfoerror object.
The important design choice is that BES passes around one mutable execution context instead of large numbers of specialized parameter types.
xmlcommand/BESXMLInterface.cc is where the framework turns BES XML into executable work.
build_data_request_plan():
- parses the request XML with libxml2,
- validates that the root element is
<request>, - extracts request identifiers and logging metadata,
- iterates over each child command element,
- looks up a command builder in the XML command registry,
- creates a
BESXMLCommand, - asks the command to parse its node into a command-specific
BESDataHandlerInterface, - validates any requested transmitter,
- appends the command to an ordered command list.
Two details are especially important:
- BES supports a request document with multiple commands, but only one command in the request may produce a response.
setContextis executed immediately while the plan is being built because later commands may depend on those context values.
In effect, xmlcommand/ is the planning stage of the framework.
BESXMLInterface::execute_data_request_plan() walks the planned command list.
For each command it:
- runs
prep_request(), - swaps
d_dhi_ptrto that command'sBESDataHandlerInterface, - logs the command,
- calls the command's bound response handler
execute(), - checks timeout status,
- calls
transmit_data().
The response handler is where a command becomes real work. For a get command, that usually means constructing a response object and asking the request-handler registry to populate it from one or more containers.
The framework is registry-driven. Modules do not patch a switch statement; they populate shared registries.
dispatch/BESRequestHandlerList.cc maps a container type or module name to a BESRequestHandler.
Its key behaviors are:
add_handler()andremove_handler()for module registration,execute_each()to iterate all containers in the current request,execute_current()to resolve the current container type, find the matching request handler, and invoke the method for the current action,execute_all()for service-wide operations like help/version.
The most important call is execute_current():
- it ensures the container is accessible,
- determines container type,
- finds the matching request handler,
- finds the method for the requested action,
- invokes that method to populate the response object.
That is the framework's main data dispatch pivot.
BESResponseHandlerList is the second dispatch layer. XML commands bind to response-handler builders, and those response handlers know how to execute a command using the current BESDataHandlerInterface.
Examples include:
- container management,
- definition management,
- show/version/help/status,
- DAP response builders.
dispatch/BESReturnManager.cc maps return as ... names to BESTransmitter instances.
This keeps response construction separate from response serialization.
Examples:
- the basic transmitter for standard BES output,
- DAP transmitters,
- file-out transmitters such as NetCDF, JSON, CoverageJSON, GeoTIFF, and DMR++.
When BESXMLInterface::transmit_data() sees RETURN_CMD, it resolves the named transmitter and lets the response handler transmit through it.
The rest of dispatch/ provides shared framework state:
BESCatalogList: named catalogs and browsing backends.BESContainerStorageList: named container stores and persistence layers.BESDefinitionStorageList: stores named definitions.BESInfoList: builders for human-readable or machine-readable info/error output.BESServiceRegistry: advertised services and formats.BESContextManager: request-scoped or command-scoped key/value context.
These registries are why modules can integrate cleanly without touching the server loop.
BESInterface sets a per-request timeout from either:
- the
bes_timeoutcontext, or BES.TimeOutInSeconds.
It starts RequestServiceTimer before execution and checks for timeout expiration before transmission. Some transmitters may disable the timeout once streaming has started.
Any BESError, C++ exception, or allocation failure is converted into a framework error response by BESInterface::handleException().
That path:
- logs the error,
- chooses XML or non-XML error formatting depending on context,
- embeds server administrator contact information,
- stores the error response in the current
BESDataHandlerInterface.
Then finish() prints/transmits the error info using the already-selected output stream.
At the end of a request:
BESXMLInterface::clean()deletes command objects and lets each command clean its DHI,BESInterface::end_request()releases any containers used by the request,- the server loop decides whether to continue or terminate the worker based on status.
standalone/StandAloneClient.cc uses the same BESXMLInterface path as the daemon, but skips PPT and sockets entirely.
That makes it useful for:
- regression tests,
- local debugging,
- generating baseline outputs,
- validating XML command behavior without the daemon stack.
Architecturally, standalone/ proves that the execution engine is decoupled from the network protocol. The same planning and dispatch code can run in-process.
http/ is not the request planner, but it is framework infrastructure because many modules depend on it for remote access.
It provides:
- remote resource access abstractions,
- URL handling,
- allowed-host checks,
- proxy configuration,
- credential management,
- HTTP and curl utilities,
- AWS SigV4 support.
This code sits below modules such as gateway, DMR++, S3-related access, and remote catalog implementations.
The framework flow is:
ServerApp
-> PPTServer
-> BESServerHandler
-> BESXMLInterface
-> build_data_request_plan()
-> response_handler->execute()
-> BESRequestHandlerList / other registries
-> transmitter
-> output stream / PPT chunks
For an ordinary get request, that usually expands to:
<request> XML
-> XML command object
-> BESDataHandlerInterface
-> response handler chosen by action
-> request handler chosen by container type
-> response object populated
-> transmitter chosen by return format
-> bytes written to client
The BES framework is less a monolithic server than a layered runtime:
server/andppt/handle process and transport concerns.xmlcommand/handles parsing and planning.dispatch/owns the registries and the shared execution contract.http/provides reusable remote-access primitives.standalone/reuses the same engine without the daemon protocol.
That layering is what lets BES mix and match:
- different request commands,
- different data readers,
- different output formats,
- different container sources,
- different transport environments.
The framework's core abstraction is: build a BESDataHandlerInterface, resolve handlers from registries, and let plugins do the format-specific work.