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

advertisement

AddThis Social Bookmark Button

Quick and Easy Custom Templates with XDoclet
Pages: 1, 2

Putting It Together

The last piece of the puzzle is how to put this all together and get our hands on the generated source file. This is where Ant comes in. We'll be doing three things:



  1. Create a directory, separate from our code base, where we'll keep the pseudo-source and auto-generated source files.
  2. Create a directory for our templates.
  3. Create an Ant task for the code generation.

On my development machine, I have the following directory structure:

trunk
  |
  +----+- src
       +- xdoclet
            |
            +- build
            +- src
                +- com/mytest/account/controller
            +- template

At first, this may seem a little confusing, with two src directories. However, I feel there is good reason for this, as I have a separate src directory under the xdoclet directory that allows me to keep separate all of the pseudocode and generated code. After I have run the Ant task, it's just a matter of copying the file over to the main src directory (on the same level of the trunk as the xdoclet directory) and adding in the implementation code. For the purpose of this article, I'm keeping these things separate from the source code base, which should prevent any build issues that might creep in if we try to integrate everything at once.

Based on the above directory structure, this is where our two files should live:

trunk/src/xdoclet/src/com/mytest/account/controller/TestController.java

trunk/src/xdoclet/template/MultiController.xdt

The .java file will need the package to match the directory structure for XDoclet to be able to pick it up and run the template against it. I found XDoclet only had the log4j debugging levels set to INFO and so, when I did not have the .java file in the correct tree, XDoclet would not display any error messages; nor would it find and generate the file. It wasn't until I went into the .jar and modified the log4j.properties file so that debugging was turned on that I got the proper debug messages saying there were no files to process. Technically, this wasn't an error, but it kept me scratching my head for some time, so be aware of this.

The following Ant task will use the above tree and place the generated code in the proper directory.

1.      <!-- ========Xdoclet Target ======== -->
2.
3.  <target name="xdoclet">
4.
5.  <path id="xdoclet.classpath">
6.    <fileset dir="${xdoc.home}/lib"> 
7.      <include name="*.jar"/> 
8.    </fileset> 
9.  </path>
10.
11.  <echo>+------------------------------------+</echo>
12.  <echo>|Generating sources from xdoclet tree|</echo>
13.  <echo>+------------------------------------+</echo>
14.
15.  <taskdef name="doclet" \
     classname="xdoclet.DocletTask" \
     classpathref="xdoclet.classpath" />
16.  <doclet destdir="${jxdoc.dir}/build"
17.        verbose="true">
18.    <fileset dir="${jxdoc.dir}/src">
19.      <include name="**/*Controller.java" />
20.    </fileset>
21.    <template
22.       templateFile="${jxdoc.dir}/template/MultiController.xdt"
23.       destinationFile="{0}.java"
24.       subTaskName="Create Controller file(s).." />
25.  </doclet>
26.
27.  </target>

A few settings will be required to run this.

  1. On line 6, xdoc.home should point to your XDoclet install directory.
  2. Line 22 defines the location and name of our template file.
  3. Line 24 specifies the task name message that XDoclet will send to the console when the doclet task is running. This is for convenience only.

You can run the task by issuing the following command: ant xdoclet.

If all goes well, you should see the usual Ant BUILD SUCCESSFUL message. Then, under the xdoclet/build/com/mytest/account/controller directory, your new file should appear, full of the necessary skeleton properties and methods.

This template approach can also be adapted to generate not only controllers, but beans, forms, services, etc. In the xdoclet target, you would just have to add a new taskdef for each object type you want to generate and provide a different template. For example, if we want to generate service objects, we could use the following taskdef:

1.  <taskdef name="servicedoclet" \
    classname="xdoclet.DocletTask" \
    classpathref="xdoclet.classpath" />
2.  <servicedoclet destdir="${jxdoc.dir}/build"
3.        verbose="true">
4.    <fileset dir="${jxdoc.dir}/src">
5.      <include name="**/*Service.java" />
6.    </fileset>
7.    <template
8.       templateFile="${jxdoc.dir}/template/Service.xdt"
9.       destinationFile="{0}.java"
10.       subTaskName="Create Service files(s).." />
11.  </doclet>

Above, we see that we've changed the name of the taskdef to servicedoclet and also the include fileset property from *.Controller.java to *.Service.java, which will only look for files ending in "Service.java." On line 8, we are telling XDoclet to use the Service.xdt template. So we see that it's fairly trivial to add on special code generators once we have the initial setup going. It is entirely possible (in fact, this is what I do) to generate a few templates and, under the xdoclet task, have it look through the source tree for each object type and generate everything at once.

A word about Maven 2.0 and Archetypes: Maven 2.0 will contain a mechanism called Archetypes which allow for template-driven project code generation. While implemented differently--using Velocity for the templates--than what we have done above, it will basically accomplish the same things, but on a larger, project scale. I have tried Maven a few times and I can appreciate its scope. However, I feel it is much more a part of a larger picture and would require more developer effort and time (in the beginning, of course) to integrate than the example above. But it does address one of my initial issues, that I liked the fact that Rails worked with the developer on a project level. Maven, too, will operate on that project level, saving developers time by auto-generating the needed code based on set config parameters. This can only be a positive thing. For existing projects, however, the approach discussed here will probably be faster to implement and have less of a structural impact.

Conclusion

To recap, here's what we've accomplished:

  1. Created a custom pseudo-Java file containing all of the methods and properties to not just generate a class, but also generate the necessary XML for our framework config files. In our case, we defined a custom controller that could be used in the Spring web framework.
  2. Defined a template file that contained the static elements of our class and incorporated XDoclet XML tags, which provided the dynamic class code at run time.
  3. Set up a quick directory structure and Ant task that will call XDoclet to generate our skeleton controller class.

This only scratches the surface of XDoclet templates. We've seen just one small, yet powerful, example of how templates can help you not just become a more efficient programmer, but also a smarter one. I hope it will save you as much time as it has me.

Resources

Jason Lee has been developing web apps for the past ten years and currently is VP of technology at a small startup.

Return to ONJava.com.