Architecture

This section is for those who want to develop new modules for XDoclet. End users who just use the tool do not need to read this section, but it's an essential read for those who want to write custom modules or want to contribute.

XDoclet mainly consists out of five components:

  • XDoclet Engine
  • XJavaDoc Engine
  • Subtasks (contained in modules)
  • Templates (contained in modules)
  • Tag Handlers (contained in modules)
Although there are several modules (and therefore several subtasks and tag handlers), all sub tasks are built for the same framework: the framework provided by the XDoclet and the XJavaDoc engines.

XDoclet Engine

The XDoclet engine provides the template engine that transforms a template to a file. A template consists of static content and dynamic content, very much in the same way as a JSP page or a Velocity template.

Further, XDoclet contains a Jakarta Ant task that will invoke the templates (via subtasks). Part of XDoclet is also a loader that will dynamically discover and plug in XDoclet modules (via xdoclet.xml file contained in the module's jar file).

The XDoclet engine also contains general-purpose tag handlers that act as a bridge between templates and XJavaDoc. This makes it possible for the templates to access the information in the XJavaDoc beans.

XJavaDoc Engine

The XJavaDoc engine is a complete rewrite of Sun's JavaDoc engine that is faster and more suited for XDoclet (although it is completely stand alone). It scans java source code and makes information about a class available via special java beans that are part of the XJavaDoc core. These beans provide the same information about a class as Sun's JavaDoc API, and some nice extra features.

The extra features of XJavaDoc is the possibility to programmatically modify the javadoc tags and save the source code, as well as an extra API for extracting information from @tags that are using an XML-like attribute scheme.

XJavaDoc was written because Sun's classic JavaDoc engine was too slow, printed too many warnings to standard output and wasn't well suited for XDoclet's purposes (nobody knew by the time of JavaDoc's writing that a tool like XDoclet would show up). XJavaDoc is approximately 5 times faster than JavaDoc and is based on JavaCC.

Further, XJavaDoc has its own Jakarta Ant task (which XDoclet's Ant task inherits from) that makes it easier to deal with than the Ant javadoc task.

Subtasks

A subtask is a class that tells the XDoclet engine what templates to invoke and how to invoke them. Each subtask specifies a name and a parent task. Subtasks are invoked by nesting an element under an XDoclet ant task invocation:

<ejbdoclet ...>
    <remoteinterface/>
</ejbdoclet>

Subtasks have logical names and belong under certain tasks. This information is included in special tags in the header of the subtask. Let's take a look at the RemoteInterfaceSubTask that is located in the ejb module:

/**
 * @ant.element
 *    display-name="Remote Interface"
 *    name="remoteinterface"
 *    parent="xdoclet.modules.ejb.EjbDocletTask"
 */
public class RemoteInterfaceSubTask extends AbstractEjbCodeGeneratorSubTask
{
...

The information in the @ant.element is used during build of the module to generate a special deployment descriptor for the module. Take a look at the xdoclet.xml file inside the xdoclet-ejb-module.jar, and you'll see the following entry:

<subtask
   name="remoteinterface"
   implementation-class="xdoclet.modules.ejb.intf.RemoteInterfaceSubTask"
   parent-task-class="xdoclet.modules.ejb.EjbDocletTask"
/>

The XDoclet core reads this deployment descriptor at start-up and uses the information to map a logical name to a class name. It instantiates the corresponding class and invokes the execute() method to start the subtask.

As you see, XDoclet itself is used during the build of the modules, and all this functionality is in the XDoclet core. This makes it easy to write new sub tasks. Just extend xdoclet.SubTask and put the @ant.element tag in the class header. Looking at other subtasks and the javadocs should give you a good starting point.

Last, but not least, the information in the same tag is used when generating the documentation.

Templates

At the core of XDoclet's extensibility are the template files. An XDoclet template is a file that contains static parts and dynamic parts. The static parts will be rendered "as is", and the dynamic parts will be substituted by content provided by a tag handler.

The XDoclet templates are not XML files, but they're using an XML-like syntax to add logic and content to the templates. Here is a part of a template that generates a method in a Java source file (slightly reformatted for the sake of readability):

/**
 * <XDtI18n:getString bundle="xdoclet.modules.ejb" resource="util_obtain_localhome_def_ic"/>
 * @return <XDtI18n:getString
    bundle="xdoclet.modules.ejb"
    resource="local_home_interface_for"
    arguments="<XDtEjb:ejbName/>"/>
    <XDtI18n:getString
    bundle="xdoclet.modules.ejb"
    resource="util_lookup_using"
    arguments="<XDtEjbUtilObj:lookupKind/>"/>
 */
public static <XDtEjbHome:homeInterface type="local"/> getLocalHome() throws NamingException
{
   // <XDtI18n:getString bundle="xdoclet.modules.ejb" resource="util_localhome_not_narrowed"/>
   <XDtConfig:ifConfigParamEquals paramName="cacheHomes" value="true">
   if (cachedLocalHome == null) {
      // <XDtI18n:getString bundle="xdoclet.modules.ejb" resource="util_obtain_ic"/>
      InitialContext initialContext = new InitialContext();
      try {
         cachedLocalHome = (<XDtEjbHome:homeInterface type="local"/>)
            initialContext.lookup(<XDtEjbHome:homeInterface type="local"/>.<XDtEjbUtilObj:lookupKind/>);
      } finally {
         initialContext.close();
      }
   }
   return cachedLocalHome;
   </XDtConfig:ifConfigParamEquals>
   <XDtConfig:ifConfigParamNotEquals paramName="cacheHomes" value="true">
   // <XDtI18n:getString bundle="xdoclet.modules.ejb" resource="util_obtain_ic"/>
   InitialContext initialContext = new InitialContext();
   try {
      return (<XDtEjbHome:homeInterface type="local"/>)
         initialContext.lookup(<XDtEjbHome:homeInterface type="local"/>.<XDtEjbUtilObj:lookupKind/>);
   } finally {
      initialContext.close();
   }
   </XDtConfig:ifConfigParamNotEquals>
}

And this one shows a template file that generates parts of an XML file:

<!-- Entity Beans -->
<XDtClass:forAllClasses type="javax.ejb.EntityBean">
 <XDtEjb:ifIsAConcreteEJBean>
  <XDtEjbCmp:ifEntityIsCmp>
   <entity <XDtId:prefixedId prefix="ContainerManagedEntity"/>>
  </XDtEjbCmp:ifEntityIsCmp>
  <XDtEjbBmp:ifEntityIsBmp>
   <entity <XDtId:prefixedId prefix="BeanManagedEntity"/>>
  </XDtEjbBmp:ifEntityIsBmp>
     <XDtMerge:merge file="xdoclet/modules/ejb/dd/resources/ejb-body.xdt">
     </XDtMerge:merge>
   </entity>
 </XDtEjb:ifIsAConcreteEJBean>
</XDtClass:forAllClasses>

The XDoclet template engine can generate any kind of file. It isn't limited to Java or XML. A perfect example of this is XDoclet's ability to generate a todo list from @todo tags in your code. But let's get back to the first template.

When the first template has been rendered, you'll end up with a Java source file that contains this fragment:

/**
 * Obtain local home interface from default initial context
 * @return Local home interface for Person. Lookup using JNDI_NAME
 */
public static airline.ejb.interfaces.PersonLocalHome getLocalHome() throws NamingException
{
   // Local homes shouldn't be narrowed, as there is no RMI involved.
   // Obtain initial context
   InitialContext initialContext = new InitialContext();
   try {
      return (airline.ejb.interfaces.PersonLocalHome)
            initialContext.lookup(airline.ejb.interfaces.PersonLocalHome.JNDI_NAME);
   } finally {
      initialContext.close();
   }
}

Here you see several tags. A tag is an XML-like element that consists of a namespace (followed by a . (dot)) and then by a tag name. Further, each tag can have an arbitrary number of attributes (also referred to as parameters). Don't get confused, these tags have nothing to do with javadoc @tags.

Tag Handlers

When XDoclet's template engine encounters a tag, it will use the namespace to obtain a reference to an object whose class is a subclass of xdoclet.TagHandler. Then it will use reflection to invoke a method on that tag handler that corresponds to the name of the tag (the part following the :).

So how does the template engine know what tag handler java object to use? This is done by looking at a map that maps the namespace to a class. This information is also included in the xdoclet.xml file of a module:

<taghandler
   namespace="EjbUtilObj"
   class="xdoclet.modules.ejb.lookup.LookupUtilTagsHandler"
/>

So when the template engine encounters <XDtEjbUtilObj:lookupKind/> it will invoke the lookupKind on an instance of xdoclet.modules.ejb.lookup.LookupUtilTagsHandler. (The XDt part is stripped away before looking in the namespace->taghandler object map).

If you look closely at this template fragment, you'll see that there are two kinds of tags: Tags that have a body, and tags that don't. These are respectively known as block tags and content tags. Block tags "do" something, and content tags "return" something.

A block tag can evaluate its body or it cannot. It can do this as many times as it wants. Block tags are used to loop or to have conditional evaluation of the tag's body. The methods that implement block tags are void methods.

A content tag is a tag that returns a value, and every occurrence of a content tag will be substituted by a String value.

Tag handler classes are documented with special javadoc tags too. This is in order to generate the taghandler elements in the xdoclet.xml deployment descriptor. The xdoclet.modules.ejb.lookup.LookupUtilTagsHandler class has the following header:

/**
 * @xdoclet.taghandler namespace="EjbUtilObj"
 */
public class LookupUtilTagsHandler extends EjbTagsHandler
{
   ...