Generate JavaBean classes dynamically with XSLT

Use the XSLT processor as a powerful source code generator

1 2 3 Page 2
Page 2 of 3
DROP TABLE Beans;
CREATE TABLE Beans (id INT,name VARCHAR(30) NOT NULL,comments
VARCHAR(100));  DROP TABLE Properties;
CREATE TABLE Properties (beanId INT,name VARCHAR(30) NOT NULL,type
VARCHAR(30) NOT NULL,comments VARCHAR(100));

Even though I'd rather you use JDBC to create these tables, feel free to use whatever tools and interfaces you prefer to enter data. This might be a good time to think about what kind of tool you'll provide to future customers for these purposes as well. Let's describe the metadata for our Product bean in this data dictionary:

INSERT INTO Beans (id,name,comments) VALUES (1,"Product","This
bean represents a product that the company offers to its customers");
INSERT INTO Properties (beanId,name,type,comments) VALUES
(1,"code","int","the product inventory code");
INSERT INTO Properties (beanId,name,type,comments) VALUES
(1,"name","String","the product name");
INSERT INTO Properties (beanId,name,type,comments) VALUES
(1,"testedOnAnimals","boolean","the flag that indicates if the product
was tested on animals");
INSERT INTO Properties (beanId,name,type,comments) VALUES
(1,"availableSince","java.util.Date","the date when the company started
offering this product to its customers");

As I mentioned earlier, this is probably the absolute minimum information we need to maintain in order to make our metadata useful. (Well, we could probably live without the comments, but uncommented code is considered unprofessional in the programming world.) Therefore, Tables 1 and 2 show what we finally need.

Table 1. Beans

IdName Comments
1ProductThis bean represents a product that the company offers to its customers

Table 2. Properties

BeanIdNameTypeComments
1Codeintthe product inventory code
1NameStringthe product name
1testedOnAnimalsboolean

the flag that indicates if the product

was tested on animals

1availableSincejava.util.Date

the date when the company started

offering this product to its customers

In real life, you would probably need to stretch these tables a bit. You may want to consider including more useful information in your metadata. Actually, the Java API provides some good suggestions in the form of java.lang.Class and several classes from the java.lang.reflect package. There are also two useful classes in the java.beans package. In fact, the java.beans.BeanDescriptor class essentially encapsulates the metadata about the bean itself, whereas the java.beans.PropertyDescriptor class does the same for a bean property. If you are not satisfied with the offered metadata model, these descriptor classes let you expand it by associating a set of named attributes with the entity. I have found this feature particularly handy for setting constraints on the acceptable data values for the bean's properties. I've described it in the context of a data validation framework presented in "Validation with Pure Java" (JavaWorld, December 2000).

With all this in mind, here are additional fields with self-describing names that may be worth adding to your commercial-grade metadata structure.

Beans table:

  • package
  • imports
  • modifiers
  • isAbstract
  • isInterface
  • superClass
  • implements
  • displayName
  • description

Properties table:

  • modifiers
  • isAbstract
  • isIndexed
  • isHidden
  • isLookup
  • lookupTableName
  • isLowercaseAllowed
  • isPassword
  • isRequired
  • isReadonly
  • defaultValue
  • maxValue
  • minValue
  • maxLength
  • displayName
  • description
  • displayFormat

You probably also need to make provisions for the metadata version control by adding a project and version field or similar extra fields to each table.

Describe metadata in XML

Before we extract metadata into the XML document, we must agree on its structure. For that purpose, we can use one of the two existing metalanguages: DTDs or XML Schemas.

DTDs are somewhat old fashioned, mostly due to their limited typing capabilities; however, they are simple, compact, and readable by the human eye. Perhaps that is why they remain quite popular. On the other hand, XML Schemas offer more advanced features, like inheritance and strong typing, at the expense of simplicity. For our purposes, a DTD works fine. Here is how we structure the metadata document:

<!ELEMENT bean (name,property*,comments?)>
<!ELEMENT property (name,type,comments?)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT type (#PCDATA)>
<!ELEMENT comments EMPTY>

This really means that a <bean> element must have exactly one <name> child and optionally any number of <property> elements. A <property> element in turn must have exactly one <name> and one <type> element. Both <bean> and <property> elements may have an optional <comments> element.

Now we need to build an XML metadata server that does the following:

  • Extracts the metadata from the data dictionary
  • Generates an XML document according to the above DTD
  • Communicates with clients via HTTP protocol

We use JSPs to create this simple XML metadata server. The server accesses the database directly from the JSP with the JDBC 2.0 core API, which comes with the Java 2 Platform, Standard Edition (J2SE). Please note that this is not the recommended approach. I've chosen it solely to stay focused on the functionality without distracting your attention with complex Web application design issues. According to Sun Microsystems' Java BluePrints, you should create JSPs with as little Java code exposed as possible. Normally you would implement something like this with a servlet or at least with a custom JSP tag from something like the Jakarta Taglibs project for the database access. My goal today, however, is to provide you with an easy-to-understand algorithm that you can try on your home computer with minimum effort.

To open a database connection, the java.sql.DriverManager class needs the following information:

  • Fully qualified JDBC driver class name
  • Database URL
  • Database user name (optional)
  • User's password (optional)

We provide this information as the HTTP request's parameters. Another important request parameter is the name of the bean whose metadata we are extracting. Listing 2 is the resulting JSP:

Listing 2. GenXml.jsp

<?xml version="1.0" encoding="ISO-8859-1"?>
<%@ page import="java.sql.*" %>
<%
try {
  
  String driver = request.getParameter("driver");
  String url = request.getParameter("url");
  String username = request.getParameter("username");
  String password = request.getParameter("password");
  String bean = request.getParameter("bean");
  
  Class.forName(driver);
  Connection con = DriverManager.getConnection(url, username,
password);
  Statement st = con.createStatement();
  ResultSet rs = st.executeQuery("SELECT * FROM Beans WHERE name='" +
bean + "'");
  
  // we expect exactly one record here
  if( !rs.next() ) {
      throw new IllegalArgumentException("No metadata found for the
bean " + bean);
  }
%>  
  <bean>
  <name><%= rs.getString("name") %></name>
  <comments><%= rs.getString("comments")
%></comments>
<%  
  rs.close();
  rs = st.executeQuery("SELECT b.* from Beans a, Properties b WHERE
b.beanId=a.id AND a.name='" + bean + "'");
  
  while(rs.next()) {
%>
      <property>
      <name><%= rs.getString("name") %></name>
      <type><%= rs.getString("type") %></type>
      <comments><%= rs.getString("comments")
%></comments>
      </property>
<%
  }
  st.close();
  con.close();
%>
  </bean>
<%
} catch( Exception ex ) { 
%>
  <exception><%= ex.getMessage() %></exception>
<%
}
%> 

For the JSP to function, it must be deployed on a Web server that provides a JSP/Java servlet engine. These days it is increasingly difficult to find a Web server that doesn't support Java technology. If you don't have one yet, maybe now is the time. Check out these free products that offer full JSP and servlet support:

  • Tomcat servlet container, developed by the Apache Software Foundation, is the official reference implementation for the Java Servlet and JavaServer Pages technologies. Tomcat is a subproject of the larger Jakarta Project.
  • iPlanet Web Server, FastTrack Edition is a joint venture between Netscape and Sun Microsystems. When Sun announced the End of Life (EOL) of the Java Web Server 2.0 on February 7, 2001, it began referring users to the iPlanet server. I particularly like the server's intuitive Web interface for remote administration.

Once you deploy GenXml.jsp on a Web server, you should be able to process a request in the following format:

http://<myserver>/<mypath>/GenXml.jsp?driver=<jdbcd
river>&url=<dburl>&username=<dbuser>&password=<dbpwd>
&bean=Product

You must substitute the variables with angle brackets for real values. Don't forget to use &amp; instead of &, when specifying the url parameter. If you do everything correctly, you should receive a response, which is essentially the Product bean's metadata expressed in XML:

<?xml version="1.0" encoding="ISO-8859-1" ?> 
<bean>
    <name>Product</name> 
    <comments>This bean represents a product that the company
offers to its customers</comments> 
    <property>
        <name>code</name> 
        <type>int</type> 
        <comments>the product inventory code</comments> 
    </property>
    <property>
        <name>name</name> 
        <type>String</type> 
        <comments>the product name</comments> 
    </property>
    <property>
        <name>testedOnAnimals</name> 
        <type>boolean</type> 
        <comments>the flag that indicates if the product was
tested on animals</comments> 
    </property>
    <property>
        <name>availableSince</name> 
        <type>java.util.Date</type> 
        <comments>the date when the company started offering this
product to its customers</comments> 
    </property>
</bean> 

If your request causes an exception, most likely due to invalid parameter values, the page generates a different type of XML document with the <exception> element:

<?xml version="1.0" encoding="ISO-8859-1" ?> 
<exception>No metadata found for the bean Produkt</exception>

If you're itching to see how this XML metadata server works, you may query mine, available here.

Transform XML into Java code

Before XSLT became a wonderful reality, you had to transform XML into Java like this: handcraft a class that utilizes a DOM or a SAX parser to extract the data from the source XML document, apply some hardcoded rules to it, and eventually spit out the Java code. Every time you had to change some rules, you would have to modify and recompile the handcrafted class. If you decided to transform into another language -- Smalltalk, say -- you would have had to build another class that does just that. This is doable, of course, but it's no fun, especially if you know about XSLT. XSLT is an official World Wide Web Consortium (W3C) Recommendation. It lets you transform an XML document into almost anything else. Traditionally, though, you use it to transform the document into HTML, text, or other XML document types. The XSLT processor performs the transformation according to the rules defined in the XSLT stylesheet, which is an XML document itself. If you need to change the transformation logic, you simply modify the stylesheet with a text editor.

Many different implementations of the XSLT processors are available today; check the W3C Website for references. We will use the Xalan XSLT processor developed under the Apache XML project. Because it's a Java-based implementation, Xalan comes with some features particularly attractive for a Java developer. One such feature is support for TRaX (Transformation API for XML), which is part of the Java API for XML Processing (JAXP). TRaX helps you build applications that utilize the XSLT transformation service through a standard, implementation-neutral interface.

Another goodie is the XSLT Compiler, a tool that compiles the transformation logic from a stylesheet into a lightweight Java class called a translet. You may then use Sun's XSLT runtime processor to apply the translet to an XML document and significantly increase the transformation performance.

Listing 3 defines a stylesheet to transform our simple metadata into Java source code. If you later decide to expand your metadata structure, you will have to reflect the changes in the stylesheet as well:

Listing 3. JavaBean.xsl

1 2 3 Page 2
Page 2 of 3