Table of Contents
While containers are the mechanism that you use to store and manage XML
documents, you use XmlManager
objects to create
and open XmlContainer
objects.
We therefore start with the XmlManager
.
XmlManager
is a high-level class used to
manage many of the objects that you use in a BDB XML application.
The following are some of the things you can do with
XmlManager
objects:
Manage containers. This management includes creating, opening, deleting, and renaming containers (see Managing Containers).
Create input streams used to load XML documents into containers (see Input Streams and Strings).
Create XmlDocument
,
XmlQueryContext
, and
XmlUpdateContext
objects.
Prepare and run XQuery queries (see Using XQuery with BDB XML).
Create a transaction object (see the Berkeley DB XML Getting Started with Transaction Processing guide for details).
Because XmlManager
is the only way to
construct important BDB XML objects, it is central to your BDB XML
application.
Before you can instantiate an
XmlManager
object, you have to make
some decisions about your Berkeley DB Environment. BDB XML
requires you to use a database environment. You can use
an environment explicitly, or you can allow the
XmlManager
constructor to manage the
environment for you.
If you explicitly create an environment, then you can turn on important features in BDB XML such as logging, transactional support, and support for multithreaded and multiprocess applications. It also provides you with an on-disk location to store all of your application's containers.
If you allow the XmlManager
constructor to implicitly create and/or open an environment
for you, then the environment is only configured to allow
multithreaded sharing of the environment and the underlying
databases (DB_PRIVATE
is used). All other
features are not enabled for the environment.
The next several sections describe the things you need to know in order to create and open an environment explicitly. We start with this activity first because it is likely to be the first thing you will do for all but the most trivial of BDB XML applications.
In order to use an environment, you must first open it. When you do this, there are a series of flags that you can optionally specify. These flags are bitwise or'd together, and they have the effect of enabling important subsystems (such as transactional support).
There are a great many environment open flags and these are described in the Berkeley DB documentation. However, there are a few that you are likely to want to use with your BDB XML application, so we describe them here:
DB_CREATE
If the environment does not exist at the time that it is opened, then create it. It is an error to attempt to open a database environment that has not been created.
DB_INIT_LOCK
Initializes the locking subsystem. This subsystem is used when an application employs multiple threads or processes that are concurrently reading and writing Berkeley DB databases. In this situation, the locking subsystem, along with a deadlock detector, helps to prevent concurrent readers/writers from interfering with each other.
Remember that under the covers BDB XML containers are using Berkeley DB databases, so if you want your containers to be accessible by multiple threads and/or multiple processes, then you should enable this subsystem.
DB_INIT_LOG
Initializes the logging subsystem. This subsystem is used for database recovery from application or system failures. For more information on normal and catastrophic recovery, see the Berkeley DB XML Getting Started with Transaction Processing guide.
DB_INIT_MPOOL
Initializes the shared memory pool subsystem. This subsystem is required for multithreaded BDB XML applications, and it provides an in-memory cache that can be shared by all threads and processes participating in this environment.
DB_INIT_TXN
Initializes the transaction subsystem. This subsystem provides atomicity for multiple database access operations. When transactions are in use, recovery is possible if an error condition occurs for any given operation within the transaction. If this subsystem is turned on, then the logging subsystem must also be turned on.
We discuss writing transactional applications in the Berkeley DB XML Getting Started with Transaction Processing guide.
DB_RECOVER
causes normal recovery to be run against the underlying database. Normal recovery ensures that the database files are consistent relative to the operations recorded in the log files. This is useful if, for example, your application experienced an ungraceful shut down and there is consequently an possibility that some write operations were not flushed to disk.
Recovery can only be run if the logging subsystem is turned on. Also, recovery must only be run by a single thread of control; typically it is run by the application's master thread before any other database operations are performed.
Regardless of the
flags
you decide to set at creation time,
it is important to use the same ones on all subsequent
environment opens (the exception to this is
DB_CREATE
which is only
required to create an environment). In
particular, avoid using
flags
to open environments that were not used at creation time. This
is because different subsystems require different data
structures on disk, and it is therefore illegal to attempt
to use subsystems that were not initialized when the
environment was first created.
To use an environment, you must first open it. At open time, you must identify the directory in which it resides and this directory must exist prior to the open attempt. At open time, you also specify the open flags, properties, if any, that you want to use for your environment.
When you are done with the environment, you must
make sure it is closed. You can either do this
explicitly, or you can have the XmlManager
object do it for you.
If you are explicitly closing your environment, you must make sure any containers opened in the environment have been closed before you close your environment.
For information on XmlManager
instantiation, see
XmlManager Instantiation and Destruction
For example:
#include "DbXml.hpp" ... u_int32_t env_flags = DB_CREATE | // If the environment does not // exist, create it. DB_INIT_LOCK | // Initialize the locking subsystem DB_INIT_LOG | // Initialize the logging subsystem DB_INIT_MPOOL | // Initialize the cache DB_INIT_TXN; // Initialize transactions char *envHome = "/export1/testEnv"; DB_ENV *myEnv = NULL; int dberr; dberr = db_env_create(&myEnv, 0); if (dberr) { std::cout << "Unable to create environment: " << db_strerror(dberr) << std::endl; if (myEnv) myEnv->close(myEnv, 0); return (EXIT_FAILURE); } myEnv->open(myEnv, envHome, env_flags, 0); // Do BDB XML work here myEnv->close(myEnv, 0);
You create an XmlManager
object by
calling its constructor. You destroy a XmlManager
object by calling its
destructor, either by using the
delete
operator or by allowing the
object to go out of scope (if it was created on the stack).
Note that XmlManager
is closed
and all of its resources released when the last open handle
to the object is
destroyed.
To construct an XmlManager
object, you
may or may not provide the constructor with an
open
DB_ENV
object. If you do instantiate XmlManager
with an opened environment handle, then XmlManager
will close and destroy that
DB_ENV
object for you if you
specify DBXML_ADOPT_DBENV
for the
XmlManager
constructor.
If you provide an
DB_ENV
object to the constructor, then you can use that object to use
whatever subsystems that you application may require (see
Environment Open Flags
for some common
subsystems).
If you do not provide an environment object,
then XmlManager
will implicitly create an environment for you. In this case, the
environment will not be
configured to use any subsystems and it is only
capable of being shared by multiple threads from within
the same process. Also, in this case you can optionally identify the
on-disk location where you want your containers to reside using
one of the following mechanisms:
Specify the path to the on-disk location in the container's name.
Specify the environment's data location using the
DB_HOME
environment variable.
In either case, you can pass the XmlManager
constructor a
flag argument
that controls that object's behavior
with regard to the underlying containers (the
flag
is NOT passed directly to the underlying environment or
databases).
Valid values are:
DBXML_ALLOW_AUTO_OPEN
When specified, XQuery queries that reference created but unopened containers will automatically cause the container to be opened for the duration of the query.
DBXML_ADOPT_DBENV
When specified,
XmlManager
will close and
destroy the
DB_ENV
object that it was instantiated with when the
XmlManager
is closed.
DBXML_ALLOW_EXTERNAL_ACCESS
When specified, XQuery queries executed from inside BDB XML can access external sources (URLs, files, and so forth).
For example, to instantiate an XmlManager
with a default environment:
#include "DbXml.hpp" ... using namespace DbXml; int main(void) { XmlManager myManager; // The manager and underlying // environment are closed when // this goes out of scope. return(0); }
And to instantiate an XmlManager
using an explicit
environment object:
#include "DbXml.hpp" ... using namespace DbXml; int main(void) { u_int32_t env_flags = DB_CREATE | // If the environment does not // exist, create it. DB_INIT_LOCK | // Initialize locking DB_INIT_LOG | // Initialize logging DB_INIT_MPOOL | // Initialize the cache DB_INIT_TXN; // Initialize transactions char *envHome = "/export1/testEnv"; DB_ENV *myEnv = NULL; int dberr; XmlManager *myManager = NULL; dberr = db_env_create(&myEnv, 0); if (dberr) { std::cout << "Unable to create environment: " << db_strerror(dberr) << std::endl; if (myEnv) myEnv->close(myEnv, 0); return (EXIT_FAILURE); myEnv->open(myEnv, envHome, env_flags, 0); myManager = new XmlManager(myEnv, DBXML_ADOPT_DBENV); // The manager and // environment is closed // when this object is // destroyed. try { if (myManager != NULL) { delete myManager; } } catch(XmlException &xe) { std::cerr << "Error closing XmlManager: " << xe.what() << std::endl; } }