Generate your TLD using java annotations

If you are stuck with JSP and keeping your TLD files by hand (like me), you are probably ignoring the elephant in the room:

  • Tag classes must be kept up-to-date with the TLD by hand. This means duplicate effort, a.k.a. "XML sucks".
  • For any non-trivial tag library, inheritance is a fact. In order to avoid replication in the TLD, your libraries are bound to use some dark XML magic.
  • IDEs are extracting JSP coding hints from the TLD file, so you should be introducing your documentation in the TLD by hand.
  • You are probably not sacrificing Javadoc in the process, which implies maintaining duplicate documentation: java(doc) and TLD.
  • Make it triple, if you want online HTML documentation.
  • There is no way to document deprecated behavior.I love 1.0 releases

This March I will be 35, which sucks. Last year my brother Carlos Coloma celebrated his 25-year-old anniversary reading his MSc project with his colleague Javier Reyes Alonso: Generating TLD and HTML documentation from java annotations. Today we are releasing it as an open source project, TLDGen.

Instant gratification: click here to check out the generated HTML documentation.

TLDGen is a standalone library that does not have external dependencies. This is an important requirement to be able to generate our own TLD files.

Download tldgen.jar and add it to your classpath

First you should extract tldgen.jar from the tldgen zip file or from maven (group org.extrema-sistemas, artifact tldgen). This jar file contains just the annotations to get you running:

package com.acme.tags;

import org.loom.tldgen.annotations.Attribute;
import org.loom.tldgen.annotations.Tag;
import org.loom.tldgen.annotations.BodyContent;

/** 
 * This comment will be included in the generated TLD and HTML 
 */
@Tag(  
  dynamicAttributes=true,
  bodyContent=BodyContent.SCRIPTLESS,
  example="<l:sample name=\"foo\"/>"
)
public class SampleTag extends SimpleTagSupport {

  @Attribute(required=true)
  private String name;

  /** @deprecated use name instead */
  @Attribute  public void setFoo(String value) {
     name = value;  
  } 
}
package com.acme.tags;
import org.loom.tldgen.annotations.Function;
public class Functions { 

  /**
   * bar javadoc <p>this is a paragraph</p>
   */
  @Function(
     displayName="bar display name",
     example="Function example"
  )
  public static void bar() {    }

  @Function
  public static void baz() {    }
}

Execute tldgen

You should download tldgen.zip from google-code, uncompress it somewhere and execute all in the same line (we are assuming a standard gradle project structure):

tldgen
   -sourcepath src/main/java
   -subpackages com.acme.tags
   -tldFile src/main/resources/META-INF/acme-tags.tld
   -displayName "Acme Tag Library"
   -name acme
   -uri "http://acme.com/acme-tags.tld"
   -htmlFolder build/docs/tlddoc

This should generate the TLD docs in build/docs/tlddoc and one TLD file in src/main/resources/META-INF/acme-tags.tld. As an example, you can check out the Loom taglib documentation.