Recent Posts

RSS Feeds

Struts Live @ TSS

Struts Live 1.0 is now online and a free download from TSS.

Discussion about the book is here and the download is here.

I'm sure Rick would love your feedback!

Permalink     No Comments

Snow in May!

It snowed this afternoon!

Anyone in Denver that reads this blog should probably make tracks for ABasin tomorrow morning. Should be a beatufil day for spring skiing!

Permalink     No Comments

JSF 1.1 - Changes relatively small...

Well an updated version of JSF (1.1) has shipped. Thankfully the changes are minor and wont affect the book content.

pheewww that was close... When I noticed the post at TSS I almost keeled over. I really don't want to rewrite the JSF book again! :-)

Permalink     No Comments

Grrrrrrr, what is the problem with Sun's distributions

JSF was updated yesterday and the recommended download will only install on Windows, Linux & Solaris. Has the community complained so much about tar/zip that they feel like there must be a .exe installer? I hate to complain but I guess I sort of believed WORA thing. I'm sure if I could get the thing to decompress it would run but what is wrong w/ .zip files any way?

ok I'm off my soap box now

You can get a .zip of jsf 1.1 here

Permalink     No Comments

TMC rids it's self of JBoss Inc.

The posting is here. Good ridance, may JBoss Inc fade silentely into the night...

Come Geronimo, come!

Permalink     No Comments

Testing Hibernate Mappings with DBUnit

Recently I've been very quite on my blog cause I've been spending every spare moment on a research project, more on that later...

Part of that project has been to build a model of a stock tracking service (modeled on Yahoo Finance's portfolio tracking service). So I have a POJO model persisting through Hibernate. In order to test this model I chose DBUnit

  1. because I wanted to learn the DBUnit 2.0 stuff
  2. I wanted to figure out a 'good' way to use it with hibernate
  3. I wanted a way to make sure I had all those XDoclet tags right

Figure 1 is a bit of UML describing the object model

Figure 1

My main testing goal was to make sure the Hibernate mapping files are correct, since that has been a major pain in the neck for me in the past. (For example, as I update the XDoclet tags I'll get something wrong but without a good test suite I did not notice until much later and had forgotten about the XDoclet tag I changed, thus I'll spend a lot of time running down rat trails). So the focus of the tests in this post are not really intended to test business logic of the POJOs but intstead to make sure the objects go into and out of the database as expected.

The basic process goes like this

  1. Define the data in a DBUnit xml file that will be used during the test
  2. Define the data in a DBUnit xml that you expect to see after the test
  3. Use DBUnit to put the starting data into the db
  4. Run the test
  5. Use DBUnit to get the actual data from the db
  6. Use DBUnit to assert the equality of the actual data with the expected data

So what is needed is a test that will create thw whole object graph shown in Figure 1 then put it into the database the make sure that it all made it. Forutunately DBUnit has an easy XML syntax for building the expected data. Here is a small sample file that I used in these tests.

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
  <st_site_user passwd="insert-test" uname="insert-test"/>
  <st_portfolio name="insert-test"/>
  <st_position version="0"/>
  <st_trade quantity="100" trade_date="2004-01-01" price="33.7600" commission="14.9500"
  	        kind="B"/>
  <st_security symbol="ibm" company_name="int buz mach"/>
</dataset>

These XML elements are named after the tables, the attributes are column names, the values are the values to put into the rows. So each of these elements represent one row in the database. You can specify any number of rows for each table with this schema, DBUnit will take care of any type conversion needed. In DBUnit speak this group of rows is called a dataset. Datasets are central to the assertion stuff that DBUnit provides. Basically this XML dataset is used to verify against the data in the database. The data in the database is retrieved into a DB dataset then the two datasets are asserted to be equal.

Now onto the Java code that I used to test the hibernate mappings. I start out with a base class that has the DBUnit required functionality as well as some custom assertion methods. Here is the code.


public class HibernateBaseTest extends TestCase {
  protected Session session;
  protected Configuration config;

...

  protected void setUp() throws Exception {
    super.setUp();
    config = new Configuration().configure("/hibernate-test.cfg.xml");
    SessionFactory sf = config.buildSessionFactory();
    session = sf.openSession();
    // Load empty data from an XML dataset
    InputStream is = getClass().getClassLoader().getResource(
        "net/dudney/domain/hibernate/empty-test-data.xml").openStream();
    IDataSet dataSet = new FlatXmlDataSet(is);
    DatabaseOperation.CLEAN_INSERT.execute(getConnection(), dataSet);
  }

  protected void tearDown() throws Exception {
    super.tearDown();
    session.close();
  }

...

  protected IDatabaseConnection getConnection() throws Exception {
    ConnectionProvider connProvider = ConnectionProviderFactory
        .newConnectionProvider(config.getProperties());
    Connection jdbcConnection = connProvider.getConnection();
    IDatabaseConnection connection = new DatabaseConnection(jdbcConnection);
    return connection;
  }

  protected void assertDBAsExpected(String expectedDataFile, String[] tableNames)
      throws Exception {
    // Fetch database data after executing your code
    IDatabaseConnection conn = null;
    try {
      conn = getConnection();
      IDataSet actualDataSet = conn.createDataSet(tableNames);
      // Load expected data from an XML dataset
      InputStream is = getClass().getClassLoader().getResource(expectedDataFile)
          .openStream();
      IDataSet expectedDataSet = new FlatXmlDataSet(is);
      for (int i = 0; i < tableNames.length; i++) {
        ITable expected = expectedDataSet.getTable(tableNames[i]);
        ITable actual = actualDataSet.getTable(tableNames[i]);
        // converts actual to use the same colums as expected
        ITable trimmedActual = new CompositeTable(expected.getTableMetaData(),
            actual);
        Assertion.assertEquals(expected, trimmedActual);
      }
    } finally {
      if(null != conn) {
        conn.close();
      }
    }
  }
...
}

The constructor and a few utility methods were elided from this code so we could focus on what is actually interesting. The first method of import is the setUp method. In here hibernate is initialized and we use DBUnit to initialize the database. It would be better to initialize the Hibernate Configuration and SessionFactory in a TestDecorator but I was feeling lazy and did not want to take the time to do that. (Basically the Decorator would allow the relatively expensive operation of loading the Hibernate configuration stuff only once for the whole test. I'll try to get that updated before I post the code.)

The next method gets the IDatabaseConnection for DBUnit. This uses the same connection info that Hibernate uses so we don't have to maintain it in multiple places.

Next is the custom assertion code that I think makes testing easier. The first argument is a string that identifies the XML file that contains the expected dataset. It is expecte to be on the classpath and I actually jar the xml files up with the junit tests. The second argument is the list of table names that you want to make assertions about. The method then loads the data from the database based on the list of tables provided. (With huge data sets this of course won't work very well). Next the expectedDataFile is loaded from the classpath and turned into a dataset. For each table specified the code then 'trims' the actual dataset so that the assertions can be made. This is an important step for cases where let either the DB or Hibernate generate the primary key values for you. In this example all the keys are generated by Hibernate through the hilo generator. Since we can't know the id's up front we don't put them into the expectedDataSet. They do come from the db though (I don't know of a way to get DBUnit to ignore a particular column) so we have to make the actual data drop the columns that we can't compare on, thus the call to create a CompositeTable. Now that we have the trimmed data we can make the assertion about it being what is expected.

Now on to a particular test, here is the code that tests that a simple insert of the whole graph goes as expected (the XML from above is the insert-test-data.xml file).

  public void testInsert() throws Exception {
    // user
    SiteUser user = createUser("insert-test", "insert-test");
    // portfolio
    Portfolio main = createPortfolio("insert-test");
    // position
    Position position = createPosition();
    // security
    Security ibm = createSecurity("ibm", "int buz mach");
    position.setSecurity(ibm);
    // trade
    GregorianCalendar cal = new GregorianCalendar(2004, 0, 1);
    Trade trade = createTrade(new Integer(100), cal.getTime(), ibm,
        createBigDecimal(14.95, 4), createBigDecimal(33.76, 4));
    // finalize object graph
    position.addTrade(trade);
    main.addPosition(position);
    user.addPortfolio(main);
    // save and commit
    session.save(user);
    session.flush();
    session.close();
    // make sure its as expected
    assertDBAsExpected("net/dudney/domain/hibernate/insert-test-data.xml",
        new String[]{"st_site_user", "st_portfolio", "st_position", "st_trade", "st_security"});
  }

All this test is doing is creating a single instance of each of the objects then inserting the whole graph into the DB (via save, flush & close) then using the assertion method we looked at earlier.

Some tricks to make using DBUnit to test your Hibernate mappings easy

  • Define a root test class to keep all the 'reuseable' code in
  • Put your XML datasets on the class path so you don't have to hard code any real paths (I use this trick in Cactus tests as well so that I can access the XML datasets even on my app server)
  • Create at least one test (does not have to be a single test, a suite will do) that exercises all the parts of your mapping files (i.e. the whole object graph). The test does not need to be extensive, remember its just to make sure that your mapping is correct. For really big models I'd break the code up into multiple test methods that can each focused on a 'root' type and all its dependents (SiteUser and Security are the two roots in the graph displayed in Figure 1. Another way to look at it or think about it is anything that does not or should not have a cascading delete comming to it can be thought of as a root)

Happy Hibernating

Permalink     6 Comments

Catalina AKA Tomcat Ant tasks

I'm a big fan of the catalina/tomcat ant tasks so i was excited to see Matt's post. So even though I have alot of stuff I should be doing I just could not resist taking the new stuff for a spin.

Well its not quite as straight forward as the nifty Cactus Integration is. (Matt sited the cactus integration in his bug report.)

In short the catlina.tasks file is burried deep in the zip file at org/apache/catalina/ant/catalina.tasks. Which by its self is not that big of a deal, but it references the Jasper JSP Compiler so you must have more than the catalina-ant.jar file in your classpath.

Here is the classpath that worked for me, however I did not use the jasper2 (JspC) task so there could be some additional classes required for that task to work.

<path id="tomcat.classpath">
  <pathelement location="${tomcat.home}/server/lib/catalina-ant.jar"/>
  <pathelement location="${tomcat.home}/common/lib/jasper-compiler.jar"/>
  <pathelement location="${tomcat.home}/common/lib/jasper-runtime.jar"/>
  <pathelement location="${tomcat.home}/common/lib/servlet-api.jar"/>
  <pathelement location="${tomcat.home}/common/lib/commons-logging.jar"/>
</path>

and here is the taskdef element

<taskdef resource="org/apache/catalina/ant/catalina.tasks"
          classpathref="tomcat.classpath"/>

Permalink     No Comments

Eclipse Refactoring on JDJ

My second column has hit the JDJ site. Its about the Change Method Signature refactoring and part of the Eclipse 3 Live title.

Permalink     No Comments

Mastring JavaServer Faces code open sourced...

I'm pleased to announce that the source code for Mastering JavaServer Faces has been open sourced @ java.net.

The only way to currently get at the code is via CVS. If there is enough interest I'd be glad to zip it up and make it a single download

Permalink     No Comments