Inside the Extension Mechanism


Because it is the most complicated path through the entire program, we will now discuss how a extension is picked up off of the XML stream (at least under SAX), how it is handled, how is is constructed and dispatched.

Extensions could be defined basically as anything with an xmlns which isn't handled by the core (being the xmlns of etherx). In the current implementation, Extensions are only picked up in appropriate places in the xml 'packets' that make up Info/Query (IQ), Message, and Presence.

here is what happens, step by step - from an extension's conception to maturity.

Part 1: Inside the Backend (SAX)

For example, the following data is received in a packet:

<iq type="set">
  <query xmlns="jabber:iq:auth">
    <username>David</username>
    <digest>DEADBEEFCAFEBABEABBAFEFEFE</digest>
    <resource>PalmV</resource>
  </query>
</iq>

(we will assume that this client could receive an auth request rather than be sending one, and that there is a client for the Palm Pilot alredy existing)

The ProtocolHandler will see the iq element and fire up the InfoQuerySubHandler. SubHandlers are subroutine-type handlers - they are responsible for the current element level of the file, and are out-of-scope once the element they are created on is closed.

Inside the InfoQuerySubHandler, an instance of the InfoQueryBuilder (from 'jabberbeans') is used to create an InfoQuery packet. The SAX parser passes the attribute, which is set right away. The next element received is a query with an atribute 'xmlns="jabber:iq:auth"'.

The presence of the xmlns triggers the extension-handling code. In the current implementation, the extension-handling code is started by using a serializing subhandler on the extension element and all nested elements.

The serializing subhandler does nothing but concatenate events back to XML, so that after the parser is done reading in elements we have an equivalent piece of XML.

Once the element goes out of scope, the serializing subhandler is done, and two strings are read out - the xmlns and the 'snippet' of XML which was created by recording the SAX events.

Part 2: Leaving the Backend

At this point, the ExtensionBuilderFactory in the SAX backend comes to play. We look up the extension by the XMLNS in the hash table of the factory.

If we do not find an element, we go with a default (DefaultExtension) which simply saves the xmlns and serialized data.

If we do find an element in the factory lookup, the ExtensionXMLBuilder which is pointed to by the entry is instantiated, and the XMLNS and XML snippet is passed in, as well as a string from the factory (so that a single class can build different types of extensions).

The way that the ExtensionXMLBuilders actually build Extensions requires a level of indirection, since Extension-derived objects are immutable. There is an associated ExtensionBuilder with the Extension, the ExtensionBuilder is used in the parsing of the XML to put the data into an Extension object. Both ExtensionBuilder and Extension are considered elements of the Frontend, and the jabber:iq:auth is handled on the frontend by IQAuthExtension and IQAuthExtensionBuilder, both in org.jabber.jabberbeans.Extension.

Part 3: final steps + etc

So we have taken the XML snippet, converted it into an extension object - now the IQHandler takes that extension object and adds it to the InfoQuery packet being build with the InfoQueryBuilder. The only real check that is done on the InfoQuery construction process is that the Extension implements IQExtension.

When the extension is done, we are finished with the last element in the IQ packet. Upon </iq>, the InfoQuerySubHandler closes down, building the InfoQuery packet as it finishes. The base level protocol handler then sends this out to the frontend (ConnectionBean), which broadcasts it as an event to all subscribed methods.

Normally the Extension object both holds the data and has the methods for manipulating that data. Jabber:iq:auth is an exception in that it does not have a method for handling digests, or handling the dates. Date handling is considered specialty code, and the SHA1 funtionality needed for the digest authentication is implemented in JCE, which is most definately not going to become a requirement for jabberbeans. Because of this, some extensions like jabber:iq:auth will also require 'Decorator'-type objects to give full functionality.

The Packet objects are considered immutable in that they cannot be altered at all following their construction, and correct in that any IQ, message, or presence packet will always be convertable to XML that complies with the published specifications/DTDs.

The toString() method is used for this. Extension.toString() returns an equivalent representation of the original XML snippet in the packet. InfoQuery.toString() calls this method in generating a full packet which is equivalent to the original message. Even if the extension is not recognized, the DefaultExtension handler will leave the XML code in a string so that an equivalent packet can be recreated when desired.

-David Waite <mass@ufl.edu>