Prerequisites


The XML:DB initiative has defined an abstract API for interacting with an NXD. This API has two parts. A programmatic API lets programs talk to the database. This API serves the same function as the JDBC API: It provides a vendor-neutral API for applications that need to use a database. The initiative also defined an XML vocabulary, XUpdate, for describing updates to be performed on XML data in an NXD. Both the XML:DB API and XUpdate rely on XPath. If you aren’t familiar with XPath (and you ought to be, because it’s so useful), you should skip back to Chapter 2 on Xalan and XSLT; that chapter includes a brief overview of XPath syntax and functionality.

XML:DB

The XML:DB API is broken into a series of modules. These modules are combined to form Core Levels of the API. Because the NXD area is new, and because the required functionality is changing, the authors of the XML:DB API felt that breaking the API into modules was the best way to allow flexibility while providing a guarantee of a particular level of database functionality for a given Core Level. At the moment there are two Core Levels, 0 and 1.

click to expand

This figure gives an overview of the interfaces defined by XML:DB. The API is designed to be platform neutral, so it’s specified using CORBA IDL types. For our discussion, we’ll look at the Java bindings, but you should know that the intent is for the same API to be used across multiple languages. There are two modules in Core Level 0: API Base and XMLResource.

Core Level 0

The API Base module contains the Configurable, Database, Collection, Resource, ResourceSet, ResourceIterator, and Service interfaces (shown in blue in the previous figure). The XMLResource module contains the XMLResource interface (shown in red). All the interfaces in the API throw XMLDBException to indicate errors. XMLDBException wraps integer error codes in ErrorCodes.

Database

Let’s look at the interfaces in the API Base module. The Database interface is an abstraction of the NXD you want to use. The specification doesn’t specify how you obtain an object that implements Database, so this aspect of using XML:DB is vendor specific and nonportable. Database extends the Configurable interface, which is patterned on the SAX2 Configurable interface, but differs from the SAX version because property values can only be Strings, not Objects. The major functionality of Database is to give you a Collection you can work on. Collections are identified with URIs and can be access-controlled via username and password authentication. Here are the most important methods on Database:

  • Collection getCollection(String uri, String username, String password)—Returns a Collection instance using the uri parameter as a key. Username and password are checked if they aren’t null.

  • boolean acceptsURI(String uri)—Returns true if this database contains a collection for the uri.

  • String getName()—Returns the name of the database instance.

  • String getProperty(String name)—Gets the value of the configuration property name.

  • void setProperty(String name, String value)—Sets the value of the configuration property name to value.

You’ll notice that there are no methods to create/delete collections. These operations must be performed using a collection manager service. We’ll talk more about services in a bit.

DatabaseManager

In the Java implementation of XML:DB, the DatabaseManager class is the glue between the XML:DB interfaces and the database vendors’ implementation of those interfaces. You need to create a DatabaseManager in order to be able to do anything useful with an NXD and the XML:DB APIs. All the methods of DatabaseManager are static:

  • Database[] getDatabases()—Returns a list of all available databases.

  • void registerDatabase(Database db)—Registers a new database implementation instance with the Database Manager. This implementation instance is obtained by using reflection to create an instance of a vendor-supplied database implementation class.

  • void deregisterDatabase(Database db)—Unregisters the database implementation instance. Once deregistered, the implementation instance can no longer handle requests.

  • Collection getCollection(String uri)—Retrieves the Collection instance corresponding to the URI. The URI has the format xmldb:<vendor-id>://<host>:<port>/path/to/collection. The <host> and <port> values are optional. The colon between <host> and <port> is needed only if <port> is supplied.

  • Collection getCollection(String uri, String username, String password)—Retrieves the Collection instance corresponding to the URI. The URI has the format xmldb:<vendor-id>://<host>:<port>/path/to/collection. The <host> and <port> values are optional. The colon between <host> and <port> is needed only if <port> is supplied. If username and password are supplied (not null), they must be correct.

Collection

Resources in the database are stored in collections, represented by the Collection interface. Collection is where you’ll get access to most of the functionality of an NXD. All the methods for creating, retrieving/querying, updating, and deleting resources are found on Collection. Database vendors can implement the contents of the database as a hierarchy of collections, but this isn’t required (although it may impact your ability to retrieve data, especially if queries can’t traverse child collections). Some of the key methods on Collection are as follows:

  • String getName()—Returns the name of the collection.

  • String createId()—Creates a unique identifier (within the collection). You need this to create a resource.

  • Resource createResource(String id, String type)—Creates an empty resource with the id. The valid values for type are "XMLResource" and "BinaryResource". If the id is null, a new ID is created by calling createId.

  • Resource getResource(String id)—Retrieves the resource associated with the id.

  • int getResourceCount()—Returns the number of resources in the collection.

  • String[] listResources()—Returns a list of all the resource IDs for all the resources in the collection.

  • void removeResource(Resource res)—Removes the resource.

  • void storeResource(Resource res)—Stores the resource. If the resource doesn’t exist yet, it’s created; if it already exists, it’s updated.

  • boolean isOpen()—Returns true if the collection is open.

  • Service getService(String name, String version)—Returns the service named name with version version.

  • Service[] getServices()—Returns a list of the names of all the available services.

  • Collection getParentCollection()—If the database supports hierarchical collections, returns the parent collection of this collection or null if there is no parent.

  • Collection getChildCollection(String name)—Gets the child connection named name.

  • int getChildCollectionCount()—Returns the number of child collections for this collection.

  • String[] listChildCollections()—Returns a list of the names of all child collections for this collection.

Resource

All resources have a String identifier and a type. Resources are the actual containers for data stored within a database. You need a concrete implementation of the Resource interface to do anything useful. The interface identifies a common set of operations:

  • String getId()—Returns the String used to identify the Resource.

  • String getResourceType()—Returns the String that tells what type the Resource is. The valid values are "XMLResource" and "BinaryResource".

  • Collection getParentCollection()—Returns the collection that contains this resource.

  • Object getContent()—Returns the content of the resource as an Object.

  • void setContent(Object o)—Sets the content of the resource to be the Object.

XMLResource

XMLResource is a specialization of the Resource interface. It adds operations that allow you to put XML data into the resource and get it back out again. The methods of XMLResource don’t deal with XML documents, but rather with either a SAX event stream or a DOM tree:

  • boolean getSAXFeature(String feature)—Gets the value of a SAX2 configurable feature on the underlying SAX processor.

  • void setSAXFeature(String feature, boolean value)—Sets the value of a SAX2 configurable feature on the underlying SAX processor. This is particularly important so you can turn on validation.

  • void getContentAsSAX(ContentHandler handler)—Retrieves the XML data in the resource from the database and feeds it to the SAX ContentHandler handler.

  • ContentHandler setContentAsSAX()—Asks the database to provide a SAX ContentHandler you can give to your application. As your application fires SAX events, they are detected by the database and stored.

  • Node getContentAsDOM()—Returns a W3C DOM node that’s the root of the XML data.

  • void setContentAsDOM(Node content)—Sets the stored XML data to be the W3C DOM tree rooted at content.

  • String getDocumentId()—If this document has a parent document (Resource), returns its ID; otherwise, returns null.

BinaryResource

BinaryResource is the other specialization of the Resource interface. It allows you to store data as a byte array. It adds no methods to the Resource interface. Instead, it specifies that the getContent and setContent methods are working on byte[] as opposed to Object. When you’re working with a BinaryResource, you should be able to safely downcast the Object arguments and return values of setContent and getContent to byte[].

ResourceSet

We haven’t talked about queries yet; we’ll get to them after we introduce services. But the XML:DB Core Level 0 does include interfaces for dealing with query results. In XML:DB, queries return ResourceSets, which are sets of resources. The name of this interface should really be ResourceList, because the access model for the contents of the ResourceSet is integer indices. This, of course, means that order within the set is important. The other major feature of ResourceSets is that they can produce a ResourceIterator, which you can use to process the contents of the set. Here are the methods on ResourceSet:

  • Resource getResource(long index)—Gets the Resource at index.

  • void removeResource(long index)—Removes the Resource at index.

  • long getSize()—Returns the number of Resources in the set. This may cause all Resources in the set to be retrieved, depending on the implementation.

  • void addResource(Resource res)—Adds res to the set.

  • void clear()—Removes all Resources from the set.

  • ResourceIterator getIterator()—Returns a ResourceIterator that produces every Resource in the set.

  • Resource getMembersAsResource()—Return an XMLResource containing a representation of all Resources in the set.

ResourceIterator

A ResourceIterator implements the Iterator design pattern. The interface defines Resource-specific methods in the general form of the Java2 collections iterators. We believe the authors of XML:DB wanted to spare the clients of ResourceIterator from having to cast from Object to Resource, so they created a type-specific interface. Unfortunately, it’s one more piece of information that you need to keep around and be aware of. Fortunately, the interface is short and sweet:

  • boolean hasMoreResources()—Returns true if there are more Resources left in the iterator.

  • Resource nextResource()—Returns the next available resource.

Service

The last interface in the API Base is Service, which provides an extension mechanism for collection implementations. Implementations can provide as many or as few services as they wish. The higher levels of the XML:DB Core APIs define some services, and vendors are free to define their own. The Service interface provides a generic interface that all services should provide:

  • String getName()—Returns the name of the service.

  • String getVersion()—Returns the version of the service.

  • void setCollection(Collection c)—Sets the collection that the service will operate on.

Services also implement the Configurable interface, allowing for customization.

Core Level 1

XML:DB Core Level 1 contains all the functionality in Core Level 0 and adds the XPathQueryService interface.

XPathQueryService

The XPathQueryService provides a query capability for XML:DB. Because it’s defined as a service, there’s room for multiple query capabilities, each implemented as a separate service. You can imagine a more SQL-like query interface, or an XQuery based interface, when the W3C finalizes XQuery as a recommendation. At the moment, though, XPathQueryService is the only query capability for XML:DB-compliant databases.

XPathQueryService gets all of its expressive power from the XPath expression language. In Chapter 2, you saw how XSLT uses XPath to select portions of an XML document. That style of usage is supported by XPathQueryService. One issue with XPath queries is that they rely on namespace prefixes to select nodes belonging to namespaces. XPathQueryService provides a number of methods to set up a group of namespace prefix bindings that are used as the context for any XPath queries executed by the service:

  • void clearNamespaces()—Clears the current set of namespace bindings.

  • String getNamespace(String prefix)—Returns the URI associated with the namespace prefix.

  • void removeNamespace(String prefix)—Removes the namespace prefix from the current set of namespace bindings.

  • void setNamespace(String prefix, String uri)—Adds a namespace binding for prefix and uri to the current set of namespace bindings.

  • ResourceSet query(String query)—Executes the XPath query on the Collection, using the current set of namespace bindings, and returns the results as a ResourceSet.

  • ResourceSet queryResource(String id, String Query)—Executes the XPath query on the resource with identifier id, using the current set of namespace bindings, and returns the results as a ResourceSet.

Other Services

At the moment, only Core Levels 0 and 1 are defined by XML:DB. However, the XML:DB initiative has defined a few other services, although they aren’t assigned to any level in the Core API. These services provide facilities for updating and managing collections and for transaction management.

XUpdateQueryService

The XUpdateQueryService allows you to execute XUpdate commands against a collection or a resource in a collection. We’ll cover the details of XUpdate in the next section. For now, you need to know that XUpdate commands take the form of an XML document:

  • long update(String document)—Executes an XUpdate document on the collection. All updated documents should be written back to the database. Returns the number of documents updated.

  • long updateResource(String id, String document)—Executes an XUpdate document against the resource with identifier id. All updated documents should be written back to the database. Returna the number of documents updated.

CollectionManagementService

The CollectionManagementService provides the facilities you need to create and remove collections:

  • Collection createCollection(String name)—Creates a collection named name.

  • void removeCollection(String name)—Removes the collection named name.

TransactionService

XML:DB defines a TransactionService to allow basic transaction capabilities. These capabilities allow you to mark transaction boundaries and control transaction commit and abort:

  • void begin()—Marks the beginning of a new transaction.

  • void commit()—Commits the results of the current transaction.

  • void rollback()—Aborts the current transaction and rolls back any updates performed because the begin method was called.

The current release of Xindice doesn’t support the transaction service, but it does support all the other API’s that we’ve discussed in this section.

XUpdate

The XML:DB initiative has defined XUpdate, an XML vocabulary that specifies how to update all or part of an XML document. In the context of an XML:DB database, XUpdate documents are used as commands to the XUpdateQueryService; but XUpdate is useful any time you need to describe an update to an XML document. XUpdate relies on XPath for selecting the parts of the document to be updated and uses XPath expressions for conditional processing of updates. XUpdate is broken into two pieces: a set of elements for describing the contents of the update operation and a set of attributes for describing the kind of update operation that should be performed. For the examples in this section, we’ll return to the simple inventory of books (books.xml):

  1: <?xml version="1.0" encoding="UTF-8"?>   2: <books xmlns="http://sauria.com/schemas/apache-xml-book/books"   3:   xmlns:tns="http://sauria.com/schemas/apache-xml-book/books"    4:   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    5:   xsi:schemaLocation=   6:     "http://sauria.com/schemas/apache-xml-book/books   7:      http://www.sauria.com/schemas/apache-xml-book/books.xsd"   8:   version="1.0">   9:   <book>  10:    <title>Professional XML Development with Apache Tools</title>  11:    <author>Theodore W. Leung</author>  12:    <isbn>0-7645-4355-5</isbn>  13:    <month>December</month>  14:    <year>2003</year>  15:    <publisher>Wrox</publisher>  16:    <address>Indianapolis, Indiana</address>  17:   </book>  18:   <book>  19:    <title>Effective Java</title>  20:    <author>Joshua Bloch</author>  21:    <isbn>0-201-31005-8</isbn>  22:    <month>August</month>  23:    <year>2001</year>  24:    <publisher>Addison-Wesley</publisher>  25:    <address>New York, New York</address>  26:   </book>  27:   <book>  28:    <title>Design Patterns</title>  29:    <author>Erich Gamma</author>  30:    <author>Richard Helm</author>  31:    <author>Ralph Johnson</author>  32:    <author>John Vlissides</author>  33:    <isbn>0-201-63361-2</isbn>  34:    <month>October</month>  35:    <year>1994</year>  36:    <publisher>Addison-Wesley</publisher>  37:    <address>Reading, Massachusetts</address>  38:   </book>  39: </books>

Update Contents

XUpdate provides elements for creating elements, attributes, and processing constructions with computed names. It also provides elements for creating text and comments, so that all the major components of XML documents can be created and used for updates:

  • <xupdate:element name="elementName">elementContent</xupdate:element>—<xupdate:element> allows you to create an element that can be used as part of an update. To make it more convenient to work with elements, the content of <xupdate:element> can be elements and text. Here’s an example:

      1: <xupdate:element name="book">   2:  <title>Refactoring</title>   3:  <author>Martin Fowler</author>   4:  <isbn>0-201-48567-2</isbn>   5:  <month>September</month>   6:  <year>2000</year>   7:  <publisher>Addison-Wesley</publisher>   8:  <address>Reading, Massachusetts</address>   9:  <xupdate:comment>Added 8/2003</xupdate:comment>  10: </xupdate:element>

    This creates a new book element that you can use to update your book inventory. That book element looks like this:

      1: <book>   2:  <title>Refactoring</title>   3:  <author>Martin Fowler</author>   4:  <isbn>0-201-48567-2</isbn>   5:  <month>September</month>   6:  <year>2000</year>   7:  <publisher>Addison-Wesley</publisher>   8:  <address>Reading, Massachusetts</address>   9:  <!-- Added 8/2003 -->  10: </book>
  • <xupdate:attribute name="attributeName">attribute value</xupdate:attribute>—In a similar fashion, <xupdate:attribute> allows you to create an attribute. This example

      1: <xupdate:element name="printing">   2:  <xupdate:attribute name="year">   3:   1996   4:  </update:attribute>   5:  <xupdate:text>5</xupdate:text>   6: </xupdate:element>

    produces an element that looks like this:

      1: <printing year="1996>5</printing>
  • <xupdate:processing-instruction name="piName">instructions</xupdate:processing-instruction>—<xupdate:processing instruction> creates a processing instruction named piName. The arguments to the processing instruction come from the content of the <xupdate:processing-instruction> element. This example

      1: <xupdate:processing-instruction name="xsl-stylesheet">   2:  type="text/xsl" href="books.xslt"   3: </xupdate:processing-instruction>

    produces a processing instruction that looks like this:

      1: <?xsl-stylesheet type="text/xsl" href="books.xslt" ?>
    • <xupdate:text>text</xupdate:text>—<xupdate:text> inserts text content into the update under construction.

    • <xupdate:comment>comment text</xupdate:comment>—<xupdate:comment> inserts a comment into the update under construction.

Update Operations

Now that you can construct fragments of XML documents, you can use those fragments to update the contents of a document in the database. An XUpdate update consists of an <xupdate:modifications> element that contains one or more of the following update command elements:

  • <xupdate:insert-before select="xpath-expression">update</xupdate:insert-before>—Inserts the update elements before the node selected by the select xpath-expression. In XPath terms, the inserted node becomes the preceding sibling of the selected node:

    <xupdate:insert-before select="tns:books/tns:book[1]">  <xupdate:processing-instruction name="xsl-stylesheet">   type="text/xsl" href="books.xslt"  </xupdate:processing-instruction> </xupdate:insert-before>
  • <xupdate:insert-after select="xpath-expression">update</xupdate:insert-after>—Inserts the update elements after the node selected by the select xpath-expression. In XPath terms, the inserted node becomes the following sibling of the selected node:

    <xupdate:insert-after   select='/tns:books/tns:book[tns:title="Effective Java"]'> <xupdate:element name="book">   <title>Refactoring</title>   <author>Martin Fowler</author>   <isbn>0-201-48567-2</isbn>   <month>September</month>   <year>2000</year>   <publisher>Addison-Wesley</publisher>   <address>Reading, Massachusetts</address>   <xupdate:comment>Added 8/2003</xupdate:comment>  </xupdate:element> </xupdate:insert-after>
  • <xupdate:append select="xpath-expression" child ="child- xpath-expression">update</xupdate:append>—The select xpath-expression selects a node that’s used as the parent of the node to be appended. The child attribute is optional. If it’s provided, it specifies the integer position of the new child node. If it’s omitted, the new child is appended as the last child of the selected node:

    <xupdate:append select="/tns:books">  <xupdate:element name="book">   <title>Refactoring</title>   <author>Martin Fowler</author>   <isbn>0-201-48567-2</isbn>   <month>September</month>   <year>2000</year>   <publisher>Addison-Wesley</publisher>   <address>Reading, Massachusetts</address>   <xupdate:comment>Added 8/2003</xupdate:comment>  </xupdate:element> </xupdate:append>
  • <xupdate:update select="xpath-expression">update</xupdate:update>—Updates/replaces the node selected by the select xpath-expression with the update:

    <xupdate:update   select='/tns:books/tns:book/tns:author[text()="Ted Leung"]'>  Theodore W. Leung </xupdate:update>

  • <xupdate:remove select="xpath-expression"/>—Removes the node selected by the select xpath-expression:

    <xupdate:remove select='//tns:book[tns:year="2001"]'/>
  • <xupdate:rename select="xpath-expression">name</xupdate:rename>—Renames the node (element or attribute node) selected by the select xpath-expression to name:

    <xupdate:rename select='/tns:book'>book-inventory</xupdate:rename> 

  • <xupdate:variable name="var" select="xpath-expression"/>—Defines a variable var whose value is the node selected by the select xpath-expression:

    <xupdate:variable name="twlbook"   select='//tns:book[tns:author="Ted Leung"]'/>
  • <xupdate:value-of select="xpath-expression"/>—Replaces the <xupdate:value-of> element with the value selected by the select xpath-expression:

    <xupdate:value-of select="$twlbook"/> <xupdate:value-of select='//tns:book[tns:author="Ted Leung"]'/>

Let’s look at a few examples of how this works. We’ll take the earlier book inventory and apply a few XUpdates to it to see what we get. Apply this insert-before:

  1: <?xml version="1.0"?>   2: <xupdate:modifications version="1.0"   3:  xmlns:xupdate="http://www.xmldb.org/xupdate">   4:  <xupdate:insert-after   5:    select='/tns:books/tns:book[tns:title="Effective Java"]'>   6:   <xupdate:element name="book">   7:    <title>Refactoring</title>   8:    <author>Martin Fowler</author>   9:    <isbn>0-201-48567-2</isbn>  10:    <month>September</month>  11:    <year>2000</year>  12:    <publisher>Addison-Wesley</publisher>  13:    <address>Reading, Massachusetts</address>  14:    <xupdate:comment>Added 8/2003</xupdate:comment>  15:   </xupdate:element>  16:  </xupdate:insert-after>  17: </xupdate:modifications>

Your document looks like this:

  1: <?xml version="1.0"?>   2: <books xmlns="http://sauria.com/schemas/apache-xml-book/books"   3:  xmlns:tns="http://sauria.com/schemas/apache-xml-book/books"   4:  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   5:  xsi:schemaLocation="   6:    http://sauria.com/schemas/apache-xml-book/books   7:    http://www.sauria.com/schemas/apache-xml-book/books.xsd"   8:  version="1.0">   9:   <book>  10:    <title>Professional XML Development with Apache Tools</title>  11:    <author>Theodore W. Leung</author>  12:    <isbn>0-7645-4355-5</isbn>  13:    <month>December</month>  14:    <year>2003</year>  15:    <publisher>Wrox</publisher>  16:    <address>Indianapolis, Indiana</address>  17:   </book>  18:   <book>  19:    <title>Effective Java</title>  20:    <author>Joshua Bloch</author>  21:    <isbn>0-201-31005-8</isbn>  22:    <month>August</month>  23:    <year>2001</year>  24:    <publisher>Addison-Wesley</publisher>  25:    <address>New York, New York</address>  26:   </book>  27:   <book>  28:    <title>Refactoring</title>  29:    <author>Martin Fowler</author>  30:    <isbn>0-201-48567-2</isbn>  31:    <month>September</month>  32:    <year>2000</year>  33:    <publisher>Addison-Wesley</publisher>  34:    <address>Reading, Massachusetts</address>  35:    <!--Added 8/2003-->  36:   </book>   37:   <book>  38:    <title>Design Patterns</title>  39:    <author>Erich Gamma</author>  40:    <author>Richard Helm</author>  41:    <author>Ralph Johnson</author>  42:    <author>John Vlissides</author>  43:    <isbn>0-201-63361-2</isbn>  44:    <month>October</month>  45:    <year>1994</year>  46:    <publisher>Addison-Wesley</publisher>  47:    <address>Reading, Massachusetts</address>  48:   </book>  49: </books>

Here is an XUpdate document which renames the books element to book-inventory.

  1: <?xml version="1.0"?>   2: <xupdate:modifications version="1.0"   3:   xmlns:xupdate="http://www.xmldb.org/xupdate">   4:  <xupdate:rename select="/tns:books">book-inventory</xupdate:rename>   5: </xupdate:modifications>

The updated document looks like this:

  1: <?xml version="1.0"?>   2: <book-inventory   3:  xmlns="http://sauria.com/schemas/apache-xml-book/books"   4:  xmlns:tns="http://sauria.com/schemas/apache-xml-book/books"   5:  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   6:  xsi:schemaLocation="   7:    http://sauria.com/schemas/apache-xml-book/books   8:    http://www.sauria.com/schemas/apache-xml-book/books.xsd"   9:  version="1.0">  10:   <book>  11:    <title>Professional XML Development with Apache Tools</title>  12:    <author>Theodore W. Leung</author>  13:    <isbn>0-7645-4355-5</isbn>  14:    <month>December</month>  15:    <year>2003</year>  16:    <publisher>Wrox</publisher>  17:    <address>Indianapolis, Indiana</address>  18:   </book>  19:   <book>  20:    <title>Effective Java</title>  21:    <author>Joshua Bloch</author>  22:    <isbn>0-201-31005-8</isbn>  23:    <month>August</month>  24:    <year>2001</year>  25:    <publisher>Addison-Wesley</publisher>  26:    <address>New York, New York</address>  27:   </book>  28:   <book>  29:    <title>Refactoring</title>  30:    <author>Martin Fowler</author>  31:    <isbn>0-201-48567-2</isbn>  32:    <month>September</month>  33:    <year>2000</year>  34:    <publisher>Addison-Wesley</publisher>  35:    <address>Reading, Massachusetts</address>  36:    <!--Added 8/2003-->  37:   </book>   38:   <book>  39:    <title>Design Patterns</title>  40:    <author>Erich Gamma</author>  41:    <author>Richard Helm</author>  42:    <author>Ralph Johnson</author>  43:    <author>John Vlissides</author>  44:    <isbn>0-201-63361-2</isbn>  45:    <month>October</month>  46:    <year>1994</year>  47:    <publisher>Addison-Wesley</publisher>  48:    <address>Reading, Massachusetts</address>  49:   </book>  50: </book-inventory>




Professional XML Development with Apache Tools. Xerces, Xalan, FOP, Cocoon, Axis, Xindice
Professional XML Development with Apache Tools: Xerces, Xalan, FOP, Cocoon, Axis, Xindice (Wrox Professional Guides)
ISBN: 0764543555
EAN: 2147483647
Year: 2003
Pages: 95

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net