Tuesday, 25. January 2005
AppFuse w/ PostgreSQL and Tapestry01/25/2005
In my continuing search for a nice wizard
to set up a skeletal J2EE app a la MS Studio's "projects", I
decided to try Matt Raible's AppFuse.
It's painless if you use Matt's preferred development environment
(Tomcat/MySQL/Struts/Hibernate/Spring). Once you start veering off
the beaten path...
Some quick notes that probably should
be included in the Quickstart for Appfuse in case you want to use a different
database:
- in the lib directory, there is a lib.properties file that points to all
the database types and their jar files. The file actually points
to all the different jar files that AppFuse uses. It's a nice directory
structure in the lib subdirectory since each version of a jar is in a different
subdirectory; you can use this to test out different versions of various
jar files in your application w/o committing to it. The negative
is that when the .war file is generated via AppFuse, *all* the jar files
in the lib directory are included in the WEB-INF/lib directory, though
I'm sure this should be an easy workaround by modifying the build.xml to
only include jar files your project actually uses
- in the build.properties file, be sure to update the following items if
you want to use a non-MySQL database:
database.jar
database.type
database.name
database.username
database.password
database.host
hibernate.dialect
database.driver_class
- this set of settings only has to be set if you are using MySQL because
it's the only database that lets you create databases using JDBC:
database.admin.username
database.admin.password
database.admin.url
- when installing tomcat, set the password for the admin user to admin;
if you use a different password, remember to change the tomcat.properties
file
- don't change database.properties; despite the name, this file is generated
by build.xml
- make sure you do a recursive search on "appfuse" to make sure
the appgen/rename process got everything
Of course, I just *had* to go try to use HypersonicSQL since I just wanted
a simple test application and didn't need a high powered database for a
test application. Hibernate doesn't work that well w/ HypersonicSQL.
Hibernate generates table names w/o quotes (which HypersonicSQL for
some reason then decides to uppercase), then it generates SQL statements
naming the tables using quotes and the table names are in lowercase so
HypersonicSQL can't find the table :-P
The next database I tried was PostgreSQL since PostgresSQL 8.0 had just
come out so I was hoping it'd fix some of the quirks mentioned in the AppFuse
Wiki. Sadly, you still can't create a database via JDBC. Automatically
creating a database is one nice thing an EJB container can do which Hibernate
can't do. On Matt's Wiki, he mentions that you have to change the
Hibernate generator-class to "increment" instead of "native"
or the dbunit tests fail; this is disappointing since PostgreSQL is more
SQL compliant than MySQL, but luckily this is in all the AppFuse code already.
After switching to PostgreSQL, some of the unit test emit weird errors
like this but all the tests pass:
uh oh, user 'badusername' not found...
ERROR: duplicate key violates unique constraint "app_user_pkey"
Other useful ant commands:
ant db-create - creates a database using JDBC (quick way to find out if
your JDBC drivers are in the right place)
ant db-prep - generates the DDL for your database into the file
ant setup-tomcat - sets up tomcat by copying the transaction manager and
the JDBC driver over to the right place and copying the application's configuration
xml file over to tomcat
ant test-all - runs all unit tests
I do wish the build.xml were broken down into more pieces so you can concentrate
on the pieces you have to use. It currently has a bunch of targets
for testing, packaging up the original AppFuse distribution, creating documentation,
hooking into Tomcat, substituting in various web frameworks (Tapestry,
Spring, etc.), Wiki gathering, etc. The testing targets can probably
be moved into the test subdirectory. The Tomcat specific targets
can be moved into a separate tomcat.xml file so people can contribute version
for other containers like JBoss, Geronimo, etc.
Also, some of the files should be put into a conf directory to indicate
that they should be modified or at least reviewed. E.g., tomcat.properties,
build.properties, app-settings.xml. Files that are imported or used
by build.xml directly (e.g., compile-jsp.xml, properties.xml) should go
into another subdirectory so that they don't clutter the top level directory.
Next up is trying to switch web UI frameworks. If you switch to Tapestry
or one of the other UI layer other than the default of struts, be aware
that the rename utility on the AppFuse site that lets you change all the
"org.appfuse" packages to something else may not get all the
files. In the Tapestry case, the web/web-inf/tapestry.application
file didn't get the rename done to it so you have to do the rename by hand
or Tomcat will complain that "org.appfuse.webapp.action.BaseEngine"
couldn't be found. Also, all the *.page files in the web/pages/ directory
have references to "org.appfuse" that have to be changed to your
own package; this should be pretty obvious because Tapestry will point
you to the pages that it can't find classes on instead of displaying the
typical Java exception stack
I'm not sure if it can be done, but what would be nice is if there were
separate build.xml files for each of the different frameworks that Matt
integrated so all the war files can be built at once or separately w/o
running the "install-tapestry" target from the top level of your
application; that way, testing of all the frameworks can be integrated
as well as allow an easy way for you to build versions of your applications
for multiple frameworks at once to do your own comparisons.
After all that, I finally got the web application to show up at the url:
http://localhost:8080/mytestapp
and found my first bug. If you add a user (either as admin or by
signing up) w/ a duplicate username or email address, you get a SQL exception
dump instead of a nice error message, which again makes me wonder if people
use PostgreSQL w/ Hibernate because this cryptic error that it prints out
should probably be fixed in the Hibernate plugin for PostgreSQL (why isn't
it calling getNextException and dumping that instead?):
ERROR [http-8080-Processor23] JDBCExceptionReporter.logExceptions(58) |
Batch entry 0 insert into user_role (username, role_name) values (user4,
tomcat) was aborted. Call getNextException to see the cause.
The fix in the AppFuse Wiki is to disable Hibernate's batch processing
so you can see the errors which doesn't seem like the right thing to do
because your app may have SQL exceptions in the field (if it escapes testing)
and you'd like to get the real exception message :-P