# DirXML™ Native Driver Frequently-Asked-Questions (FAQ)

## Contents

Where do I start?
What is a native driver?
How does a native driver differ from a Java driver?
When should I write a native driver instead of a Java driver?
What are the native driver interfaces?

Character strings and the interfaces
Interfaces in C++
Namespaces
Interfaces implemented by the driver
The CreateDriver function
Interfaces implemented by DirXML

What is the life-cycle of a native driver?

Creating the driver
Destroying the driver

How do I use the XmlDocument interface?

Getting the XML document as a DOM tree
Getting the XML document as SAX events
Getting the XML document as a byte array

How do I create an XML document?

Creating the XML document from a DOM tree
Creating the XML document from SAX events
Creating the XML document from a byte array

What about Memory Management?
Are there any utility functions available?

Interface factories and destructors
Encoding support
XDS Support

With which libraries do I link my native driver?
How do I debug a native driver?

Win32
NetWare

## Where do I start?

If you have not already, read the general DirXML FAQ. The DirXML FAQ covers items of general import to driver writers and introduces the interfaces that DirXML uses to communicate with a driver. The C++ interfaces that DirXML uses are the same as the Java interfaces except for language-specific items such as pointers and memory management. This document focuses on items specifically of interest to driver writers who wish to use C++ for their drivers.

## What is a native driver?

A native driver is a DirXML driver that is written in C++ as opposed to Java. As such, it runs using native, compiled code instead of Java byte-code.

## How does a native driver differ from a Java driver?

The primary differences between a native driver and a Java driver are the native interfaces' use of pointers and the need to dispose of XML documents that the native driver creates. In addition, a native driver can more easily cause DirXML and NDS to crash because the native driver runs in the NDS process space and DirXML does not have the benefit of the Java Virtual Machine to insulate DirXML and NDS from an errant driver.

Note, however, that DirXML does take some precautions when calling into a native driver: All calls into the native driver are protected by a try-catch block that catches all exceptions. If an exception is caught by this try-catch block (e.g., the driver didn't catch it), the driver is shut down. On Win32 this works well. On NetWare however, it does not work at all. At the time of this writing it is unknown how well this works on the Unix platforms. This mechanism cannot, however, protect against a native driver writing over process memory.

Future versions of DirXML will run drivers in a separate address space from NDS, but in the meantime, native driver writers must be very careful to verify that their code functions properly.

## When should I write a native driver instead of a Java driver?

In general, if the target application for the driver supports a Java interface, or a remote interface that can be accessed from Java (e.g., an IP interface accessible with sockets) then the driver should be written in pure Java. Writing the driver in pure Java means that the driver should be able to run unchanged on any platform on which DirXML runs.

However, for many applications the only interface available is through native library calls on the application platform (e.g., an interface .dll is provided). For such cases it is typically far more convenient to write the driver in C++ than it is to write the driver in Java with a JNI layer to access the native library.

## What are the native driver interfaces?

The native driver interfaces are declared in the following files (the actual header files are found in the DirXML SDK):

- NativeInterface.h - Defines the primary interfaces implemented by the native driver, as well as the mechanism used to pass the XML command and event notification documents.
- dom.h - Defines a C++ language binding for the Document Object Model (DOM) 1.0 interfaces.
- sax.h - Defines a C++ language binding for the Simple Api for Xml (SAX) 1.0 interfaces
- OutputStream.h - Defines a java-like stream interface used with several other interfaces.
- XMLWriter.h - Defines an interface returned from XmlDocument used to control the serialization of XML.
- DriverFilter.h - Defines an interface provided so that drivers can easily use the publisher and subscriber channel Event Filters.
- Trace.h - Defines an interface used for drivers to write debugging or informational messages to the DSTrace screen.

The native driver primary interfaces are very similar to the Java interfaces. For detailed discussion of each interface see the relevant section in the general FAQ. The native driver interfaces are defined using macros which expand into C++ class declarations.

### Character strings and the interfaces

Virtually all the characters strings used by the DirXML interfaces are null-terminated UTF-16 encoded character strings. The underlying primitive type is an unsigned short, which is typedef'd to the identifier unicode. The only char strings used are in some auxiliary interfaces and functions that deal with filenames.

### Interfaces in C++

If a C++ file is being compiled the interface and its methods appear as a virtual base class. For example, the following code references the XmlDocument::getDocument() interface method in C++:

         DOM::Document document = inputDoc->getDocument();

### Namespaces

In several DirXML header files you will see a construct similar to:

         NAMESPACE(DOM)Element * element

The "NAMESPACE(DOM)" macro expands to "DOM::" when compiling C++ on most platforms. If you are writing a C++ driver it is typically easier to use a "using namespace" directive at the top of your source file than using the NAMESPACE macro.

### Interfaces implemented by the driver

- DriverShim - The top-level interface responsible for starting up and shutting down the driver.
- SubscriptionShim - The interface responsible for accepting and processing commands from DirXML.
- PublicationShim - The interface responsible for notifying DirXML of events that occur in the application.
- XmlQueryProcessor - The interace passed by the PublicationShim into XmlCommandProcessor::execute() when calling DirXML to report an application event. DirXML uses the XmlQueryProcessor interface to query the PublicationShim for any additional information DirXML may need to process the application event.

### The CreateDriver function

In addition to the above interfaces your native driver implementation module (dll, nlm, or shared library) must export a single function called CreateDriver. This entry point is called by DirXML to get the DriverShim interface pointer. The CreateDriver entry point takes no parameters and returns a pointer to DriverShim. On Win32 platforms the function may be exported as "CreateDriver", "_CreateDriver", or "_CreateDriver@0" due to differences in compiler name generation. On NetWare the function name must have the uppercase name of the NLM appended to it (e.g., CreateDriverCOOLDRVR if the driver is implemented in COOLDRVR.NLM).

### Interfaces implemented by DirXML

- XmlCommandProcessor - The interface passed to PublicationShim::start() so that the PublicationShim can notify DirXML of application events.
- XmlQueryProcessor - The interface passed by DirXML to SubscriptionShim::execute so that the SubscriptionShim can query DirXML for any additional information it may need to process a command.

## What is the life-cycle of a native driver?

The life-cycle of a native driver is very similar to the life-cycle of a Java driver. The differences are in how the driver is created and in the fact that DirXML notifies the driver when it is no longer needed so that the driver can perform resource cleanup (such notification is not needed in Java since the JVM handles deallocation, etc.).

### Creating the driver

To create a native driver DirXML loads the driver's implementation module (dll, nlm, or shared library) and calls the exported CreateDriver function. The CreateDriver function creates an instance of the native driver object that implements DriverShim and returns a pointer to the object. DirXML then uses the DriverShim methods exactly as described in the general FAQ.

### Destroying the driver

After DirXML has called DriverShim::shutdown() and after the Publisher thread has returned from PublicationShim::start() DirXML calls the DriverShim::destroy() method. At this point it is safe for the driver to cleanup any resources used and it is safe to delete the driver object. In fact, the most common way of implementing the destroy() method in C++ is:

    					 void
    					 DriverShimImpl::destroy()
    					 {
    						  //destructor handles resource cleanup
    						  delete this;
    					 }

Note that DriverShim::destroy() may be called on either the Publisher thread or on the Subscriber thread. DirXML will call destroy() on the Publisher thread after the Publisher returns from PublicationShim::start() if the Publisher thread does not return from PublicationShim::start() within 30 seconds of the call to DriverShim::shutdown().

In addition note that there is only a destroy() method on the DriverShim interface. This means that when destroy() is called that the DriverShim implementation is responsible for cleaning up any resources used by the Subscriber and Publisher implementations, if such resources have not previously been cleaned up (in a typical implementation the driver object destructor will destroy the Subscriber and Publisher objects).

## How do I use the XmlDocument interface?

The XmlDocument interface is used by DirXML to encapsulate an XML document so as to hide the underlying document representation. For example, your driver might find it most useful to receive the XML document as a series of SAX events. The XmlDocument interface allows your driver to do this regardless of the underlying document representation.

The XmlDocument interface provides methods for getting and setting the document in the following forms:

- Document Object Model (DOM)
- Simple Api for Xml (SAX)
- Serialized XML (byte sequence)

### Getting the XML document as a DOM tree

The following C++ code snippet illustrates getting the XML document from an XmlDocument interface as a DOM tree:

    					 XmlDocument *
    					 SubscriptionShimImpl::execute(XmlDocument * inputDoc
    					 XmlQueryProcessor * query)
    					 {
    						  DOM::Document * document = inputDoc->getDocument();
    						  ...
    					 }

### Getting the XML document as SAX events

The following C++ code snippet illustrates getting the XML document from an XmlDocument interface as a series of SAX events:

    					 XmlDocument *
    					 SubscriptionShimImpl::execute(XmlDocument * inputDoc, XmlQueryProcessor * query)
    					 {
    						  SAX::Parser * eventGenerator;
    						  SAX::InputSource * inputSource;
    						  eventGenerator = inputDoc->getDocumentSAX();
    						  inputSource = inputDoc->getDocumentInputSource();
    						  //my event handler is a class that implements the DocumentHandler interface
    						  //declared in sax.h
    						  eventGenerator->setDocumentHandler(myEventHandler);
    						  //this call causes the SAX events to be sent to the document handler
    						  eventGenerator->parse(inputSource);
    						  ...
    					 }

### Getting the XML document as a byte array

The following C++ code snippet illustrates getting the XML document from an XmlDocument interface as a byte array:

    					 XmlDocument *
    					 SubscriptionShimImpl::execute(XmlDocument * inputDoc
    					 XmlQueryProcessor * query)
    					 {
    						  static const unicode ENCODING[] = {'U','T','F','-','8',0};
    						  const unsigned char * bytes;
    						  int length;
    						  bytes = inputDoc->getDocumentBytes(ENCODING, LITTLE_ENDIAN, &length);
    						  ...
    					 }

## How do I create an XML document?

The XmlDocument interface provides three primary methods of creating an XML document. The XML document may be created from a Document Object Model (DOM) tree, Simple Api for Xml (SAX) events or from a byte array containing the serialized XML document. Examples of each follow:

### Creating the XML document from a DOM tree

The following C++ snippet illustrates creating an XmlDocument using the DOM (the factory methods are defined in InterfaceFactory.h):

    					 //create a document using DirXML factory method
    					 DOM::Document * document = Document_new();
    					 //create the document tree
    					 DOM::Element * ndsElement = document->createElement(NDS_ELEMENT_NAME);
    					 ...
    					 //create the XmlDocument instance
    					 XmlDocument * returnDoc = XmlDocument_newFromDOM(document);
    					 ...
    					 //sometime later, after returnDoc is no longer being used:
    					 XmlDocument_destroy(returnDoc);
    					 document->destroy();

### Creating the XML document from SAX events

The following C++ snippet illustrates creating an XmlDocument using SAX events. For ease of illustration, the SAX event source is the DirXML supplied XML parser, using a file as input:

    					 SAX::InputSource * inputSource = InputSource_new();
    					 inputSource->setSystemId("tempfile.xml");
    					 SAX::Parser * parser = Parser_new();
    					 XmlDocument * xmlDoc = XmlDocument_newFromSAX(parser, inputSource);
    					 ...
    					 //sometime later, after xmlDoc is no longer being used:
    					 XmlDocument_destroy(xmlDoc);
    					 Parser_destroy(parser);
    					 InputSource_destroy(inputSource);

### Creating the XML document from a byte array

The following C++ snippet illustrates creating an XmlDocument from a byte array:

    					 static const unicode ENCODING[] = {'U','T','F','-','8',0};
    					 //make a driver-specific call to get the serialized XML from somewhere
    					 int length;
    					 unsigned char * bytes = get_bytes_from_somewhere(&length);
    					 //create the XmlDocument from the serialized XML
    					 XmlDocument * xmlDoc = XmlDocument_newFromBytes(bytes,length,ENCODING,LITTLE_ENDIAN);
    					 ...
    					 //sometime later, when xmlDoc is no longer in use...
    					 XmlDocument_destroy(xmlDoc);
    					 release_bytes_from_somewhere(bytes);

## What about Memory Management?

As with any C++ program native drivers must carefully manage the objects they create. What this means primarily is that a native driver must handle destroying any XML document objects the driver creates, and must not destroy any XML document objects passed to the driver. This requires management for two primary areas:

- Documents submitted to DirXML - Documents submitted to DirXML are submitted either through the XmlCommandProcessor interface passed to the PublicationShim::start() method or through the XmlQueryProcessor interface passed to the SubscriptionShim::execute method. In either case the driver must handle destroying the XmlDocument object passed to DirXML as well as the underlying XML representation. For example, if the driver uses the factory methods Document_new() and XmlDocument_newFromDOM() then the driver must destroy the XmlDocument object through XmlDocument_destroy() and the DOM object through DOM::Document::destroy
- Documents returned to DirXML - Documents returned to DirXML are returned from DriverShim::init(), DriverShim::shutdown(), SubscriptionShim::init(), SubscriptionShim::execute, PublicationShim::init(), and PublicationShim::start(). Such documents must not be destroyed until they are no longer used. DirXML guarantees that a return document is no longer referenced when:
  - Another method in the same interface is called. For example, the document returned from SubscriptionShim::init() may be destroyed when SubscriptionShim::execute is called.
  - When the same method is called again. For example, a document returned from a previous call of SubscriptionShim::execute may be destroyed during a subsequent call to SubscriptionShim::execute.
  - When DriverShim::destroy() is called.

Any other objects that the driver creates are also the driver's responsibility to destroy. For example, if the driver calls FileOutputStream_newFromName() then the driver must also call FileOutputStream_destroy() with the returned object when it is no longer used.

## Are there any utility functions available?

There are a number of utility functions available. These fall into the following general categories:

- Interface factories and destructors
- Encoding support
- Support for DirXML's XML dialect (XDS Support)

### Interface factories and destructors

DirXML supplies implementations of all interfaces required. The interface factories and corresponding destructors are defined in InterfaceFactory.h. If a factory method does not have a corresponding destructor method it is because the destructor method is defined in the interface itself (see, for example, Node::destroy()).

Note that a driver writer is free to implement the interfaces for passing data to DirXML from the driver if such implementations work better for a particular driver. However, all interfaces passed to the driver from DirXML will use DirXML's underlying implementation.

- Document_new() - Creates a new DOM Document object using the DirXML native implementation.
- Document_destroyInstance() - Destroys a DOM Document object returned from Document_new().
- Parser_new() - Creates a new SAX Parser implementation using the DirXML native implementation.
- Parser_destroy() - Destroys a SAX Parser returned from Parser_new().
- InputSource_new() - Creates a new SAX InputSource implementation using the DirXML native implementation.
- InputSource_destroy() - Destroys a SAX InputSource returned from InputSource_new().
- XmlDocument_newFromBytes() - Creates a new XmlDocument implementation from a byte array using the DirXML native implementation.
- XmlDocument_newFromDOM() - Creates a new XmlDocument implementation from a DOM Document object.
- XmlDocument_newFromSAX() - Creates a new XmlDocument implementation from a SAX Parser and InputSource.
- XmlDocument_destroy() - Destroys an XmlDocument implementation returned from a factory method.
- FileOutputStream_newFromName() - Creates an implementation of OutputStream that uses a standard C library FILE as the underlying stream.
- FileOutputStream_newFromFILE() - Creates an implementation of OutputStream that uses a standard C library FILE as the underlying stream.
- FileOutputStream_destroy() - Destroys the OutputStream returned from a the factory methods FileOutputStream_newFromName() and FileOutputStream_newFromFILE().
- ByteArrayOutputStream_new() - Creates an implementation of OutputStream that uses a byte array as the underlying stream.
- ByteArrayOutputStream_getDataSize() - Returns the size of the data in a OutputStream returned from ByteArrayOutputStream_new().
- ByteArrayOutputStream_getBytes() - Returns a pointer to the data in a OutputStream returned from ByteArrayOutputStream_new().
- ByteArrayOutputStream_destroy() - Destroys a OutputStream returned from ByteArrayOutputStream_new().
- SAXException_new() - Create an implementation of SAXException. This is useful for drivers implementing the SAX DocumentHandler interface.
- SAXParseException_new() - Create an implementation of SAXParseException. This is useful for drivers implementing the SAX Parser interface.
- DriverFilter_new() - Create an implementation of DriverFilter. This is a useful for drivers who need to make use of the Event Filter in their driver.
- DriverFilter_destroy() - Destroy an implementation of DriverFilter returned from DriverFilter_new().
- Trace_new() - Create an implementation of Trace. This is useful for debugging. The Trace interface causes messages to be written to the DSTrace screen and, optionally, a file. See DSTrace for more information.
- Trace_destroy() - Destroy an implementation of Trace returned from Trace_new().

### Encoding support

The encoding support functions are for converting to and from Base64 encoding and for converting between UTF-8 and UTF-16 encoding. Base64 encoding is how DirXML encodes binary data in the XML documents used for encoding commands and events. UTF-8 and UTF-16 are two encoding methods for Unicode characters using 8- and 16-bit encoding units, respectively. Base64 functions are declared in Base64Codec.h and the UTF functions are in UTFConverter.h.

- Base64Codec_encode() - Encode a byte array into UTF-16 characters using Base64 encoding.
- Base64Codec_encodeFree() - Free the UTF-16 string returned from Base64Codec_encode().
- Base64Codec_decode() - Decode a UTF-16 character string that represents binary data into a byte array.
- Base64Codec_decodeFree() - Free the byte array returned from Base64Codec_decode().
- UTFConverter_16to8() - Convert a UTF-16 string to UTF-8.
- UTFConverter_8to16() - Convert a UTF-8 string to UTF-16.
- UTFConverter_free() - Free a string returned from UTFConverter_16to8() or UTFConverter_8to16().

### XDS Support

XDS (XML Directory Services) is the name of the XML dialect used by DirXML. The XDS support functions allow access to the strings necessary to create a valid XDS document and provide simple methods to create empty XDS input and output documents. The XDS support functions are in NdsDtd.h.

- NdsDtd_getStrings() - Returns a pointer to a structure containing pointers to const unicode strings. These strings are tag names, attribute names, and attribute values defined in nds.dtd. See the actual include file for the structure declaration.
- NdsDtd_newInputDocument() - Creates a new, empty input document for publishing data to DirXML.
- NdsDtd_newOutputDocument() - Creates a new, empty output document for returning data to DirXML.
- NdsDtd_addStatus() - Adds a status element to an input or an output element in an XDS document.

## With which libraries do I link my native driver?

The import libraries are found in the DirXML SDK.

- Win32 - dirxmllib.lib
- NetWare - dirxml.imp
- Solaris - libdirxml.so
- Linux - libdirxml.so

## How do I debug a native driver?

This varies by platform.

### Win32

- Attach a debugger to the dhost process, or start dhost.exe under the debugger. To attach the Visual Studio debugger, use Build | Start Debug | Attach to Process...
- Set a breakpoint in your driver code. This will require preloading your driver dll if your driver is not already running. For Visual Studio, preloaded modules are configured under Project | Settings..., Debug tab, Additional DLLs.
- Start your driver using ConsoleOne.

### NetWare

- Load your NLM using load -d.
- Set a breakpoint in your driver.
- Start the driver using ConsoleOne.