Matt RaibleMatt Raible is a Web Architecture Consultant specializing in open source frameworks.

10+ YEARS


Over 10 years ago, I wrote my first blog post. Since then, I've authored books, had kids, traveled the world, found Trish and blogged about it all.

Trails - like Rails, but with Tapestry, Spring and Hibernate

I've been thinking about Rails ever since I wrote a post about it on Monday. The main reason is because of Dion's comment:

Matt - You should follow the lead and do a video of setting up a simple app using AppFuse.

This might sounds like a good idea, but if I did it right now in AppFuse's current state, it'd be a disaster. The reason? Because you have to manually create a whole bunch of classes to do CRUD on a database table. Here's a list of new classes needed for adding a new "person" table.

  • model.Person
  • dao.PersonDAOTest
  • dao.PersonDAO
  • dao.hibernate.PersonDAOHibernate
  • service.PersonManagerTest
  • service.PersonManager
  • service.impl.PersonManagerImpl
  • webapp.action.PersonActionTest
  • webapp.action.PersonAction
  • web/pages/personList.jsp
  • web/pages/personForm.jsp

The last two JSPs can be generated, but that's still a buttload of classes (9) just to CRUD (and test!) a database table. Not too mention all the files you need to edit for Spring and i18n.

  • dao/hibernate/applicationContext-hibernate.xml
  • service/applicationContext-service.xml
  • test/web/web-tests.xml
  • web/WEB-INF/classes/ApplicationResources_en.properties
  • web/WEB-INF/menu-config.xml

Result: to CRUD a database table using AppFuse you have to create 11 new files and modify 5 existing files. 16 files. What a beotch, huh? If I made a video of this - it'd be 20 minutes long! While this might make AppFuse look silly, it's really more of a symptom of the patterns we have in J2EE and how we're supposed to architect our apps. 3 tiers, test-driven, loosely-coupled and internationalized. Of course, if I was focused on fast and efficient, I could do this all with 1 JSP and JSTL's SQL tags. Everyone would slap my hand for not following patterns, but I'm willing to bet it'd work just as well and be just as fast. But I digress.

There have been a fair amount of requests (and some patches submitted) to generate and modify all of the files listed above. For the most part, I've frowned upon adding such a feature because I think if folks can run "ant generate -Dmodel=Person" - they'll end up with a whole bunch of code that they know nothing about. Sure there's the tutorials, but folks will quit reading those. Instead, they'll create a whole slew of POJOs (maybe even using Middlegen) and run "ant generate" on all 50 of them. Poof - now they've got 550 new files to maintain. Talk about a maintenance nightmare. Even worse - a support nightmare for me.

Nevertheless, if I wanted to create a cool video for AppFuse, I'd spend a few days writing this code-generation engine. Then I could show how you could create the data, service and web layer (including UI) in a matter of seconds. It'd be cool and folks would dig it. I'm still considering it, but I'm also leary of the resulting support fiasco. Maybe I could just say "use at your own risk". ;-)

A while back, I saw Erik Hatcher suggest a better solution than code-generation. I can't remember what he called it, but it was something like "meta-data dynamic rendering". The idea is that your application reads the metadata of a table (or POJO) and renders the appropriate UI for it. I loved the idea as soon as I heard it. I've always wanted a way to dynamically render the UI rather than writing HTML. Of course, I still want the ability to edit the templates and HTML since I fancy that sort of stuff. I don't like writing HTML for each row in a form, but I do like tweaking the HTML and CSS to look good.

Earlier this week, I saw the concept in action with Rails and its demo. IMO, something like Rails would never fly in Java because it appears to be tightly coupled to the database and only MySQL, PostgreSQL, and SQLite are supported. The Java community always seems to pride itself on database abstraction, partly due to JDBC and its ability to connect to anything that has a JDBC Driver. Ruby will probably catch up someday, but right now it appears to be looking for something like JDBC.

Then along comes Trails, which made me smile earlier today when I first read about it on the tapestry-dev mailing list.

I've been working on a project called Trails that uses Tapestry quite heavily and I thought it time to start soliciting feedback. Trails is a domain driven development framework that uses Tapestry, Spring, and Hibernate. Trails is very much in it's infancy, but the current version is functional and should give people a rough idea where I am heading. It's my first real forray into Tapesty and I have really found Tapestry a joy to work with.

Trails is very much like Rails, except that it doesn't talk directly to a database table. Instead, it talks to your domain objects that you mark up with XDoclet/Hibernate tags. To test it out, I dropped a User.java file into the org.trails.demo package, marked it up with XDoclet and deployed. It didn't work at first because the .hbm.xml files are explicitly listed in Spring's applicationContext.xml. I changed the "sessionFactory" bean to use the following and wammo - success! I could list and CRUD the table that my User object was mapped to.

  <property name="mappingDirectoryLocations">
    <list>
      <value>classpath:</value>
    </list>
  </property>

Trails is very cool, and I'd love to incorporate it into AppFuse or Equinox. Does an LGPL license allow me to do this? The one problem I can see with adding it is that it's specific to Tapestry and Hibernate, which doesn't always suite folks. I think developers might be willing to change because this solution will vastly improve their development productivity, but who knows. I think the best solution would be to offer this option in AppFuse/Equinox, but also offer the current manual and code-generation options. The holy grail would be the ability to plug in iBATIS or JDO instead of Hibernate. In addition, using Struts, Spring, WebWork or JSF instead of Tapestry would have folks clammering to use this stuff.

Mad props to David Heinemeier Hansson and Chris Nelson - you guys are developing awesome software.

Posted in Java at Oct 29 2004, 01:50:30 AM MDT 22 Comments
Comments:

I've written something similar that uses tag libraries to talk to hibernate directly, which works in a similar fashion to rails (in fact, I did ask your advice on OGNL v. Groovy for the graph navigation). I ended up with a small application that only need the hibernate & groovy jars (plus required jars for hibernate) and I could knock up application, almost like naked objects, very quickly. Whilst it works well for creating demos etc., it falls fla on it's face when the real requirements arrive. Trying to impose contraints on the db access, for example, becomes very difficult. Trying to validate input also becomes very difficult, not on a field by field basis (I added a "match" attribute on my tag for regex), but on a dependency basis, i.e. last name is only valid is first name has also been filled in. In short, rails creates an impressive demo, but the longer video on the site talks about filters to impose addiational functionality, like AOP interception, which is built in to ruby. So now you have reflection to look at each bean, hibernate to persist it, and AOP to impose contraints. Ultimately, it becomes very compilcated very quickly for the person creating the framework. PS. I'm sure it would be possible to use something like dynaop to help much of this process and use the new tag liraries and tag files in servlet 2.4 to create it again. I did the original cut in 2.3, which had a dreadful API for tags. If anyones interested, there is the potential to create a cleaner implementation again, that could be very light weight. Perhaps we could get together and put something together. How about "jails" as the project name ;)

Posted by analogueboy on October 29, 2004 at 03:14 AM MDT #

I made a simple app that generates most of those files you described for a simple crud use case like you mentioned, works out pretty well but yeah, there are alot of files involved in just handling crud operations.

Posted by Francisco Hernandez on October 29, 2004 at 03:29 AM MDT #

Hi Matt. Thanks for the kind words. I'm honored. I'd just like to correct one misconception about Rails.

When you write "something like Rails would never fly in Java because it appears to be tightly coupled to the database" that's not really true. Active Record (the ORM part of Rails) has a database abstraction layer embedded called Abstract Adapter, which all the specific adapters are implementing transparently.

Hence, your Rails application is not tightly coupled to a specific database, but rather to the Abstract Adapter interface. Since concrete adapters are just ~100 lines of code, it's pretty easy to implement your own adapter if the current offering isn't sufficient.

The PostgreSQL and SQLite adapters were implemented one the same day by ONE person. There's a Microsoft SQL Server adapter just around the corner and I've heard of people working on Oracle and DB2 adapters too.

So the only tightly coupling going on is if you write database-specific SQL. On that account, I'm actually very sympathetic to Jeremy Zawodny's Database Abstraction Layers Must Die!. Chasing database abstraction without a specific business case asking for it is just something You're Not Gonna Need and it's certainly not doing The Simplest Thing Possible.

Posted by David Heinemeier Hansson on October 29, 2004 at 04:39 AM MDT #

Hi Matt, I've (also) created a similar framework, albeit it only offers read-only functionality. Chosing your MVC framework or view technology is more like a religious choice now a days, but I'm glad to learn that everybody's sticking to the Spring/Hibernate tandem. I think there is an opportunity here to create a universal service/dao business layer for this pattern, which could use the Trail implementation as a basis and could include additional features like dynamic query generation for searching and sorting. Add paging to the mix and we would have a nice common ground to start from. Although my implementation of the approach was inspired by a business requirement, this functionality specifically is of intrest to me in the early stages of a project, where you want screens to work with your database.

Posted by bitnix on October 29, 2004 at 05:25 AM MDT #

In response to the analogueboy's pledge, I'm intrested in putting something together for this purpose. I'm on holiday next week but if anyone contacts me I'll be happy to get back to you when I return.

Posted by bitnix on October 29, 2004 at 05:54 AM MDT #

Okay, so I must disclose my e-mail adress. It's bitnix at primary dot be. Let the spam come to me.

Posted by bitnix on October 29, 2004 at 05:55 AM MDT #

Interesting that you mention code generation. One of my side projects is Diesel, a generic code generator that uses Velocity with some XML config files. I had been thinking that it would be cool to create a Diesel cartridge for either AppFuse or Equinox. Hmm... the weekend is coming up...

Posted by Dave Noble on October 29, 2004 at 09:08 AM MDT #

Matt, what about the /extras/daogen scripts I contributed a while back. That generates all the dao & service related components as well as Test stubs for them.

Posted by Lance Lavandowska on October 29, 2004 at 10:10 AM MDT #

Lance - I'll definitely be using what you wrote as a starting point for the "appgen" module.

Posted by Matt Raible on October 29, 2004 at 10:13 AM MDT #

Doh, sorry, should have read further before opening my mouth.

Posted by Lance on October 29, 2004 at 10:13 AM MDT #

Rails will soon have an adapter for SQL Server. It was completely finished, but the guts of ActiveRecord changed under me, and I haven't had time to update it. It should be soon, though. But as for the Rails model flying in Java, the static nature of Jave, I think, prevents it. The way ActiveRecord works is to have all method calls for columns, etc, get routed through the method_missing method, which all objects in Ruby inherit, and then discover if the method that was called is a column, and do the right thing. I know you can do some funky dynamic mojo with CGLib and the like, but I don't think you can get anywhere near the Ruby level of dynamicity in Java.

Posted by Joey Gibson on October 29, 2004 at 08:14 PM MDT #

Hey matt - collapsing your dao layer into your biz layer would save you 3 to 4 classes per entity, and probably wouldn't get you slapped (I wouldn't slap you anyway.) Of course, you could also scrap the tests if all you're doing is CRUDing by delegating to Hibernate, but I didn't say that. ;-)

Posted by Keith on October 29, 2004 at 09:47 PM MDT #

I was intrigued by Keith's comment about accessing DAO's directly in the business logic. My feeling is that a manager layer is only occasionally necessary when it adds real value/functionality on-top of the DAO layer. Otherwise it's just an unnecessary pass-thru layer sending the model objects right through to the DAO layer without doing anything. Matt, I'm curious what your thoughts and experiences have been regarding when to use a manager layer?

Posted by Todd on October 29, 2004 at 11:11 PM MDT #

The main reason for the Manager layer is to wrap its methods with transactions so that multiple calls to DAOs (in a Manager's method) will take part in the same transaction. I've seen some folks get around this and wrap their Actions (i.e. WebWork) with transactions.

Posted by Matt Raible on October 30, 2004 at 12:48 AM MDT #

Collapsing DAO into biz, keeping transaction demarcation at the biz layer is a viable design worth considering with apps with little to no business logic (CRUDers). Also, no one says you have to create a new DAO implementation for each entity--a single facade for the entire application is fine for a small app. Spring's Petclinic is a good example of this. I generally start with a single biz layer and then extract out a lower-level DAO layer only if I need it, for two reasons: to demarcate transactions for biz operations that span multiple DAO operations (what Matt said), AND to decouple my biz logic workflow from my data access technology (which simply not a concern for apps with little biz logic since the biz logic IS the persistence logic). Note with a single biz layer you can still get transaction demarcation for biz operations spanning multiple DAO calls: just have the public interface implementation (which executes transactionally) delegate to private DAO methods within the biz implementation. Don't introduce more layers than you have to, that's what I say (start simple and scale up by refactoring only if you see the need.)

Posted by Keith on October 30, 2004 at 08:24 AM MDT #

Matt, Thanks so much for your kind words. As I mention in the blog, Trails is still in the early stages and there is plenty of opportunity for folks to get involved. The 2 posters who were discussing starting their own Rails type project: I encourage you to look at Trails and see if you like where we're going, and if so, to join us. I also added a brief post on some of the upcoming features I see myself working on next.

Posted by Chris Nelson on October 30, 2004 at 09:08 AM MDT #

Isn't one of the goals of AOP in Spring to use interceptors to demarcate transactions (rather than having to create a facade like a Manager) to do transaction demarcation? Or is that approach sometimes too simplistic in practice?

Posted by Todd on October 30, 2004 at 10:39 AM MDT #

Spring has a "TransactionProxyFactoryBean" which at runtime can dynamically proxy any POJO to make it execute in the context of a transaction managed by JTA or by a single JDBC resource, completely transparently to callers. It does this by applying a Transaction Interceptor (around advice) that intercepts main flow of execution, typically beginning a transaction before method execution and committing or rolling it back after method execution. The TransactionProxyFactoryBean also accepts fine grained policies about which methods should be made transactional on your POJO object (and which ones should be excluded), which types of Exceptions should/should not trigger a rollback, and the isolation levels and propogation behaivior to use. Now, with that said, yes, it is possible to demarcate transactions at the web-tier, for example on your controllers. Spring can proxy any java interface (using Dynamic Proxies) or class (using cglib). However, I would not recommend this. It's much cleaner to have a well-defined business layer that captures the business requirements of your application, and demarcate transactions there. I think this central to any application: the web layer (and any other presentation layer like Swing) should be kept thin, delegating to the biz layer for the execution of domain-specific use cases. There are many advantages for doing this: 1. It's clear WHAT your application does (through a well-defined, descriptive business contract), 2. biz and persistence logic is fully decoupled from presentation, 3. because of #2, biz/persistence logic can be tested independently of presentation concerns, and 4. middle-tier people and presenation-people are often different people in practice, and the two layers can be developed independently. As I alluded to previously, I see less value in automating the testing of your controllers when the meat of your application is centralized behind a well-defined business. I see more costs than benefits in defining an additional DAO layer when all your application is is a data entry system (CRUDer).

Posted by Keith on October 30, 2004 at 11:47 AM MDT #

Hi, the rails demo reminds me the Jac demo I saw once, one year ago, in Paris. http://jac.objectweb.org/ The framework is based on an aop approch. With it, You write in some config files and - magic! - the application creates crud. The developper can create crud apps in Swing and/or html in less than 5 minuts. This was rather impressive. (But this is not hibernate+Spring.)

Posted by Gabriel K. on October 31, 2004 at 10:29 AM MST #

Just out of curiousity, would this type code generator bind you to a specific UI? That being Tapestry, WebWork, SWT, Swing, etc. Or is it flexible enough that the code generated is independent of the UI.

I'm assuming that it wouldn't necessarily force you to use one specific framework although I'm sure a good bit of the UI code could be written for you in advance, it would just depend on what your UI religion is... I'm an Echo user and would love to shrink the tedium of writing the CRUD code using an open source tool.

Posted by Mark on November 01, 2004 at 08:58 AM MST #

WebObjects has had a similar feature for several years:
  1. Direct-to-Web : Open a new D2W project add an O-R model file run and viola CRUD via HTML
  2. Direct-to-Java-Client : Open a D2JC project, add an O-R model file and voila, CRUD via Applet/Swing
D2W does not generate code, unless you 'freeeze' a page to get the components to customize with hand-crafted code. It's not open-source, but it's only $699

Posted by Mike Henderson on November 04, 2004 at 07:52 PM MST #

I agree, if there were something like Trails that supported something more standard like Struts or JSF, that would rock.

Posted by Bill Schneider on August 25, 2005 at 12:02 PM MDT #

Post a Comment:
  • HTML Syntax: Allowed