9.3. XDoclet

XDoclet is an open-source project hosted at http://xdoclet.sourceforge.net. It is an extension of Sun's javadoc tool that allows you to embed well-formed tags in java source code and output metadata of a particular type. In the case of the JDODoclet, you can generate a filename.jdo file based on various tags starting with @jdo:. Any class that has the @jdo:persist class-level tag will be processed. This allows for all refactoring to be done in just one place (the java source code file), without the developer needing to manually ensure that the .jdo metadata file is always synchronized with the state of the java source code.

In order to utilize the XDoclet task, you will need to download the XDoclet libraries separately from http://xdoclet.sourceforge.net and add them to your CLASSPATH. The JDODoc task can then be executed from the build.xml as follows:

Example 9.5. Invoking the JDO Metadata generator from an Ant build.xml file

<target name="generateJdoMetadata">
  <taskdef name="jdodoclet"
    classname="com.solarmetric.modules.integration.ant.KodoDocletTask"/>

  <jdodoclet sourcepath="${basedir}" destdir="${classes}">
    <fileset dir="${basedir}">
      <include name="**/*.java"/>
    </fileset>

    <!-- perform the actual transformation here -->
    <jdotags/>

  </jdodoclet>
</target>
		

An example of a commented file is as follows:

Example 9.6. Source code comments for automatic JDO Metadata generation

package samples.xdoclet;

import java.util.*;

/**
 *  This is the TestDoclet example class. It is used to
 *  demonstrate automatic generation of the
 *  TestDoclet.jdo file based on the jdo: tags
 *  in the source code.
 *
 *  Note that the double-$ for inner class names is required
 *  in order to escape ant variable handling.
 *
 *  The jdo:persist tag is required for the XDoclet to know that we
 *  should generate persistence information for this class.
 *
 *  @jdo:persist
 *  @jdo:identity-type     application
 *  @jdo:objectid-class    TestDoclet$$Id
 *  @jdo:requires-extent   false
 *  @jdo:extension         vendor-name="kodo" key="table"
 *                         value="DOCLET_TEST_TABLE"
 *  @jdo:extension         vendor-name="kodo" key="pk-column"
 *                         value="TEST_PK_COLUMN"
 *  @jdo:extension         vendor-name="kodo" key="lock-column"
 *                         value="TEST_LOCK_COLUMN"
 *  @jdo:extension         vendor-name="kodo" key="class-column"
 *                         value="TEST_CLASS_COLUMN"
 *
 *  @author                Marc Prud'hommeaux
 */
public class TestDoclet
{
  /**
   *  @jdo:primary-key      true
   *  @jdo:extension        vendor-name="kodo" key="column-length"
   *                        value="10"
   */
  private String pk1;

  /**
   *  @jdo:primary-key      true
   *  @jdo:extension        vendor-name="kodo" key="column-length"
   *                        value="20"
   */
  private String pk2;

  /**
   *  @jdo:persistence-modifier  transactional
   *  @jdo:null-value            exception
   *  @jdo:default-fetch-group   true
   *  @jdo:embedded              true
   *  @jdo:extension             vendor-name="kodo" key="data-column"
   *                             value="NAME_DATA"
   */
  private String name;

  /**
   *  @jdo:extension    vendor-name="kodo" key="data-column"
   *                    value="MEMO_COLUMN"
   *  @jdo:extension    vendor-name="kodo" key="column-length"
   *                    value="-1"
   */
  private String memo;

  /**
   *  @jdo:extension    vendor-name="kodo" key="data-column"
   *                    value="AGE_DATA"
   */
  private int age;

  /**
   *  @jdo:collection   element-type="TestDocletChild"
   *                    embedded-element="false"
   *  @jdo:extension    vendor-name="kodo" key="table"
   *                    value="TEST_XREF_DOCLET_TABLE"
   *  @jdo:extension    vendor-name="kodo" key="ref-column"
   *                    value="TEST_CHILDREN_REF_COLUMN"
   *  @jdo:extension    vendor-name="kodo" key="order-column"
   *                    value="TEST_CHILDREN_ORDER_COLUMN"
   *  @jdo:extension    vendor-name="kodo" key="inverse"
   *                    value="childInverse"
   */
  private Collection children = new ArrayList ();

  /**
   *  @jdo:map          key-type="String" embedded-key="false"
   *                    value-type="Integer" embedded-key="false"
   *  @jdo:extension    vendor-name="kodo" key="key-column"
   *                    value="TEST_MAP_KEY"
   */
  private Map testMap = new HashMap ();

  /**
   *  Application identity class.
   */
  public static class Id
  {
    public String pk1;
    public String pk2;

    public boolean equals (Object other)
    {
      return other.getClass () == this.getClass () &&
        ((Id)other).pk1.equals (pk1) &&
        ((Id)other).pk2.equals (pk2);
    }

    public int hashCode ()
    {
      return pk1.hashCode () + pk2.hashCode ();
    }
  }
}
		

The resulting TestDoclet.jdo metadata file will then look like:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  This file is auto-generated by the
  com.solarmetric.modules.integration.ant.JDODoclet ant task.
  Bear in mind that any changes to this file will be overwritten
  if the task is re-run.
-->
<jdo>
  <package name="samples.xdoclet">
    <class name="TestDoclet" identity-type="application"
     objectid-class="TestDoclet$Id" requires-extent="false" >
      <extension vendor-name="kodo" key="table"
        value="DOCLET_TEST_TABLE" />
      <extension vendor-name="kodo" key="pk-column"
        value="TEST_PK_COLUMN" />
      <extension vendor-name="kodo" key="lock-column"
        value="TEST_LOCK_COLUMN" />
      <extension vendor-name="kodo" key="class-column"
        value="TEST_CLASS_COLUMN" />
      <field name="age">
        <extension vendor-name="kodo" key="data-column"
          value="AGE_DATA" />
      </field>
      <field name="children">
        <collection element-type="TestDocletChild" embedded-element="false" />
        <extension vendor-name="kodo" key="table"
          value="TEST_XREF_DOCLET_TABLE" />
        <extension vendor-name="kodo" key="ref-column"
          value="TEST_CHILDREN_REF_COLUMN" />
        <extension vendor-name="kodo" key="order-column"
          value="TEST_CHILDREN_ORDER_COLUMN" />
        <extension vendor-name="kodo" key="inverse"
          value="childInverse" />
      </field>
      <field name="memo">
        <extension vendor-name="kodo" key="data-column"
          value="MEMO_COLUMN" />
        <extension vendor-name="kodo" key="column-length"
          value="-1" />
      </field>
      <field persistence-modifier="transactional" null-value="exception"
       default-fetch-group="true" embedded="true" name="name">
        <extension vendor-name="kodo" key="data-column"
          value="NAME_DATA" />
      </field>
      <field primary-key="true" name="pk1">
        <extension vendor-name="kodo" key="column-length"
          value="10" />
      </field>
      <field primary-key="true" name="pk2">
        <extension vendor-name="kodo" key="column-length"
          value="20" />
      </field>
      <field name="testMap">
        <map key-type="String" embedded-key="false" value-type="Integer" />
        <extension vendor-name="kodo" key="key-column"
          value="TEST_MAP_KEY" />
      </field>
    </class>
    </package>
</jdo>