dbXML Developers Guide version 1.0

The dbXML Project

Kimbro Staken

This documentation is a work in progress. Links to the most current version can be found at http://www.dbxml.org/docs/

$Id: DevelopersGuide.xml,v 1.24 2001/09/06 22:57:28 kstaken Exp $


1. Introduction to Programming dbXML
Accessing the Server
Introducing the XML:DB XML Database API
Diving in With an Example Program
Accessing dbXML Remotely
2. Managing Documents
Creating a Collection
Working with Documents
3. Using XPath to Query the Database
Introduction
Using the XML:DB Java API
4. Using XUpdate to Modify the Database
Introduction
XUpdate Commands
XUpdate Node Construction
Using the XML:DB API for XUpdate
5. Using XMLObjects
Introduction
A Simple XMLObject
Building the XPath Query XMLObject
Putting the XPath Query XMLObject to Work
6. Database properties
7. Address Book Example Application
8. Experimental Features
Auto Linking

1. Introduction to Programming dbXML

Accessing the Server

The dbXML server can be accessed either programmatically through the server's APIs or from the command line using the provided command line tools. This document covers programmatic access, for more information on using the command line interface please refer to the dbXML Users Guide.

dbXML currently offers three layers of APIs that can be used to develop applications.

  • XML:DB XML Database API used to develop dbXML applications in Java. This is the primary API used to develop applications and will be the API given the most coverage in this manual. The XML:DB API is built on top of the dbXML CORBA API. dbXML currently implements the May 07, 2001 draft of the XML:DB API. This API will change slightly in the future to track the development of the XML:DB API.

  • CORBA API used when accessing dbXML from a language other then Java. The CORBA API is built on top of the Core Server API. It is likely that the CORBA API will be replaced with a new network API in a later version of dbXML.

  • Core Server API is the internal Java API of the core database engine. This API is used to build the CORBA API and is also used when writing XMLObject server extensions. This is the lowest level API and is only available to software running in the same Java VM as the database engine itself. the most common use of this API is in developing XMLObject server extensions.

The most common API for end user applications is the XML:DB XML Database API that was developed by the XML:DB Initiative. This API is a vendor neutral API intended to make it possible to build applications that will work with more then one XML database without too much difficulty. This is similar to the capabilities provided by JDBC for relational databases. More information about this API can be found on the XML:DB Initiative web site. http://www.xmldb.org Most programming examples in this manual will use the XML:DB API. The dbXML implementation of the API is a Core Level 1 implementation.

The dbXML server also exposes a CORBA API that is used to implement the XML:DB API. The CORBA API will mainly be of interest to those who want to access dbXML from a language other then Java. Any language that supports a CORBA binding should be able to utilize the services of the dbXML server via the CORBA API. This document does not cover development with the CORBA API as the XML:DB API is the preferred mechanism for developing dbXML applications. If you are developing applications in Java you can safely ignore the existence of this API. The CORBA API will be covered in a seperate document to be written at a later time.

The final API for dbXML is the Core Server API that is used when writing XMLObject server extensions. This API is the lowest level API available for the dbXML server and will be covered briefly when looking at the development of XMLObjects.

Introducing the XML:DB XML Database API

XML:DB API is being developed by the XML:DB Initiative to facilitate the development of applications that function with minimal change on more then one XML database. [1] This is roughly equivalent to the functionality provided by JDBC or ODBC for providing access to relational databases. dbXML provides an implementation of the XML:DB API that also serves as the primary programming API for dbXML.

The XML:DB API is based around the concept of collections that store resources. A resource can be an XML Document, a binary blob or some type that is not currently defined. Collections can be arranged in a hierarchical fashion. This makes the architecture very similar to that of a typical Windows or UNIX file system. What is different however, is that collections also expose services that allow you to do things such as query XML documents using XPath or update resources in a transactionally secure manner.

The XML:DB API defines several levels of interoperability called Core Levels in XML:DB terminology. The dbXML implementation of the API is a Core Level 1 implementation plus implementations of some of the optional services.

The additional supported services include.

  • XUpdateQueryService - Enables execution of XUpdate queries against the database.

  • CollectionManagementService - Provides basic facilities to create and remove collections.

In addition to Core Level 1 support the dbXML implementation also supports a few added services that are specific to dbXML. These services exist because the functionality is necessary to fully utilize all the capabilities provided by dbXML. However, they are proprietary to dbXML and will not function unchanged on other XML databases. [2]

The following services are currently provided by dbXML and are not part of the common XML:DB API.

  • DatabaseInstanceManager - Provides the ability to control the operation of the server programatically.

  • CollectionManager - Provides the ability to create and configure collection instances within the server. This is a much more functional version of CollectionManagementService that will only work with dbXML.

  • XMLObjectService - Provides a mechanism to invoke XMLObjects within the server.

While this guide aims to provide some useful examples and to guide you in the process of getting to know how to program dbXML it is also useful to know that there is a good source of example code within the server it self. The dbXML command line tools are built 100% on the XML:DB API and provide a pretty comprehensive set of examples in how to use the API. This is especially true when it comes to the dbXML specific services that are included with the server. The source code for all the command line tools can be found in dbXML-Core/java/src/org/dbxml/tools/command.

Diving in With an Example Program

1.1. Simple XML:DB Example Program

This example simply executes an XPath query against a collection, retrieves the results as text and prints them out.

You can find the source code for this example in dbXML-Core/java/examples/guide/src/org/dbxml/examples/Example1.java

package org.dbxml.examples;

import org.xmldb.api.base.*;
import org.xmldb.api.modules.*;
import org.xmldb.api.*;

public class Example1 {
   public static void main(String[] args) throws Exception {
      Collection col = null;
      try {
         String driver = "org.dbxml.client.xmldb.DatabaseImpl";
         Class c = Class.forName(driver);

         Database database = (Database) c.newInstance();
         DatabaseManager.registerDatabase(database);

         col = DatabaseManager.getCollection("xmldb:dbxml:///db/root/ocs");

         String xpath = "//test[text()='Hello']";
         XPathQueryService service =
            (XPathQueryService) col.getService("XPathQueryService", "1.0");
         ResourceSet resultSet = service.query(xpath);
         ResourceIterator results = resultSet.getIterator();
         while (results.hasMoreResources()) {
            Resource res = results.nextResource();
            System.out.println((String) res.getContent());
         }
      }
      catch (XMLDBException e) {
         System.err.println("XML:DB Exception occured " + e.errorCode);
      }
      finally {
         if (col != null) {
            col.close();
         }
      }
   }
}

While this isn't the simplest possible example program to start with it does a nice job of showing all the basic techniques used when building applications with the XML:DB API.

To begin the program imports several XML:DB packages.

import org.xmldb.api.base.*;
import org.xmldb.api.modules.*;
import org.xmldb.api.*;

These import the basic classes required by the API. import org.xmldb.api.base.*; is the base API module and is required for all XML:DB applications. import org.xmldb.api.*; imports the all important DatabaseManager class which is the entry point into the API. import org.xmldb.api.modules.*; brings in the optional modules defined for the API. In this case the module we're interested in is XPathQueryService.

Before we can use the API we need to create and register the database driver we want to use. In this case since we're writing for dbXML we use org.dbxml.client.xmldb.DatabaseImpl for our driver and register it with the DatabaseManager

String driver = "org.dbxml.client.xmldb.DatabaseImpl";
Class c = Class.forName(driver);

Database database = (Database) c.newInstance();
DatabaseManager.registerDatabase(database);

Now that our driver is registered we're ready to retrieve the collection that we want to work with.

Collection col =
   DatabaseManager.getCollection("xmldb:dbxml:///db/root/ocs");

In the XML:DB API collections are retrieved by calling getCollection and handing it a URI that specifies the collection we want. The format of this URI will vary depending on the database implementation being used but will always begin with xmldb: and be followed by a database specific database name, dbxml: in the case of dbXML.

The rest of the URI is a path used to locate the collection you want to work with. This path begins with the name of the root collection for the dbXML instance that you are trying to connect with. All dbXML instances must have a unique name for the root collection. The reason for this is that the name of the root collection is also the name of the database instance and that name is what the dbXML server uses to register itself with the naming service. In all examples in this guide the root collection is called db. This is the default name used for a newly installed instance of dbXML. If you have more then one instance of dbXML running that you must be sure to change the names on all other instances so that they are unique. Once you do this you can switch between the instances by simply changing the first component of the path.

dbxml:///db/news
dbxml:///db2/news

These paths will switch between a dbXML server with a root collection of db and one of db2. These instances could be on the same machine or on two completely different machines and to your application there is no significant difference.

After the root collection name the rest of the URI simply consists of the path to locate the collection you want to work with.

Now that we have a reference to the collection that we want to work with we need to get a reference to the XPathQueryService service for that collection.

String xpath = "//test[text()='Hello'";
XPathQueryService service =
   (XPathQueryService) col.getService("XPathQueryService", "1.0");
ResourceSet resultSet = service.query(xpath);

Services provide a way to extend the functionality of the XML:DB API as well as enabling the definition of optional functionality. In this case the XPathQueryService is an optional part of Core Level 0 but is a required part of Core Level 1. Since dbXML provides a Core Level 1 XML:DB API implementation the XPathQueryService is available.

To retrieve a service you must know the name of the service that you want as well as the version. Services define their own custom interfaces so you must cast the result of the getService() call to the appropriate service type before you can call its methods. The XPathQueryService defines a method query() that takes an XPath string as an argument. Different services will define different sets of methods.

Now that we have an XPathQueryService reference and have called the query() method we get a ResourceSet containing the results. Since we just want to print out the results of the query, we need to get an iterator for our results and then use it to print out the results.

ResourceIterator results = resultSet.getIterator();
while (results.hasMoreResources()) {
   Resource res = results.nextResource();
   System.out.println((String) res.getContent());
}

Resources are another important concept within the XML:DB API. Since XML can be accessed with multiple APIs and since an XML database could potentialy store more the one type of data, resources provide an abstract way to access the data in the database. The dbXML implementation only supports XMLResource but other vendors may support additional resource types as well.

XMLResource provides access to the underlying XML data as either text, a DOM Node or via SAX ContentHandlers. In our example we're simply working with the content as text but we could just as easily have called getContentAsDom() to get the content as a DOM Node. Since we just want to print the XML out to the screen it is easier to just work with text.

The final element about our example program worth noting is the finally clause.

finally {
   if (col != null) {
      col.close();
   }
}

The finally clause closes the collection that we created earlier. This is vitally important and should never be overlooked. Closing the collection releases all the resources consumed by the collection. In the dbXML implementation this will make sure that the CORBA resources are released properly. Failure to properly call close on the collection will result in a resource leak within the server.

Accessing dbXML Remotely

By default dbXML assumes that the client and server are running on the same machine. In most configurations this will not be the case so it will be necessary to include the hostname and port of the server where dbXML is running in your URIs. The port you use is the port that the HTTP server is listening on. By default the configuration used is dbxml://localhost:4080. To access the host db.dbxml.org on port 4090 the URI would look something like this dbxml://db.dbxml.org:4090/db/root/ocs


[1] The XML:DB API is still under active development and will change slightly in later revisions.

[2] It is a goal for XML:DB API to add common services for many of these things as the requirements from various vendors become more clear.

2. Managing Documents

In this chapter we'll look at using the XML:DB API to manage documents within the dbXML server. As part of this we'll look at some sample code that could be used to manage the data used by the AddressBook example application included with the server and discussed in more detail later.

When looking at managing documents with the XML:DB API the first thing we need to confront is that the API doesn't actually work directly with documents. It works with what the API calls resources that are an abstraction of a document. This abstraction allows you to work with the same document as either text, a DOM tree or SAX events. This is important to understand as the use of resources runs as a common thread throughout the XML:DB API. The XML:DB API actually defines more then one type of resource however dbXML does not implement anything beyond XMLResource.

Creating a Collection

Before we can work with any data in the database we need to create a collection to hold our data. While we could easily create this collection using the command line tools it will be more fun to see how you might do this from your own program. This will also show you a quick example of using the dbXML specific CollectionManager service to manage collections. This guide doesn't go into detail about using this service but you can find lots of examples by looking at the source code to the command line tool commands in the package org/dbxml/tools/commands.

The collection we want to create will be named addressbook and will be a child of the root collection.

2.1. Creating a Collection

package org.dbxml.examples;

import org.xmldb.api.base.*;
import org.xmldb.api.modules.*;
import org.xmldb.api.*;

// For the dbXML specific CollectionManager service
import org.dbxml.client.xmldb.services.*;

import org.dbxml.xml.dom.*;

public class CreateCollection {
   public static void main(String[] args) throws Exception {
      Collection col = null;
      try {
         String driver = "org.dbxml.client.xmldb.DatabaseImpl";
         Class c = Class.forName(driver);

         Database database = (Database) c.newInstance();
         DatabaseManager.registerDatabase(database);
         col =
            DatabaseManager.getCollection("xmldb:dbxml:///db/");

         String collectionName = "addressbook";
         CollectionManager service =
            (CollectionManager) col.getService("CollectionManager", "1.0");

         // Build up the Collection XML configuration.
         String collectionConfig =
            "<collection compressed=\"true\" name=\"" + collectionName + "\">" +
            "   <filer class=\"org.dbxml.core.filer.BTreeFiler\" gzip=\"true\"/>" +
            "</collection>";

         service.createCollection(collectionName,
               DOMParser.toDocument(collectionConfig));

         System.out.println("Collection " + collectionName + " created.");
      }
      catch (XMLDBException e) {
         System.err.println("XML:DB Exception occured " + e.errorCode);
      }
      finally {
         if (col != null) {
            col.close();
         }
      }
   }
}

With this example you can see a basic example of how to create the CollectionManager service and use it to create the collection. This service is proprietary to dbXML so if you use it in your application you will not be able to port it to another server. However, if you have the need to create collections within your programs this is currently the most powerful way to do it. [3]

The trickiest part of creating a collection is creating the proper XML configuration to hand to the createCollection method. This XML is the exact same thing that is placed into the system.xml file. At this time these XML configurations are not documented so to see what they need to be you should look for examples in system.xml and the source code for the command line tools. Future versions of this documentation will cover this area in more detail.

Working with Documents

Now that we have a collection to store our data, we need to add some data to it. We could use the command line tools to do this but since we want to learn how the XML:DB API works we'll look at how we can do this in a program that we write.

For our examples in this chapter we'll work with some very simple XML files that could be used to represent a person in an address book. Later in the guide we'll look at an example application that implements the actual address book functionality. Each address book entry is stored in a seperate XML file.

2.2. Example Document

<person>
   <fname>John</fname>
   <lname>Smith</lname>
   <phone type="work">563-456-7890</phone>
   <phone type="home">534-567-8901</phone>
   <email type="home">jsmith@somemail.com</email>
   <email type="work">john@lovesushi.com</email>
   <address type="home">34 S. Colon St.</address>
   <address type="work">9967 W. Shrimp Ave.</address>
</person>

If we store this example XML into a file we can then load it into our addressbook collection using a simple program.

2.3. Adding an XML File to the Database

package org.dbxml.examples;

import org.xmldb.api.base.*;
import org.xmldb.api.modules.*;
import org.xmldb.api.*;

import java.io.*;

public class AddDocument {
   public static void main(String[] args) throws Exception {
      Collection col = null;
      try {
         String driver = "org.dbxml.client.xmldb.DatabaseImpl";
         Class c = Class.forName(driver);

         Database database = (Database) c.newInstance();
         DatabaseManager.registerDatabase(database);
         col =
            DatabaseManager.getCollection("xmldb:dbxml:///db/addressbook");

         String data = readFileFromDisk(args[0]);

         XMLResource document = (XMLResource) col.createResource(null,
            "XMLResource");
         document.setContent(data);
         col.storeResource(document);
         System.out.println("Document " + args[0] + " inserted");
      }
      catch (XMLDBException e) {
         System.err.println("XML:DB Exception occured " + e.errorCode);
      }
      finally {
         if (col != null) {
            col.close();
         }
      }
   }

   public static String readFileFromDisk(String fileName) throws Exception {
      File file = new File(fileName);
      FileInputStream insr = new FileInputStream(file);

      byte[] fileBuffer = new byte[(int)file.length()];

      insr.read(fileBuffer);
      insr.close();

      return new String(fileBuffer);
   }
}

Much of this program is similar to what we've already seen in our other XML:DB programs. Really the only difference is the code to add the document.

Documents are added to the server by first creating a new resource implementation from a collection, setting its content and then storing the resource to the collection. The type of resource that is created is an XMLResource this can be used to store XML as either text, a DOM Node or a SAX ContentHandler.

If you had your content already in a DOM tree you could also add the document as a DOM.

XMLResource document = (XMLResource) col.createResource(null);
document.setContentAsDOM(doc); // doc is a DOM document
col.storeResource(document);
         

The only difference here is that you must have the document as a DOM Document already and then call setContentAsDOM(). From there the resource works the same as always.

One thing to note is that a resource must be stored in the same collection from which it was originally created.

2.4. Retrieving an XML Document from the Database

package org.dbxml.examples;

import org.xmldb.api.base.*;
import org.xmldb.api.modules.*;
import org.xmldb.api.*;

import java.io.*;

public class RetrieveDocument {
   public static void main(String[] args) throws Exception {
      Collection col = null;
      try {
         String driver = "org.dbxml.client.xmldb.DatabaseImpl";
         Class c = Class.forName(driver);

         Database database = (Database) c.newInstance();
         DatabaseManager.registerDatabase(database);
         col =
            DatabaseManager.getCollection("xmldb:dbxml:///db/addressbook");

         XMLResource document = (XMLResource) col.getResource(args[0]);
         if (document != null) {
            System.out.println("Document " + args[0]);
            System.out.println(document.getContent());
         }
         else {
            System.out.println("Document not found");
         }
      }
      catch (XMLDBException e) {
         System.err.println("XML:DB Exception occured " + e.errorCode);
      }
      finally {
         if (col != null) {
            col.close();
         }
      }
   }
}

2.5. Deleting an XML Document from the Database

package org.dbxml.examples;

import org.xmldb.api.base.*;
import org.xmldb.api.modules.*;
import org.xmldb.api.*;

import java.io.*;

public class DeleteDocument {
   public static void main(String[] args) throws Exception {
      Collection col = null;
      try {
         String driver = "org.dbxml.client.xmldb.DatabaseImpl";
         Class c = Class.forName(driver);

         Database database = (Database) c.newInstance();
         DatabaseManager.registerDatabase(database);
         col =
            DatabaseManager.getCollection("xmldb:dbxml:///db/addressbook");

         Resource document = col.getResource(args[0]);
         col.removeResource(document);
         System.out.println("Document " + args[0] + " removed");
      }
      catch (XMLDBException e) {
         System.err.println("XML:DB Exception occured " + e.errorCode);
      }
      finally {
         if (col != null) {
            col.close();
         }
      }
   }
}

[3] The CollectionManagementService provides another way to create a simple collection. In this scenario it would work fine but it you need a greater level of control over the collection configuration using CollectionManager is the only way to do it.

3. Using XPath to Query the Database

Introduction

dbXML currently supports XPath as a query language. In many applications XPath is only applied at the document level but in dbXML XPath queries are executed at the collection level. This means that a query can be run against multiple documents and the result set will contain all matching nodes from all documents in the collection. The dbXML server also support the creation of indexes on particular XPaths to speed up commonly used XPath queries.

Using the XML:DB Java API

The XML:DB API defines operations for searching single documents as well as collections of XML documents using XPath. These operations are exposed through the XPathQueryService. In order to query single documents you use the queryResource() method and to query an entire collection you use the query() method.

3.1. Querying with XPath

This example simply executes an XPath query against a collection, retrieves the results as text and prints them out.

You can find the source code for this example in dbXML-Core/java/examples/guide/src/org/dbxml/examples/Example1.java

package org.dbxml.examples;

import org.xmldb.api.base.*;
import org.xmldb.api.modules.*;
import org.xmldb.api.*;

public class Example1 {
   public static void main(String[] args) throws Exception {
      Collection col = null;
      try {
         String driver = "org.dbxml.client.xmldb.DatabaseImpl";
         Class c = Class.forName(driver);

         Database database = (Database) c.newInstance();
         DatabaseManager.registerDatabase(database);

         col = DatabaseManager.getCollection("xmldb:dbxml:///db/root/ocs");

         String xpath = "//test[text()='Hello']";
         XPathQueryService service =
            (XPathQueryService) col.getService("XPathQueryService", "1.0");
         ResourceSet resultSet = service.query(xpath);
         ResourceIterator results = resultSet.getIterator();
         while (results.hasMoreResources()) {
            Resource res = results.nextResource();
            System.out.println((String) res.getContent());
         }
      }
      catch (XMLDBException e) {
         System.err.println("XML:DB Exception occured " + e.errorCode);
      }
      finally {
         if (col != null) {
            col.close();
         }
      }
   }
}

TODO: cover namespace support

4. Using XUpdate to Modify the Database

Introduction

XUpdate is a specification under development by the XML:DB Initiative to enable simpler updating of XML documents. It is useful within the context of an XML database as well as in standalone implementations for general XML applications. XUpdate gives you a declarative method to insert nodes, remove nodes, and change nodes within an XML document. The syntax is specified in the XUpdate working draft available on the XML:DB Initiative website.

The XUpdate implementation in dbXML is based around the Lexus XUpdate implementation that was developed by the Infozone Group.

The general model around XUpdate is to use an xupdate:modifications container to batch a series of XUpdate commands. All commands will be performed in series against either a single XML document or an entire collection of XML documents as specified by the developer.

Execution of XUpdate commands is performed in two phases. First selecting a node set within the document or collection and then applying a change to the selected nodes.

4.1. Basic XUpdate Insert Command

<xupdate:modifications version="1.0"
        xmlns:xupdate="http://www.xmldb.org/xupdate"> 
   
   <xupdate:insert-after select="/addresses/address[1]" > 
      
      <xupdate:element name="address">
         <xupdate:attribute name="id">2</xupdate:attribute>
         <fullname>John Smith</fullname> 
         <born day='2' month='12' year='1974'/>           
         <country>Germany</country> 
     </xupdate:element> 
   
   </xupdate:insert-after> 
</xupdate:modifications>

XUpdate Commands

  • xupdate:insert-before - Inserts a new node in document order before the selected node.

  • xupdate:insert-after - Inserts a new node in document order after the selected node.

  • xupdate:update - Replaces all child nodes of the selected node with the specified nodes.

  • xupdate:append - Appends the specified node to the content of the selected node.

  • xupdate:remove - Remove the selected node

  • xupdate:rename - Renames the selected node

  • xupdate:variable - Defines a variable containing a node list that can be reused in later operations.

XUpdate Node Construction

  • xupdate:element - Creates a new element in the document.

  • xupdate:attribute - Creates a new attribute node associated with an xupdate:element.

  • xupdate:text - Creates a text content node in the document.

  • xupdate:processing-instruction - Creates a processing instruction node in the document.

  • xupdate:comment - Creates a new comment node in the document.

Using the XML:DB API for XUpdate

The XML:DB API provides an XUpdateQueryService to enable executing XUpdate commands against single documents or collections of documents. To update a single document you use the updateResource() method and to apply the updates to an entire collection you use the update() method.

The next example program applies a set of XUpdate modifications to an entire collection of data. It first removes all elements that match the XPath /person/phone[@type = 'home'] and then adds a new entry after all elements that match the XPath /person/phone[@type = 'work']

4.2. Using XUpdate to modify the database

import org.xmldb.api.base.*;
import org.xmldb.api.modules.*;
import org.xmldb.api.*;

/**
 * Simple XML:DB API example to update the database.
 */
public class XUpdate {
   public static void main(String[] args) throws Exception {
      Collection col = null;
      try {
         String driver = "org.dbxml.client.xmldb.DatabaseImpl";
         Class c = Class.forName(driver);
         
         Database database = (Database) c.newInstance();
         DatabaseManager.registerDatabase(database);
         col =
            DatabaseManager.getCollection("xmldb:dbxml:///db/addressbook");

         String xupdate = "<xu:modifications version=\"1.0\"" +
            "      xmlns:xu=\"http://www.xmldb.org/xupdate\">" +
            "   <xu:remove select=\"/person/phone[@type = 'home']\"/>" + 
            "   <xu:update select=\"/person/phone[@type = 'work']\">" +
            "       480-300-3003" +
            "   </xu:update>" +
            "</xu:modifications>";

         XUpdateQueryService service =
            (XUpdateQueryService) col.getService("XUpdateQueryService", "1.0");
         service.update(xupdate);
      }
      catch (XMLDBException e) {
         System.err.println("XML:DB Exception occured " + e.errorCode + " " + 
            e.getMessage());
      }
      finally {
         if (col != null) {
            col.close();
         }
      }
   }
}

5. Using XMLObjects

Introduction

XMLObjects provide a mechanism for extending the functionality of the dbXML server. They are implemented in Java and are configured at the collection level within the server. Building and installing XMLObjects is quite simple but they still provide significant power to extend the server with new functionality.

The uses for XMLObjects are many i.e. updating documents on the server side, implementing a custom query language or any other functionality where doing the operation on the server side would be more efficient. The example XMLObject included here will show you how you could implement a single document query mechanism in the server even when dbXML by default queries at the collection level.

Note: When writing XMLObjects you use the internal APIs for dbXML and therefore have full access to the internals of the server. Because of this you should exercise caution in the code that you write to insure that it is robust and does not harm the operation of the server.

A Simple XMLObject

XMLObjects are simply Java classes that extend SimpleXMLObject to provide custom functionality within the server.

To get started let's take a look at a very simple Hello World XMLObject.

5.1. A Hello World XMLObject

package org.dbxml.examples;

import org.dbxml.core.objects.*;

public class HelloWorldXMLObject extends SimpleXMLObject {
   public String execute() {
     return "Hello World";
   }
}
            

This simple XMLObject just defines one method execute() that takes no parameters and returns a String. The execute() method name is special within the context of XMLObjects because if no explicit method is specified in the invocation request the XMLObject framework will call the execute() method automatically.

To compile and install this XMLObject you need to make sure that you have dbXML.jar and the directory where the XMLObject class file can be found in your CLASSPATH environment variable. The directory for the class file must also be available to the dbXML server before you start it.

Once you have your classpath setup you can compile the XMLObject like any other Java class and then install it in the /db/root collection.

dbxmladmin add_xmlobject -i org.dbxml.examples.HelloWorldXMLObject -n Hello -c /db/root
         

And then you can run it.

dbxml invoke -c /db/root -o Hello
         

One thing to note about this is that when you invoke the XMLObject you need to use the name that you configured with the -n parameter to add_xmlobject not the name of the class. In this case we named the object Hello so that is what we use to invoke it.

If all went well you should see something like this

Results =

Hello World
         

Building the XPath Query XMLObject

By default the dbXML XPath query functionality takes an XPath and applies it against all documents stored in a collection to find a set of matching nodes. Depending on our application this may not be what we really want. The XPathQueryService does support doing this but it will be instructive to look at how we could roll our own implementation to do the same thing.

To do this we first need to decide what information is required to execute the query. First we need the XPath we want to execute and second we need the key of the document within the collection. We also need the collection itself but since XMLObjects are configured at the collection level within the system this is taken care of automatically.

Now that we know what data we need we can see that we're going to need a method that takes two parameters, the xpath and the id. Once we have these parameters we'll need to retrieve the document and then use an XPath engine to select the nodes we want from the document. Our solution might look something like this.

5.2. Single Document XPath Query XMLObject

package org.dbxml.examples;

import org.dbxml.core.*;
import org.dbxml.core.objects.*;
import org.dbxml.xml.dom.*;

import org.w3c.dom.*;

import org.apache.xpath.*;

public class XPathQueryXMLObject extends SimpleXMLObject {
   public static final String [] PARAMS_query = {"xpath", "id"};
   public static final String [] DEFAULTS_query = {"/", ""};

   public Document query(String xpath, String id) throws Exception {
      Collection col = getCollection();
      Document target = col.getDocument(id);

      NodeList result = XPathAPI.selectNodeList(target, xpath);

      // Turn the NodeList into a document.
      Document doc = new DocumentImpl();

      Element root = doc.createElement("result");
      doc.appendChild(root);
      int i = 0;
      while ( (result != null) && (i < result.getLength()) ) {
         root.appendChild(doc.importNode(result.item(i), true));
         i++;
      }

      return doc;
   }
}
            

Delving into this in a little more detail we see several obvious differences from our simple example.

First we're no longer using the default execute() method and have defined our own method named query(). [4]

Next, we also have now added two contants to our class PARAMS_query and DEFAULTS_query. PARAMS_query is used to define the names of the parameters that are required by the query method. DEFAULTS_query defines the default values that are provided for those parameters if the user leaves out a parameter when invoking the XMLObject. These constants are necessary to provide hints to the XMLObject engine on how to map incoming parameters to the proper parameters on the method.

This XML Object is installed just like before except this time we're going to configure it for the addressbook collection.

dbxmladmin add_xmlobject -i org.dbxml.examples.XPathQueryXMLObject -n XPath -c /db/addressbook
         

Before we can use it though we need some sample data. Again we're using the familiar person XML. This should be added to the database under the key address1.

<?xml version="1.0"?>
<person>
   <fname>John</fname>
   <lname>Smith</lname>
   <phone type="work">563-456-7890</phone>
   <phone type="home">534-567-8901</phone>
   <email type="home">jsmith@somemail.com</email>
   <email type="work">john@lovesushi.com</email>
   <address type="home">34 S. Colon St.</address>
   <address type="work">9967 W. Shrimp Ave.</address>
</person>

dbxml add_document -c /db/addressbook -n address1 -f address1.xml
         

After adding this data we can now use our XMLObject to retrieve nodes from just this document.

dbxml invoke -c /db/addressbook -o XPath/query?xpath='/person/fname'&id=address1
         

Note: on UNIX you will need to escape the '&' with a '\' character to prevent its evaluation by the shell. If all goes well the result will be.

Results =

<!xml version="1.0"?>
<result><fname>John</fname></result>
         

Putting the XPath Query XMLObject to Work

So far we've seen how to create a couple different XMLObjects, how to install them in the server and how to invoke them from the command line. While this alone is very useful, XMLObjects are really much more useful when you use them from within another program.

Note: Since XMLObjects are a proprietary feature of dbXML we have to use a dbXML specific extension service for the XML:DB API to invoke them from within our programs. This is important to note because programs built to use XMLObjects will not be portable to other XML databases.

In this example we'll use our XPathQueryXMLObject to build a very simple program that queries for the street and then prints the the value out.

For this example we'll use the same object configuration and sample data as used in the previous section.

5.3. Invoking the XPathQueryXMLObject via the XML:DB API

package org.dbxml.examples;

import org.xmldb.api.base.*;
import org.xmldb.api.modules.*;
import org.xmldb.api.*;

// Import the dbXML specific XMLObjectService
import org.dbxml.client.xmldb.services.*;

public class XPathQueryInvoke {
   public static void main(String[] args) throws Exception {
      Collection col = null;
      try {
         String driver = "org.dbxml.client.xmldb.DatabaseImpl";
         Class c = Class.forName(driver);

         Database database = (Database) c.newInstance();
         DatabaseManager.registerDatabase(database);
         col =
            DatabaseManager.getCollection("xmldb:dbxml:///db/addressbook");

         String xpath = "/person/fname";
         String key = "address1";
         XMLObjectService service =
            (XMLObjectService) col.getService("XMLObjectService", "1.0");
         Resource result = service.invokeXMLObject("/db/addressbook/XPath/query?xpath=" +
            xpath + "&id=" + key);

         System.out.println((String) result.getContent());
      }
      catch (XMLDBException e) {
         System.err.println("XML:DB Exception occured " + e.errorCode);
      }
      finally {
         if (col != null) {
            col.close();
         }
      }
   }
}
            

As you can see from the example calling an XMLObject from a program is pretty similar to calling it from the command line. You simply create the XMLObjectService and then call invokeXMLObject() handing it the URI just like you did on the command line.

The result that you receive is a Resource that will either contain a string result or an XML result depending on the return type of the XMLObject. If the XMLObject returns it's result as anything other then a DOM Node or Document then the results are treated as a string and should only be retrieved with result.getOutput(). If the XMLObject you are calling returns a DOM Node or Document then you can cast the result to an XMLObject and use the getContentAsDOM and getContentAsSAX methods to retrieve the content as well.

We've now seen how to contruct XMLObjects, install them in the server and invoke them from the command line and our custom programs. Hopefully you see the value in these powerful server side extensions for dbXML and can use them when necessary to improve your applications.


[4] Since this object only has one method we could have still used the default method but for the sake of example we also want to see how to use other method names.

6. Database properties

In certain configurations it may be necessary to control certain aspects of the dbXML implementation of the XML:DB API. This control is provided through the setting of properties on the dbXML Database implementation. These properties allow you to control things such as which CORBA naming service is used.

The following properties are available.

  • dbxml.naming.ior - The location where the CORBA naming service IOR file can be located. This defaults to http://localhost:4080/NamingService.

  • dbxml.naming.orbproperty - The name of the property for the CORBA orb configuration that should be set to configure the location of the naming service. This defaults to InitRef.NameService. You only need to set this if you change the ORB you are using and that orb supports configuring the name service in this manner.

Setting of properties is done when you first create the Database instance and will generally only need to be done once.

String driver = "org.dbxml.client.xmldb.DatabaseImpl";
Class c = Class.forName(driver);

Database database = (Database) c.newInstance();
database.setProperty("dbxml.naming.ior",
   http://naming.mydomain.com:3000/NamingService);
DatabaseManager.registerDatabase(database);
col =
   DatabaseManager.getCollection("xmldb:dbxml:///db/addressbook");

Setting the property is a simple matter of calling setProperty() on the created Database instance.

NOTE: These properties are dbXML specific and won't apply to any other XML:DB implementations.

7. Address Book Example Application

The address book example is a simple servlet based application constructued using dbXML. For more information on this example look in the dbXML-Core/java/examples/addressbook directory.

TODO: Add more detail about building servlet applications.

8. Experimental Features

There are a couple features in dbXML that are definitely experimental. These features can be interesing to explore to see some things that could be useful in future versions of dbXML but they should not be considered complete or stable.

Auto Linking

dbXML provides a facility for automating relational links between managed documents called AutoLinking. This allows you to break out duplicate data into shared documents or include a dynamic element such as the output of a query or XMLObject invocation into an XML file stored in the repository.

AutoLinking is specified by adding special attributes to the XML tag defining the link. This works in a similar manner to Xlink from the W3C. The attributes must use the xmlns:db="http://www.dbxml.org/XMLObject" namespace. There are two attributes to be concerned with href and type.

db:href attribute: Defines the URI for the resource that this AutoLink should retrieve.

db:type attribute: Specifies how this link should be processed. The following mechanisms are available.

  1. replace - Replaces the linking element with the content of the href URI

  2. content - Replaces all child content of the linking element with the content of the href URI. The linking element is not replaced.

  3. append - Appends the content of the href URI to the content of the linking element.

  4. insert - Inserts the content of the href URI as the first child of the linking element.

8.1. Auto-Linking Using Replace

This example will replace the some-element element with the contents of the URI dbxml:///db/ocs/test/some-document.

<document>
   <some-element xmlns:db="http://www.dbxml.org/XMLObject"
         db:href="dbxml:///db/ocs/test/some-document"
         db:type="replace"/>
</document>
            

After expanding the link it will look something like this.

<document>
   <some-document>
   ...
   </some-document>
</document>
            

8.2. Auto-Linking Using Insert

The following link will insert the content of the document found at dbxml:///db/ocs/test/some-document as the first child of the element some-element.

<document>
   <some-element xmlns:db="http://www.dbxml.org/XMLObject"
         db:href="dbxml:///db/ocs/test/some-document"
         db:type="insert"/>
</document>
            

After linking the document will look like.

<document>
   <some-element xmlns:db="http://www.dbxml.org/XMLObject"
         db:href="dbxml:///db/ocs/test/some-document"
         db:type="insert">
      <some-document>
      ...
      </some-document>
   </some-element>
</document>