ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Documenting Projects with Apache Forrest
Pages: 1, 2

With all of that done, we'll finally have access to the mapping module that reads site.xml for us; without it, the code returns null and Forrest reports that the api scheme is not a supported pipeline.

The rest of the work is to convert the set and the class name into a full path. Note the use of getValue(), inherited from AbstractMetaModule, which does the hard work of using either the default InputModule configured or the one configured at runtime:

// try to find our javadoc base in site.xml
String basePath = (String) getValue(setName, objectModel,
        this.input, this.defaultInput, this.inputConf,
        null, inputName, inputConfig);
if (null == basePath) {
        warn("unknown Javadoc set '" +
        setName + "'; check site.xml");
    return null;

// make sure it's a valid path
if (!basePath.endsWith("/")) {
    basePath = basePath + "/";

return basePath +
        substring(ndxSlash + 1));

and the rest is just string manipulation to convert foo.Bar to foo/Bar.html and foo.bar#method() to foo/Bar.html#method():

private String convertToJavadocPath(String className) {
    String path = className.replace('.', '/');
    int ndxHash = path.indexOf('#');
    if (ndxHash == -1) {
        return path + ".html";
    } else {
        return path.substring(0, ndxHash) + ".html" +

You can get the full ApidocInputModule in the source package that accompanies this article.

With our Cocoon plugin done, all that remains is to integrate it. The example package contains a build file that will create a JAR containing our one class, forrest-apilink-block-1.0.0.jar. First drop this in ${forrest.home}/WEB-INF/lib so that it's visible. Next, we need to tell Cocoon to create several components we'll reference later in the sitemap. Open ${forrest.home}/WEB-INF/cocoon.xconf in your favorite editor and find the <input-modules> stanza to add the following:

      logger="core.modules.mapper" name="api"/>
      logger="core.modules.mapper" name="apiconfig"/>

This declares the mapper we use to wire our API input module to site.xml (apiconfig) and the API mapper itself (api) and ensures Cocoon will load them for use in the sitemap. With this done we have just one more step: modify Forrest's sitemap. You can find this file in ${forrest.home}/context/sitemap.xmap. Look for <map:transformer name="linkrewriter"> and replace <schemes>site ext</schemes> with <schemes>site ext api</schemes>. Add this block along with the site and ext modules:

<input-module name="api">
    <input-module name="apiconfig">
        <input-module name="linkmap">
            <file src="{src}" reloadable="true" />

Now you can add an entry to your site.xml like this, after the <external-refs> stanza:

    <example base="../../apidocs/example/"/>

You can now put your Javadocs under build/site/apidocs/example and all of your links to them will work!

Forrest Alternatives

If you're familiar with Maven, you may be wondering why anyone would bother with Forrest. Maven does everything Forrest does, and much more: completely automating your build, attaching JUnit and CVS reports and all sorts of additional useful information on top of the basic documentation. It even provides targets to auto-install your newly generated web site onto a remote host.

I have used Maven on a number of projects, and it's an impressive package. The learning curve is not much worse than Forrest's for basic use, and since you don't have to learn Ant if you use Maven, it's arguably even less for setting up a project from scratch. Forrest as a whole is less complex, though, and if you don't need everything Maven provides, you might want to start with Forrest and migrate to Maven later if you need it. Forrest is also better if you have an existing large-build system based on Ant: it lets you add in Maven-style web site generation incrementally instead of rewriting all your build scripts to Maven-ize the project.

In terms of pure-documentation alternatives, another solid option with a lot of open source community support is DocBook. You could write the manual for a 747 with DocBook: it's the ultimate SGML (or XML; there are two versions) dialect for technical writing. The XML variant has a nice set of stylesheets from Norman Walsh that can generate HTML, PDF, RTF (Microsoft Word) and other formats from DocBook source. I think Forrest's XML dialect covers 80 percent of the cases, with a much smaller learning curve, but for a large project that also needs to produce print documentation, DocBook merits consideration. Note that if you want to migrate from DocBook, Forrest supports rendering a subset of DocBook/XML as well, but it is not well supported. Forrest does not aim to become a full-fledged DocBook renderer any time in the future, either, according to one of the developers, so I would not rely upon it as a format for new documentation in Forrest.

Wrapping Up

I hope this article piqued your interest in Forrest. It's hard to do it justice in a short article, but there's plenty more on the web site to sate your curiosity. You probably will want to look at the accompanying source package, which has a complete (but minimal) set of project documentation using Forrest.

If you want to go further on the code sample, here are a number of possible extensions you might pursue. None of these is Cocoon-specific; they're more for making the Javadoc integration smoother:

  • auto-imports: parse the package-list files in the base of each apidoc directory and prefix each reference given, testing if a corresponding Javadoc file exists.

  • "Smart method matching": parse the Javadoc HTML file itself to find all its available <a name=""> tags for the documented methods to allow for shorthand, e.g. #configure instead of #configure(org.apache.avalon.framework.configuration.Configuration), if there's a configure() method in the class.

  • Actually implement the full InputModule API, including methods that retrieve multiple values for a key, to allow selection of all of the possible links you could make. This would make the module a more general database for Javadoc information for Cocoon applications; probably, you'd need to do some kind of indexing to keep performance acceptable.


Kyle Downey is a part-time Java consultant and founder of Amber Archer Consulting

Return to ONJava.com.