Archive for February, 2005

Persistence layer unit testing best practices

Friday, February 25th, 2005

I've been doing some searching on best practices for unit testing the persistence layer with DBUnit and I'm interested in people's feedback on my policies or pointers to policies others have created.

For example I've been thinking of making our company's policy as follows:

1. Unit tests can rely on certain rows with certain Id's being in the database in pristine format (loaded with DBUnit).
2. Unit tests can select DBUnit loaded row id X in table foo, reinsert it with a new id, and then change it or delete the newly added record, but should leave DBUnit loaded records pristine.
3. It's not OK for a unit test to modify DBUnit loaded rows because other unit tests expect those rows to be there.
4. A unit test cannot depend on a certain number of rows being in the database because it will likely contain the DBUnit loaded rows as well as some unknown number of rows that may be left over form other unit tests.
5. In general it's good policy for a unit test to clean up new data it added during the test but not absolutely required because when a unit test fails it may leave bad data behind.

Do these policies seem reasonable or are there other places I should be looking for these types of best practices?

Building one big fat jar file

Friday, February 25th, 2005

I've been working on software that generates a very large XML feed for customers. My plan was to have it run by a cron job and regenerate the feed once a week. I wanted my project to produce one file (including dependencies such as Spring and Hibernate) that a release engineer could drop onto the production system rather than an archive that needed to be untarred and installed. Out of stuborness I also wanted to be able to run it from cron without wrapping it with a shell script. Basically I wanted to be able to type “java -jar XmlFeed.jar” and just have it work.

I was starting to think it was going to take some significant effort to make this happen when I stumbled across Codehaus Classworlds Uberjaring. Fortunately there was a Maven Uberjar plugin so I added a maven.uberjar.main property to my project.properties file and within a couple of minutes of starting to look for a solution I had built a working jar file. Alas, I discovered the Classworlds classloader was really slow and my program ran about 15x slower than normal.

I started searching again and next stumbled across the Maven JavaApp plugin which does the same thing. I installed the plugin and within about a minute I again had one big jar, and with the Java App approach it ran at normal speed. Success!

I won't argue that Maven is the coup de gras of build systems but the fact that within a few minutes I was able to use 2 different approaches to build a big jar demonstrates the power of Maven plugins and the approach of building software by defaults. I would be just as happy if there was a repository of Ant macros that did the same thing. Until that day comes though Maven has pleasantly surprised me numerous times with plugins that have gotten me up and running quickly.

Note: I'll also add that I discussed this with a friend later and he suggested as an alternate approach that I could have also built an EAR file and run it from the command line with a J2EE app client (not within an app server).

And the best MVC framework is….

Sunday, February 20th, 2005

Norm Deane is going to be pulling a Matt Raible by reimplementing the same software with all of the major MVC frameworks (Struts, Spring MVC, Webwork, Tapestry, and JSF). I'm excited to see his progress as he writes about it, it's a lofty goal but a worthy one IMHO!

I've been going through the same process in trying to evaluate the best web framework for our company going forward. It's a tough decision because there are more factors at play than simply good technology. I don't want to select the beta max of MVC frameworks.

So far I've written sample apps in Struts, Spring MVC, and Tapestry. Next I'll be embarking down the JSF road. The sample app only gives you a taste which is why I like the fact that Norm is redoing a real app. For example I found the learning curve steepest with Tapestry relative to Struts and Spring MVC but the more I used it the more I liked it.

There's a lot to be said for working with something until you know enough about it to honestly critique it. I'm not convinced that my sample app approach is sufficient for me to say which framework I really prefer since I still feel like a novice in most of them. It's unlikely I'll ever have the luxury of time to really become proficient enough in even 3 major MVC frameworks to make a well informed assessment. My decision will be made based on a combination of my own experience writing these sample apps, opinions of colleagues/fellow bloggers I respect, perceived longevity/eventual popularity of the framework, and ability to hire people that know the framework.

As a business decision I feel like on the hiring front Struts is the safest bet at the moment, I get more Struts resumes than I can count when I'm hiring java web developers. On the longevity and developing for the future front my money is on JSF because there are big companies behind it as well as open source advocates that like it. On the cool technology front I think the award should go to Tapestry, not only does it seem to me (a novice) to be a cool component/event driven MVC framework, it has a non-invasive templating language (reminiscent of Enydra's XMLC) that I long for every time I work in JSP.

So which MVC framework will we be choosing at my company? I don't know but I've only got another 3 weeks or so to decide and will let you know what we end up with.

MySQL and not null is not good!

Thursday, February 17th, 2005

On a daily basis I continue to be unimpressed with MySQL when using MyISAM tables. Today I discovered that if you set a field to be non-nullable and then do an insert without specifying the field, MySQL happily completes the insert when I should be getting an error. Here’s an example:

create table test_state (
id bigint not null auto_increment,
date_created datetime not null,
state varchar(2) not null,
primary key (id)
);

insert into test_state (date_created) values (now());
insert into test_state (state) values ('WY');

select * from test_state
+----+---------------------+-------+
| id | date_created        | state |
+----+---------------------+-------+
|  1 | 2005-02-17 09:25:28 |       |
|  2 | 0000-00-00 00:00:00 | WY    |
+----+---------------------+-------+

You’ve got to be kidding me! It would be better if “not null” had no meaning and were simply ignored, whereas this confuses the issue even more. Now, I don’t profess to being a MySQL expert by any stretch and I know this is probably solved with InnoDB, but IMHO this shouldn’t work in MyISAM either! Oh how I miss the days of working with PostgreSQL, Oracle, and DB2.

That said I do enjoy the low administrative overhead of MySQL, it’s low memory footprint, and the excellent gnu readline and pipe support built into the client.

Maven versus Ant

Saturday, February 12th, 2005

Almost every new web project starts with writing the Ant code to build a WAR. This build process is duplicated with some variance on every web application project I've ever worked on that uses Ant.

We lift our noses when we find developers copying and pasting code, yet why do we think it's OK to copy and paste the build process. In my opinion it is not! Using Maven I can start a new project with a nice build process and managed dependencies in no time flat. In Ant, to do the same, I'll copy and paste a build.xml I've used on another project and then begin copying jars into my lib directory. All of a sudden I have tremendous duplication between two projects. What's worse though is that almost every other company developing Java web applications is also writing the build.xml to build their artifacts (jars, wars, and ears) in almost the same way.

I want a build system that manages my dependencies and already knows how to run XDoclet, Unit tests, build javadoc, build a WAR, etc… As long as I put my files where they are expected to be (read sensible defaults) then this build system should be able to build my project almost out of the box. Of course when I need to do custom operations or override a default I need to be able to do that using a property or by breaking out into Ant. These are the reasons why I use Maven. I admit it's not a highly polished and well documented system (which Ant is) but I think Maven is the direction I want to see higher level build systems moving in.

I think Ant and Maven have a very complementary relationship. Maven is a nice high level build system and Ant is a nice low level build system and since Maven let's me use Ant when I want to I'm happy.

Trails (Tapestry, Spring, XDoclet, and Hibernate) video

Friday, February 11th, 2005

Chris has posted a 10 minute video strutting some of the nice features of developing with Trails (built on Ant, Tapestry, Spring, XDoclet, and Hibernate). This was inspired by the now famous Ruby on Rails video.

In theory I like the concept that you can prototype a web application and then fill in the details such as unit tests, dao layer, business logic, business facades, and web layer as your needs for complexity grow. Other solutions that I've seen in the past that attempt to provide this type of RAD environment ultimately seemed to hinder flexibility later in the project. With Trails that doesn't appear to be the case though.

Hibernate mapping one class to multiple databases

Saturday, February 5th, 2005

At work our data is state specific and for each state we have a lot of data. It was decided to create a separate database for each state in MySQL long before I joined the company and now we have a lot of legacy infrastucutre that depends on it. In Oracle I would have solved it by creating a partitioned table in which you can partition the data based on the value of a field (yet still have it be one table). I believe partitioned tables are a feature of MySQL cluster of which I hope to learn more about in the future.

In moving the company from Perl to Java one of my challenges has been creating the OR mapping layer. I chose Hibernate and therefor wanted the ability to use Spring's Hibernate DAO support for the generic unchecked data exceptions. I also needed to have one class map to multiple databases depending on which state the data was specific for. Rather than make the project specific to our domain though I figured this is a general problem that other companies using open-source databases have solved in the same way. Especially when their data gets too large for one database and can be easily partitioned. In our case we have a main shared database and then state specific databases to store all state specific data (which is the bulk of our data).

To solve the OR mapping issue I created a generic maven / hibernate / xdoclet / dbunit kickstart project with a generic BaseDAOHibernate layer based on Appfuse as well as my own BaseDAOPartitionHibernate to easily create DAO's for partitioned tables. I still use Spring's Hibernate DAO support, but I had to add a new HibernateDaoPartitionSupport class based on Spring's HibernateDaoSupport class.

It's not very polished yet but if you're trying to use hibernate with identical schemas across multiple databases hopefully my partition-dao-kickstart-0.1.tgz project can help you get on the right track.