Generate JavaBean classes dynamically with XSLT

Use the XSLT processor as a powerful source code generator

1 2 3 Page 3
Page 3 of 3
<?xml version="1.0"?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    version="1.0"   
    xmlns:java="http://xml.apache.org/xslt/java"
    exclude-result-prefixes="java">
<xsl:output method = "text"/>
<xsl:template match="bean">
/**
 * <xsl:value-of select="comments"/>
 * This class has been generated by the XSLT processor from the
metadata 
 */
public class <xsl:value-of select="name"/> {
    /**
     * Creates a new instance of the <xsl:value-of
select="name"/> bean
     */
    public <xsl:value-of select="name"/>() {}
         
    <xsl:apply-templates select="property"/>
}
</xsl:template>
<xsl:template match="property">
    private <xsl:value-of select="type"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="name"/>;
    <xsl:variable name="name" select="name"/>
    <xsl:variable name="cname" select="java:Capitalizer.capitalize($name)"/>
    /**
     * Sets <xsl:value-of select="comments"/>
     * @param <xsl:value-of select="name"/> is <xsl:value-of
select="comments"/>
     */
    public void set<xsl:value-of select="$cname"/>(<xsl:value-
of select="type"/> <xsl:text> </xsl:text><xsl:value-
of select="name"/>) {
      this.<xsl:value-of select="name"/> = <xsl:value-of
select="name"/>;          
    }
    /**
     * Returns <xsl:value-of select="comments"/>
     * @return <xsl:value-of select="comments"/>
     */
    public <xsl:value-of select="type"/><xsl:text></xsl:text>
    <xsl:apply-templates select="type"/><xsl:value-of
    select="$cname"/>() {
      return <xsl:value-of select="name"/>;
    }
</xsl:template>
<xsl:template match="type">
    <xsl:variable name="type" select="."/>
    <xsl:choose>
    <xsl:when test="$type='boolean'">is</xsl:when>
    <xsl:otherwise>get</xsl:otherwise>
    </xsl:choose>
</xsl:template>
</xsl:stylesheet> 

The XSLT language, XSL, requires time and effort to learn. The above stylesheet may not tell you much yet. If you are already versed in XSL, let me briefly explain how this works.

There are three templates in this stylesheet. One processes the <bean> element. This is where we provide the bean's class definition and the default constructor. The other template processes the <property> element. Please note that I used the Xalan-Java extensions mechanism to initialize the XSL variable arbitrarily called cname. This variable contains the name of the property with the first character capitalized. Here I use it to generate the names of the accessor methods. Since XSLT does not provide an extension function, I had to create a small Java utility class, Capitalizer (shown in Listing 4), that capitalizes the first character in the string. This class must be compiled and made available to the XSLT processor via the CLASSPATH at transformation time:

Listing 4. Capitalizer.java

/**
 * This class provides an XSLT extension function that
 * may be utilized by Xalan-Java extension mechanism.
 */
public class Capitalizer {
    /**
     * This method capitalizes the first character
     * in the provided string.
     * @return modified string
     */
    public static String capitalize(String str) {
        return Character.toUpperCase(str.charAt(0)) + str.substring(1);
    }
}

Please note that the idea of the XSLT extensions is not proprietary to Xalan. In fact, it is part of the XSLT Recommendation.

Finally, a third template decides on the getter method's name. Remember, it should have the prefix is for boolean properties and get for all the others. Before we continue, please note that XSLT is rich enough to let you accomplish the same transformation in different ways. For example, instead of a separate template, we could use the <xsl:for-each select="property"> element to process the <property> element.

Now we have all the ingredients to cook up our Product bean. Once you have Xalan installed and configured, you may perform the transformation from the command line like this:

java org.apache.xalan.xslt.Process -in xmlSource  -xsl stylesheet -out outputfile

The mandatory -in parameter must be the source XML document's URL. You may point it to a local file or to the URL I suggested earlier.

The optional -xsl parameter is the URL pointing to an XSLT stylesheet. Again, you are welcome to try the one I have prepared for you: http://www.climbstairs.org/examples/JavaBean.xsl. Please note that this parameter is optional because you may specify the XSLT URL directly inside the source XML document via processing instruction (PI). I would not recommend doing that simply because by decoupling these two documents you gain more flexibility. For example, you may decide in the future to create another XSLT stylesheet to generate an IDL (Interface Definition Language) for your CORBA application.

The optional -out parameter specifies the name of the file to host the source code for the Java class we are about to receive. You may omit it to output the transformation results on the screen.

If you've configured your CLASSPATH properly, Listing 5 shows what you get as a result of the transformation:

Listing 5. Product.java

/**
 * This bean represents a product that the company offers to its
customers
 * This class has been generated by the XSLT processor from the
metadata
 */
public class Product {
    /**
     * Creates a new instance of the Product bean
     */
    public Product() {}
         
         
    private int code;
         
    /**
     * Sets the product inventory code
     * @param code is the product inventory code
     */
    public void setCode(int code) {
        this.code = code;          
    }
    /**
     * Returns the product inventory code
     * @return the product inventory code
     */
    public int getCode() {
        return code;
    }
    private String name;
         
    /**
     * Sets the product name
     * @param name is the product name
     */
    public void setName(String name) {
        this.name = name;          
    }
    /**
     * Returns the product name
     * @return the product name
     */
    public String getName() {
        return name;
    }
    private boolean testedOnAnimals;
         
    /**
    * Sets the flag that indicates if the product was tested on animals
    * @param testedOnAnimals is the flag that indicates if the product
was tested on animals
    */
    public void setTestedOnAnimals(boolean testedOnAnimals) {
        this.testedOnAnimals = testedOnAnimals;          
    }
    /**
     * Returns the flag that indicates if the product was tested on
animals
     * @return the flag that indicates if the product was tested on
animals
     */
    public boolean isTestedOnAnimals() {
        return testedOnAnimals;
    }
    private java.util.Date availableSince;
         
    /**
     * Sets the date when the company started offering this product to
its customers
     * @param availableSince is the date when the company started
offering this product to its customers
     */
    public void setAvailableSince(java.util.Date availableSince) {
        this.availableSince = availableSince;          
    }
    /**
     * Returns the date when the company started offering this product
to its customers
     * @return the date when the company started offering this product
to its customers
     */
    public java.util.Date getAvailableSince() {
        return availableSince;
    }
} 

This result is exactly what we expected. This source code compiles very well; I'm sure the compiler has no appreciation for what it took to put it together.

What's next

Once you are happy with the transformation, you are welcome to build a JSP or a servlet that performs it via the TRaX API or by utilizing the XSL Tag Library, which is part of the Apache Jakarta Taglibs collection. You may then request the transformation via an HTTP request, receive the Java code, and compile it dynamically using the java.lang.Compiler class. You may apply the same technique to produce the BeanInfo class, different support classes, relevant SQL statements, DTDs, and anything else to accompany every bean class you generate. To plug the freshly minted classes into your application, you may consider using the Reflection API, which dynamically discovers their structure.

A framework that works

Everything I've described in this article serves as a foundation of the framework I am successfully applying in commercial projects. The business domain of financial services where I currently work operates with large quantities of relatively bulky business objects. Every new customer requires a substantial customization of these objects. Without this framework, our team would not be able to meet the tight development schedules. I'm sharing it with you because it really works, it saves you a lot of time and money, and it utilizes some of the most fascinating technologies available today.

Sun Certified Java Programmer and Microsoft Systems Engineer Victor Okunev is a design architect of the research and development team for Vancouver-based Marlborough Stirling Plexus. With an M.S. in computer science from the Moscow State Institute of Radio Engineering, Electronics, and Automation, Victor has more than 11 years' experience in software development, primarily on enterprise-scale applications. Victor also works as a Java instructor with Learning Tree International, writes for JavaWorld, snowboards, and enjoys sea and whitewater kayaking.

Learn more about this topic

1 2 3 Page 3
Page 3 of 3