Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Reduce code bloat with XDoclet

Discover a versatile template-driven code generator

Sing Li (westmakaha@yahoo.com), Author, Wrox Press
Photo of Sing Li
Sing Li is the author of Professional Apache Tomcat 5, Pro JSP, Third Edition, Early Adopter JXTA, Professional Jini, and numerous other books with Wrox Press. He is a regular contributor to technical magazines and an active evangelist of the P2P evolution. Sing is a consultant and freelance writer and can be reached at westmakaha@yahoo.com.

Summary:  The open source XDoclet code-generation engine, an integral part of many leading Java frameworks, is often heralded as an enabler for attribute-oriented programming and continuous integration. But XDoclet also has an undeserved reputation for being difficult for beginning developers to grasp and master. In this article, the ever-popular Sing Li takes on XDoclet and reveals the simple yet elegant design at its heart, enabling you to understand the technology and put it to productive use.

Date:  05 Oct 2004
Level:  Introductory
Also available in:   Chinese  Japanese

Activity:  10481 views
Comments:  

XDoclet can easily be one of the more versatile cross-technology code-generation tools in your Java programming toolbox. Unfortunately, developers often overlook XDoclet's general utility and use it only when it's bundled as a hidden element of a larger development framework or an IDE. XDoclet is often seen as difficult to apply for custom solutions. This article aims to debunk this myth, stripping XDoclet of its usual trappings of complexity and revealing how you can use this code-generation engine to your advantage.

I'll demonstrate XDoclet's utility with a hands-on example that takes a POJO (plain old Java object) and uses XDoclet to generate all the files that make up a complete Web application for data entry into a relational database. The example explores XDoclet's custom templated code generation, as well as its built-in support for the Hibernate object-relational mapping tool, the Struts Web-application framework, and application server integration (see Resources).

An intelligent code generator

XDoclet's core functionality is to generate code (and/or other configuration/data files) based on a combination of:

  • Specially tagged Java source file(s)
  • Predefined templates

XDoclet has several unique advantages over other template-based code-generation technology (such as Velocity; see Resources):

  • XDoclet is tightly integrated with Apache Ant (see Resources), providing highly automated operations.

  • You embed XDoclet tags that control code generation and template processing as in-line comments within the input Java source-code files. This eliminates the need to synchronize multiple related source and control files.

  • XDoclet's built-in Java parser uses its intimate understanding of the structure of Java code to create an internal structural model of the input Java code. This structural model is frequently called metadata, because it contains data about the associated code.

  • XDoclet's templating logic has complete access to the internal structural model of the input Java code.

Here, I'll take a deeper look into how XDoclet works to help you understand these features.

XDoclet operations

Figure 1 shows XDoclet's required input and generated output.


Figure 1. The XDoclet black box
Figure 1

You can see that Java source code containing embedded XDoclet tags is the input to the system. Driven by Apache Ant, XDoclet processes the input code and generates output text files that can be Java source, HTML pages, XML documents, and so on. To process the input, XDoclet draws on templates (stored in .xdt files) and tag handlers (coded in Java). XDoclet packages templates and tag handlers into "modules" that address different problem domains.

The structural model that XDoclet builds

XDoclet parses the input Java source code containing embedded XDoclet tags and builds a highly detailed structural model of the code. Each element in the structural model represents a Java construct in the source code. Figure 2 illustrates the structural model, revealing the code constructs and relationships that XDoclet tracks.


Figure 2. XDoclet's internal structural model of parsed Java code
Figure 2

The structural model in Figure 2 tracks code constructs (model elements) such as classes, interfaces, and methods. The model also tracks relationships among the elements, such as inheritance and interface implementation. The XDoclet tags you embed within the source code, as in-line comments, are also parsed and tracked within the model as attributes of the model elements.

Inside XDoclet

Figure 3 shows the internals of XDoclet, revealing the functional pieces that make it work.


Figure 3. Functional blocks inside XDoclet
Figure 3

As Figure 3 shows, Apache Ant controls the configuration and operation of XDoclet at runtime. XDoclet parses incoming Java source code and creates the structural model in memory. The template engine generates the output files by processing a set of templates and tag handlers. The templates and tag handlers can be either built-in or custom. The templates and tag handlers have full access to the structural model during code generation.

XDoclet's illusory complexity

Why XDoclet seems more complex than it is

Apache Ant automates the Java software build process. The build-management process is often one of the more complicated processes within a production project. Build-management terminology and concepts are built into Apache Ant and are a prerequisite for understanding its operation. Sophisticated Ant scripts can be fairly complex. Each new version of Ant introduces a new feature set, pushing the complexity envelope even further. This contributes to XDoclet's perceived complexity because XDoclet needs Ant in order to execute.

The problem domains that XDoclet works in are another source of complexity. As delivered, XDoclet comes ready to generate code for EJB component integration, J2EE Web container integration, the Hibernate persistence layer, the Struts framework, Java Management Extensions (JMX), and more. Each of these problem domains comes with a large set of domain-specific jargon and concepts. Issues from these complex problem domains often dominate XDoclet discussion, increasing XDoclet's apparent complexity. It can be difficult to see the forest for the trees.

A smart code-generation engine that understands the structural model of Java code isn't a new concept. In fact, this is precisely how the Javadoc utility that's included with the JDK operates. By parsing a set of Java source files commented with special Javadoc tags, the Javadoc utility can generate HTML documentation for all of a Java program's structural elements, including classes, interfaces, fields, and methods. The Javadoc utility also has built-in knowledge of special Java language concepts such as inheritance, abstract classes, storage classes, and modifiers.

XDoclet was born from the observation that a generalized version of Javadoc for arbitrary code generation could be extremely useful in many programming situations. However, the actual Javadoc source code isn't designed for generalized code generation, but rather is intimately tied to the generation of HTML documentation. Unable to reuse existing code, the XDoclet development team rewrote the engine from scratch and optimized its performance significantly.

What, then, makes it appear so complex? The answer lies in the fact that XDoclet is almost never discussed in isolation, but always in the company of other complex technology. Figure 4 shows the shrouds of complexity that surround XDoclet (and see the sidebar, Why XDoclet seems more complex than it is).


Figure 4. XDoclet's complex coupling
Figure 4

In Figure 4, you can see that XDoclet is intimately tied to:

  • Apache Ant, which controls its operation. XDoclet exists as a set of Ant tasks and cannot execute without Ant.
  • The details of the specific problem domains associated with the generated files.

XDoclet by itself is surprisingly straightforward, as the working code in the following example will show.


Working with XDoclet

Now you're ready see XDoclet at work by exploring the data-entry application example I promised you. (To download the Java code, XDoclet templates, and Ant scripts featured in this example, click on the Code icon at the top or bottom of this article or see the Download section.) Start by examining the Java code in Listing 1, which represents a customer's address. It's coded as a JavaBean component, with the XDoclet tags in bold text:


Listing 1. AddressBean.java source file with XDoclet tags markup
package com.ibm.dw.beans;

import java.io.Serializable;
/**
* @dw.genStrutsAction action="/addAddress.do"
* @hibernate.class table="ADDRESS"
*/
public class AddressBean implements Serializable {

private String streetNumber = "";
private String street = "";
private String city = "";
private String country = "";
private String postalCode = "";
private long id = 0;


public AddressBean() {
}

/**
* @dw.genStruts formlabel="Street Number"
* @hibernate.property length="10"
*/
public String getStreetNumber() {
   return streetNumber;
}

public void setStreetNumber(String inpStreetNumber) {
   streetNumber = inpStreetNumber;

}

/**
* @dw.genStruts formlabel="Street"
* @hibernate.property  length="40"
*/
public String getStreet() {
    return street;
}

public void setStreet(String inpStreet) {
   street = inpStreet;
}

...... more Address bean properties ......

/**
* @hibernate.id generator-class="native"
*/
public long getId(  )
{
        return id;
}

public void setId(long inId) {
   id = inId;
}

}

In Listing 1, note that you embed the XDoclet tags within comments immediately before an associated code element (such as a field, method, interface, or class). XDoclet creates an attribute for each tag, attached to the code element in the structural model, when it parses the source. Pay attention to the @dw.genStruts tag for now, because it's one that the first template in this example will use.

Generating another Java class

For the example, your requirement is to generate the code for a new Java class -- a Struts form bean. Struts will use this bean to hold and transport user input. The bean must contain all the data fields in the form as bean properties, and it must be a subclass of org.apache.struts.action.ActionForm.

To generate the code for the form bean, you create an XDoclet template according to the pseudocode shown in Listing 2. The bold text in braces represents the control-flow logic and the text substitution that you want to take place. Note how the template extracts information from the structural model of the parsed Java source file:


Listing 2. Pseudocode template for generating the AddressBeanForm.java Struts form bean code
package {package name of source class};
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;

/**
 * Form Bean class for {name of source class};.
 *
 * @struts.form name="{name of source class}Form"
 */
public class {name of source class}Form extends ActionForm {

{loop through all the methods in the source class}
   {if the method is a JavaBean "getter" method}
      {if the method has been marked with the @dw.genStruts tag }

	private {return type of method}{name of the JavaBean property}; 

	public {return type of method}{name of the getter method for this property}(){
		return {name of JavaBean property};
	}

	public void {name of the setter method for this property}(
	                            {return type of method} value) {
            {name of the JavaBean property} = value;      		
	}
       {end of if @dw.genStruts}
    {end of if JavaBean getter}
{end of loop}

Generating XDoclet tags using XDoclet

Note the generation of the XDoclet @struts.form tag within the template in Listing 2. You can use XDoclet to generate XDoclet tags in Java source code that XDoclet will process again in later operations. XDoclet will use the @struts.form tag when it generates struts-config.xml later in the example.



The code inside the loop in Listing 2 generates a field declaration, a getter method, and a setter method for each getter method tagged with @dw.genStruts in the input source code.

Listing 2 uses an easy-to-understand pseudocode for the template substitution tags. The actual XDoclet template tags are rather verbose. Listing 3 shows the genformbean.xdt template. (All XDoclet templates are stored in .xdt files.) I've highlighted the XDoclet template tags in bold text for easy reference against the pseudocode.


Listing 3. Actual XDoclet template code for generating the Struts form bean Java code
package <XDtPackage:packageName/>;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;

/**
 * Form Bean class for <XDtClass:className/>.
 *
 * @struts.form name="<XDtClass:className/>Form"
 */
public class <XDtClass:className/>Form extends ActionForm {

<XDtMethod:forAllMethods>
   <XDtMethod:ifIsGetter>
      <XDtMethod:ifHasMethodTag tagName="dw.genStruts">


	private <XDtMethod:methodType/> <XDtMethod:propertyName/>; 

	public <XDtMethod:methodType/> <XDtMethod:getterMethod/>(){
		return <XDtMethod:propertyName/>;
	}

	public void <XDtMethod:setterMethod/>(<XDtMethod:methodType/> value) {
            <XDtMethod:propertyName/> = value;      		
	}
     </XDtMethod:ifHasMethodTag>
   </XDtMethod:ifIsGetter>
</XDtMethod:forAllMethods>

Fluency in Ant scripting

The Ant script called build.xml, which I've provided with this article's sample code (see the Download section for a link to the code), defines all the required Ant targets for the example application. You'll need fluency in Ant if you want to modify the script. See Resources for more information on Ant.

You can find a list of available tags for XDoclet templates by consulting the XDoclet "Template Language" documentation (see Resources).

To run the template against the AddressBean.java source file, use the following Ant command line:

ant -Dbase.class.java=Address genstruts

This command executes a custom Ant target (see the sidebar, Fluency in Ant scripting) to process the genbeanform.xdt template. The XDoclet-supplied Ant task called xdoclet.DocletTask is used to run a template file. See the build.xml file in the sample code for more information if you are interested in the Ant details.

As XDoclet processes the template, it generates the AddressBeanForm.java file in the subdirectory named generated. Listing 4 shows this file, with all the text substituted during template processing:


Listing 4. XDoclet-generated Java source code containing the Struts form bean
package com.ibm.dw.beans;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;

/**
 * Form Bean class for AddressBean.
 *
 * @struts.form name="AddressBeanForm"
 */
public class AddressBeanForm extends ActionForm {

	private java.lang.String streetNumber; 

	public java.lang.String getStreetNumber(){
		return streetNumber;
	}

	public void setStreetNumber(java.lang.String value) {
            streetNumber = value;      		
	}

	private java.lang.Stringstreet; 

	public java.lang.String getStreet(){
		return street;
	}

	public void setStreet(java.lang.String value) {
            street = value;      		
	}

   ...... more bean properties .....

}

Generating a JSP page for data-form input

You can use the same AddressBean.java source file with the genformjsp.xdt template to generate a data-entry-form JSP page. Listing 5 shows genformjsp.xdt:


Listing 5. XDoclet template for generating a JSP page using the Struts taglib to display an HTML form
<%@ page language="java" %>
<%@ taglib  uri="/WEB-INF/struts-html.tld" prefix="html" %>

<html:html>
<head>
</head>

<body bgcolor="white">

<html:errors/>

<html:form action="<XDtClass:classTagValue tagName='dw.genStrutsAction'  paramName='action' /> >"
<table border="0" width="100%">

<XDtMethod:forAllMethods>
   <XDtMethod:ifIsGetter>
      <XDtMethod:ifHasMethodTag tagName="dw.genStruts" >


    <tr>
        <th align="right">
           <XDtMethod:methodTagValue 
                   tagName="dw.genStruts" paramName="formlabel"/>
        </th>
        <td align="left">
            <html:text  property="<XDtMethod:propertyName/>"
              size="<XDtMethod:ifHasMethodTag tagName="hibernate.property" >
              <XDtMethod:methodTagValue tagName="hibernate.property" paramName="length"/>
              </XDtMethod:ifHasMethodTag>"/>
        </td>
    </tr>

     </XDtMethod:ifHasMethodTag>
   </XDtMethod:ifIsGetter>
</XDtMethod:forAllMethods>

    <tr>
        <td align="right">
            <html:submit>
                Submit
            </html:submit>
        </td>
        <td align="left">
            <html:reset>
              Reset
            </html:reset>
        </td>
    </tr>
</table>

</html:form>
</body>
</html:html>

Note the use of the <XDt:methodTagValue> to obtain an attribute value specified in the XDoclet tags within the original AddressBean.java code.

The genformjsp.xdt template shown in Listing 5 above is also processed when you execute the genstruts Ant target. You can find the generated AddressBeanForm.jsp file in the generated subdirectory and examine it to review the template substitutions.

Try out the example, but first...

To try out the templated code generation in this article's example, you need to have Ant and XDoclet installed and tested. This article's code is based on Ant 1.5.4 and XDoclet 1.2.1. To generate all the artifacts and try out the generated Web application, you also need to install Hibernate, Struts, and an application server, and have access to a relational database. The example is based on Hibernate 2.1.4, Struts 1.1, and Servlet 2.3. See Resources for some more information on these technologies. Be sure to read the README files I've included with the source code.

Generating other artifacts

You can use XDoclet to generate any text-based output. The example I've shown you uses XDoclet to generate Java code, JSP pages, XML files, configuration files, and more. It creates a complete data-entry Web application from a single XDoclet-tagged Java source file, AddressBean.java. In doing so, it exercises the built-in templates (residing in JAR files called modules) in XDoclet for generating:

  • Struts configuration and support files
  • Hibernate configuration and support files
  • A deployment descriptor (web.xml) for the Web application

Table 1 shows all the generated files (often called artifacts) for the sample application:


Table 1. XDoclet-generated artifacts for AddressBean.java
Generated ArtifactDescriptionLocation
AddressBeanForm.javaA Java source file containing a form bean class used in Struts form handlinggenerated directory
AddressBeanForm.jspA JSP form using the Struts tag library to accept user input of addressesjsp directory
AddressBeanAction.javaA Struts action class to accept the input value and use Hibernate to store the values in a relational databasegenerated directory
AddressBean.hbm.xmlThe Hibernate mapping file to map between the AddressBean Java object and the relational ADDRESS table in the databaseweb/classes directory
dwschema.sqlThe schema of the RDBMS table for persisting instances of AddressBean objectssql directory
hibernate.cfg.xmlThe configuration file for the Hibernate runtimeweb/classes directory
web.xmlThe deployment descriptor for the generated Web applicationweb directory
struts-config.xmlThe configuration file for the Struts frameworkweb directory

Merge points in XDoclet

You'll see the terms merge point and merge file quite frequently in the XDoclet documentation. Merge files are text files you can merge into the XDoclet-generated code at specific "merge points" (as specified by the template). You use merge files to include static text (such as code fragments and XML fragments) that might be awkward or impossible to generate using XDoclet's capability. For example, you'll find the struts-action.xml file in the merge/web directory of the example code. This file is used to merge in the Struts action mappings during code generation, as part of the generated struts-config.xml file.

In this article, you've examined in detail the generation of the first two artifacts listed in Table 1 and explored the templates that generated them. The AdddressBeanAction.java artifact is generated similarly using a template called genaction.xdt. XDoclet comes with built-in templates and tag handlers for generating the other artifacts in Table 1.

Table 2 lists the Ant target and Ant task for each of the generated artifacts. You can execute each of the Ant targets in the table to generate the related artifact. All of the generated artifacts, together with the original AddressBean.java, form the example Web application. You'll also find a default Ant target called all that will do everything for you, including creating a WAR (deployable Web Archive) for the application. Be sure to read the README.txt in the code distribution before proceeding.


Table 2. Ant targets and Ant tasks for the generated artifacts
Ant targetAnt taskArtifact
genstrutsxdoclet.DocletTaskAddressBeanForm.java
genstrutsxdoclet.DocletTaskAddressBeanForm.jsp
genstrutsxdoclet.DocletTaskAddressBeanAction.java
generateHIBxdoclet.modules.hibernate.HibernateDocletTaskAddressBean.hbm.xml
generateHIBxdoclet.modules.hibernate.HibernateDocletTaskhibernate.cfg.xml
createDDLxdoclet.modules.hibernate.HibernateDocletTaskdwschema.sql
generateDDxdoclet.modules.web.WebDocletTaskweb.xml
generateDDxdoclet.modules.web.WebDocletTaskstruts-config.xml

Conclusion

XDoclet is a useful, intelligent code generator you can use to automate many of your everyday Java development tasks. Don't be put off by its seemingly complex facade. By becoming proficient in XDoclet (and the associated Apache Ant) you can realize valuable time savings that can pay off many times over in your future development work.



Download

NameSizeDownload method
j-xdoclet-code.zip10KB HTTP

Information about download methods


Resources

About the author

Photo of Sing Li

Sing Li is the author of Professional Apache Tomcat 5, Pro JSP, Third Edition, Early Adopter JXTA, Professional Jini, and numerous other books with Wrox Press. He is a regular contributor to technical magazines and an active evangelist of the P2P evolution. Sing is a consultant and freelance writer and can be reached at westmakaha@yahoo.com.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=18032
ArticleTitle=Reduce code bloat with XDoclet
publish-date=10052004
author1-email=westmakaha@yahoo.com
author1-email-cc=