Xindice 1.1 Developer Guide
Introduction to Programming Xindice
Accessing the Server
The Xindice 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 Xindice Users Guide.
API's
Xindice currently offers three layers of APIs that can be used to develop applications.
- XML:DB XML Database API used to develop Xindice applications in Java. This is the primary API used to develop applications and it will be given the most coverage in this manual. Xindice provides two implementations of the XML:DB API. One is built on top of the Xindice XML-RPC API, and another embeds Xindice within the same JVM. Xindice 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.
- Xindice XML-RPC API used when accessing Xindice from a language other then Java. The XML-RPC API is built on top of the Core Server API. XML-RPC implementation is provided by Apache Web Services Project.
- Core Server API is the internal Java API of the core database engine. This API is used to build the XML-RPC API and embedded XML:DB API. 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 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://xmldb-org.sourceforge.net. Most programming examples in this manual will use the XML:DB API. The Xindice implementation of the API is a Core Level 1 implementation.
The Xindice server also exposes a XML-RPC API that is used to implement the XML:DB API. The XML-RPC API will mainly be of interest to those who want to access Xindice from a language other then Java. Any language that supports a XML-RPC should be able to utilize the services of the Xindice server via the XML-RPC API. This document does not cover development with the XML-RPC API as the XML:DB API is the preferred mechanism for developing Xindice applications. If you are developing applications in Java you can safely ignore the existence of this API. The XML-RPC API will be covered in a seperate document to be written at a later time.
The final API for Xindice is the Core Server API.
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. This is roughly equivalent to the functionality provided by JDBC or ODBC for providing access to relational databases. Xindice provides an implementation of the XML:DB API that also serves as the primary programming API for Xindice.
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 Xindice implementation of the API is a complete Core Level 1 implementation plus implementations of some of the optional services.
Required Core 1 services supported by Xindice include.
- XPathQueryService - Enables execution of XPath queries against the database.
Optional Core 1 services supported by Xindice 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 Xindice implementation also supports a few added services that are specific to Xindice. These services exist because the functionality is necessary to fully utilize all the capabilities provided by Xindice. However, they are proprietary to Xindice and will not function unchanged on other XML databases.
The following services are currently provided by Xindice 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 Xindice.
While this guide aims to provide some useful examples and to guide you in the process of getting to know how to program Xindice it is also useful to know that there is a good source of example code within the server it self. The Xindice 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 Xindice specific services that are included with the server. The source code for all the command line tools can be found in xindice/java/src/org/apache/xindice/tools/command.
Setting up Your Build Environment
Before you can build applications for Xindice you need to make sure you have your build environment properly setup. This mainly consists of making sure that you have the proper VM version and a properly configured CLASSPATH.
To build applications for Xindice you can use JDK 1.3 or 1.4. JDK 1.2 and below will not work. If you have more than one Java VM installed make sure that your JAVA_HOME environment variable and PATH environment variable both include the correct path.
Once you have your Java VM properly configured you need to add a few jar files to your CLASSPATH. The following list of jars are required and should be made available on your CLASSPATH. All required jars can be found in xindice/java/lib
- xindice.jar - contains the main Xindice classes that are used by the client API.
- xmldb-common.jar, xmldb-api.jar, xmldb-api-sdk.jar, xmldb-xupdate.jar - contain implementations of the XML:DB API and XUpdate API.
- xml-apis.jar - contains Java XML APIs.
- xerces.jar - contains the Xerces XML parser.
- xalan.jar - contains the Xalan XSLT engine.
- commons-logging.jar - contains the Jakarta Commons Logging package.
Preparing the Server For the Examples
Before we get to some example code, we need to do a little work to setup the server. Don't worry nothing hard.
First we need to make sure the addressbook collection exists. If you followed the install instructions completely you should have already created this, but if not you should do so now. To find out if the collection exists you can run:
xindice lc -c /db
If you don't see 'addressbook' listed in the result then you need to create the collection. To create it just run:
xindice ac -c /db -n addressbook
Now that we have the collection, we can add a few example documents so that we have something to play with. You can find the examples in your Xindice installation in the directory java/examples/guide/xml. Run these commands to add the documents.
cd $XINDICE_HOME/java/examples/guide/xml xindice ad -c /db/addressbook -f address1.xml -n address1 xindice ad -c /db/addressbook -f address2.xml -n address2
If you're on Windows you'll need to adjust the path in the cd command for your platform. Most of the examples in the manual will be written for UNIX but will work fine in Windows if you just replace / with \ and $XINDICE_HOME with %XINDICE_HOME%.
That wasn't so bad and now we're set to look at some example code.
Diving in With an Example Program
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 Xindice/java/examples/guide/src/org/apache/xindice/examples/Example1.java
package org.apache.xindice.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.apache.xindice.client.xmldb.DatabaseImpl"; Class c = Class.forName(driver); Database database = (Database) c.newInstance(); DatabaseManager.registerDatabase(database); String uri = "xmldb:xindice:///db/addressbook"; col = DatabaseManager.getCollection(uri); String xpath = "//person[fname='John']"; 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(); } } } }
Before diving into the gory detail of what this program is doing, let's run it and see what we get back.
If you have a binary build of Xindice the examples are already built and you can run this example by typing.
cd $XINDICE_HOME/java/examples/guide ./run org.apache.xindice.examples.Example1
If all goes well, you should see a result that looks something like this.
<?xml version="1.0"?> <person xmlns:src="http://xml.apache.org/xindice/Query" src:col="/db/addressbook" src:key="address1"> <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>
Now that we've seen the result, let's dive in and look at the code in detail. 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 Xindice we use org.apache.xindice.client.xmldb.DatabaseImpl for our driver and register it with the DatabaseManager
String driver = "org.apache.xindice.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.
String uri = "xmldb:xindice:///db/addressbook"; Collection col = DatabaseManager.getCollection(uri);
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, xindice: in the case of Xindice.
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 Xindice instance that you are trying to connect with. All Xindice 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 Xindice 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 Xindice. If you have more then one instance of Xindice 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.
xindice:///db/news xindice:///db2/news
These paths will switch between a Xindice 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 = "//person[fname='John']"; 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 Xindice 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 Xindice 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 Xindice 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 Xindice Remotely
By default Xindice assumes that the client and server are running on the same machine, and the server is running on port 8888. In most configurations this will not be the case so it will be necessary to include the hostname and port of the server where Xindice is running in your URIs. The port you use is the port that the servlet engine is listening on. The port setting configuration depends on the servlet engine you use. Xindice comes pre-configured with the Jetty servlet engine running on port 8888, so URL used by default is xmldb:xindice://localhost:8888. To access the collection /db/addressbook on host xml.apache.org port 8000 the URI would look something like this xmldb:xindice://xml.apache.org:8000/db/addressbook. All examples in this document assume that server uses default configuration.
If you are having problems accessing Xindice remotely this may be the result of the Xindice deployment in the non-standard servlet context name. Xindice assumes that the server will be deployed under /xindice servlet context. To do this, you just need to rename Xindice WAR file to xindice.war, and deploy this renamed WAR file. Alternatively, you need to specify system property xindice.xmlrpc.service-location, or set property service-location on the Database XML:DB object right after its creation.
Managing Documents
In this chapter we'll look at using the XML:DB API to manage documents within the Xindice 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 Xindice 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 Xindice 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/apache/xindice/tools/commands.
The collection we want to create will be named mycollection and will be a child of the root collection.
Creating a Collection
package org.apache.xindice.examples; import org.xmldb.api.base.*; import org.xmldb.api.modules.*; import org.xmldb.api.*; // For the Xindice specific CollectionManager service import org.apache.xindice.client.xmldb.services.*; import org.apache.xindice.xml.dom.*; public class CreateCollection { public static void main(String[] args) throws Exception { Collection col = null; try { String driver = "org.apache.xindice.client.xmldb.DatabaseImpl"; Class c = Class.forName(driver); Database database = (Database) c.newInstance(); DatabaseManager.registerDatabase(database); String uri = "xmldb:xindice:///db/"; col = DatabaseManager.getCollection(uri); String collectionName = "mycollection"; CollectionManager service = (CollectionManager) col.getService("CollectionManager", "1.0"); // Build up the Collection XML configuration. String collectionConfig = "<collection compressed=\"true\" " + " name=\"" + collectionName + "\">" + " <filer class=\"org.apache.xindice.core.filer.BTreeFiler\"/>" + "</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 Xindice 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.
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.
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.
Adding an XML File to the Database
package org.apache.xindice.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.apache.xindice.client.xmldb.DatabaseImpl"; Class c = Class.forName(driver); Database database = (Database) c.newInstance(); DatabaseManager.registerDatabase(database); col = DatabaseManager.getCollection("xmldb:xindice:///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, "XMLResource"); 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.
Retrieving an XML Document from the Database
package org.apache.xindice.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.apache.xindice.client.xmldb.DatabaseImpl"; Class c = Class.forName(driver); Database database = (Database) c.newInstance(); DatabaseManager.registerDatabase(database); col = DatabaseManager.getCollection("xmldb:xindice:///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(); } } } }
Deleting an XML Document from the Database
package org.apache.xindice.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.apache.xindice.client.xmldb.DatabaseImpl"; Class c = Class.forName(driver); Database database = (Database) c.newInstance(); DatabaseManager.registerDatabase(database); col = DatabaseManager.getCollection("xmldb:xindice:///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(); } } } }
Using XPath to Query the Database
Introduction
Xindice currently supports XPath as a query language. In many applications XPath is only applied at the document level but in Xindice 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 Xindice 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.
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 Xindice/java/examples/guide/src/org/apache/xindice/examples/Example1.java
package org.apache.xindice.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.apache.xindice.client.xmldb.DatabaseImpl"; Class c = Class.forName(driver); Database database = (Database) c.newInstance(); DatabaseManager.registerDatabase(database); col = DatabaseManager.getCollection("xmldb:xindice:///db/addressbook"); String xpath = "//person[fname='John']"; 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
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 Xindice 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.
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']
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.apache.xindice.client.xmldb.DatabaseImpl"; Class c = Class.forName(driver); Database database = (Database) c.newInstance(); DatabaseManager.registerDatabase(database); col = DatabaseManager.getCollection("xmldb:xindice:///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(); } } } }
Storing metadata
Xindice allows a developer to store metadata that is associated with any collection or document. Metadata is data that is associated with a document or collection but is not part of that document or collection. For example, a filesystem lists the last modified time of a file or directory. That last modified time is metadata about the file or directory.
Within Xindice, when metadata is turned on, each document and collection has a MetaData object associated with it. The MetaData object is composed of three sections and is represented in XML like this:
<meta> <system type="doc"> <attr name="created" value="10128378882" /> <attr name="modified" value="10128378882" /> </system> <attrs> <attr name="key" value="value"/> <attr name="foo" value="bar"/> </attrs> <custom> <myspecial> any <valid /> xml </myspecial> </custom> </meta>
The first section is enclosed by the <system> element and is controlled by the core of Xindice. It currently tracks the type of resource referenced, creation time and last modification time of the resource. The <system> element has an attribute "type" which is either "doc" for document or "col" for collection. Within the <system> element are <attr> elements. Each <attr> element has two attributes, name and value. The creation and modification times are stored in individual <attr> elements. The value of these attributes are recorded as milliseconds since midnight Jan 1, 1970 (just as System.currentTimeMills() ).
The second section is a map of key-value pairs and is enclosed by the <attrs> element. Each key-value pair is represented in a single <attr> element. The key-value pairs are completely controlled by the user. You can add or remove any key-value pair in this section. Note that the map is from Object to Object, so you can store any object your application requires.
The third section is a custom XML document. This too is completely controlled by the user. The only prerequisite is that the document stored is well-formed XML.
Enabling metadata
To turn on metadata storage in Xindice, edit the system.xml configuration file and set the "use-metadata" attribute of the root-collection element to "on". Restart your container, and metadata storage will be enabled.
If properly configured, when you restart your container, you will see a log message that looks like this:
Dec 30, 2002 10:21:51 AM org.apache.xindice.core.Database setConfig INFO: Meta information initialized
Using metadata
How you use metadata is really up to you, the application developer. There are many potential uses depending on your application. One example is a content management system in which each document has a series of states through which it must pass. The states might be controlled by a finite state machine, but each document's current state would be stored in the metadata associated with it.
Sample metadata code
Currently the metadata accessors are implemented as non-standard meta information service which can be obtained via XML:DB API (name "MetaService", version "1.0"), and it also available via the XML-RPC code.
The Xindice XML-RPC server has four metadata related methods: GetCollectionMeta, GetDocumentMeta, SetCollectionMeta, SetDocumentMeta. Here's quick perl script which creates a collection and then dumps out its metadata contents, and then sets some values inside the metadata.
#!/usr/local/bin/perl -w use strict; use Frontier::Client; use Data::Dumper; my($server,$result,$url); $url = 'http://localhost:8888/xindice'; $server = Frontier::Client->new('url'=>$url,'debug'=>0); ## try listing the collections ## look at org/apache/xindice/server/rpc/messages/*.java ## for the 'message' possibilities. The parameters are there as well. my $colname = 'ninemetatest'; my $args = {}; #first, add the collection directly under the /db root. $args->{'message'} = 'CreateCollection'; $args->{'collection'} = '/db'; $args->{'name'} = $colname; $result = $server->call('run',$args); ## now get the collection's meta $args = {}; $args->{'message'} = 'GetCollectionMeta'; $args->{'collection'} = '/db/'.$colname; $result = $server->call('run',$args); ## this should print out the xml for the ## collection's metadata. print Dumper($result); ## now add some stuff to the metadata my $meta=<<EOF; <?xml version="1.0"?> <meta> <!-- since the system is controlled by Xindice, this doesn't matter... --> <system type="col"> <attr name="created" value="1" /> <attr name="modified" value="5" /> </system> <attrs> <attr name="test" value="added" /> </attrs> <custom> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <test>the rdf example</test> </rdf:RDF> </custom> </meta> EOF $args = {}; $args->{'message'} = 'SetCollectionMeta'; $args->{'collection'} = '/db/'.$colname; $args->{'meta'} = $meta; $result = $server->call('run',$args); ## this should print out the xml for the ## collection's revised metadata. print Dumper($result);
Example Application
Address Book
The address book example is a simple servlet based application constructued using Xindice. For more information on this example look in the Xindice/java/examples/addressbook directory.
TODO: Add more detail about building servlet applications.
Experimental Features
There are a couple of features in Xindice that are definitely experimental. These features can be interesing to explore to see some things that could be useful in future versions of Xindice but they should not be considered complete or stable.
by Kimbro Staken, Dave Viner
version 598114