Sunday, August 12, 2007

Tiles Killed Convention - Facelets Templating

I recently went to a developers conference and listened to a rapid application development with JSF session by David Geary. I found the session very helpful and informative but something keeps bothering me every time somebody talks about templating as he did with Facelets.

Every time I hear about templating I think it should be easier and more reuseable. I started out developing Struts applications which use Tiles. I then moved on to JSF and Tiles. The nice thing about Tiles is "it works". Sometimes just working is not enough and a little convention goes a long ways.

Recently I've been thinking about the convention over configuration mentality that RoR uses. Also I've come upon a great templating package called Sitemesh. What I love about Sitemesh is you can define a decorator and decorate multiple pages by a matching URL pattern. This is a GREAT idea. Using Tiles I've always had to create a definition for every page in a Tiles definition file, a JSP that inserts the definition, and the page that actually has the content. Well that's three files for each page of content the user sees. Now the problem I have with this is the Tiles definition for the page and the tiles insert JSP is basically repeated for every page. Sure you can define a base Tiles definition and extend it but it tends to be mostly repetitive. You can also define your tiles definition in the JSP page which is just as messy and repetitive.

Let me get back to Facelets. Facelets is a wonderful idea to use the html view and bind to components. The problem is when you get to the templating part it goes back to the Tiles mentality again. You have a page which defines a composition and a page which contains the content. The page with the composition is repeated for every content page and in most cases the exact same composition definition. This is where the convention of Sitemesh is nice and could come in handy. It would be nice if you could define a Facelets composition for a URL path and apply it to all pages in a directory. That's a heck of a start for reuse and convention over configuration. Then you can just configure the exceptions to the case which should be rare.
Sidenote: You can use JSF components in a Sitemesh decorator (http://jira.opensymphony.com/browse/SIM-201).
There are a couple of positive benefits to applying templates based on a URL path that are a side effect. I've been on too many projects where everything is thrown into the base directory of a web application or relatively few directories. This gets out of hand quickly and not good structure. There are also security packages like Acegi that will apply security based on the URL path. So if you have your files in an appropriate file structure it should be easy to find pages, apply templates, and apply security.

(If you don't think it can be done check this out: http://jira.opensymphony.com/browse/SIM-223)

This brings up another annoyance of JSF. JSF has no notion of subdirectories built in. With Struts you could define config files for each URL path and then you didn't need to put the subdirectory paths for the JSP pages in a configuration file. JSF should add this in and promote appropriate directory structure.

As a closing note if you're doing something multiple times and not much changes maybe you should refactor it out. If you are doing this in programming maybe you would extract the commonalities out into a method passing in the parameters and then the method can be reused. In the case of a templating package you should refactor the configuration out of the JSP page or Faclets xhtml view and move it into the URL path with a configuring Servlet or Filter.

Thursday, August 9, 2007

Optimistic Locking in Rails with Active Record for Free...almost

Like other persistence frameworks, ActiveRecord helps us with concurrency issues. This is actually fairly simple, but in general the documentation I have found on this subject has been poor. I will try to sum this up very quickly.

1) Add lock_version

For each table where concurrency is a concern a lock_version column must be added; Rails is then supposed to perform some magic behind the scenes...

In your migration this should look as follows.
table_name.column :lock_version, :integer, :default=>0

Alternatively, if you already have a versioning column in your database you can inform rails of your column by adding the line "set_locking_column 'your_versioning_column_name'" to the associated model class.

2) Catch StaleObjectError

Now that you have added the lock_version column you get concurrency for free. Whenever an attempt occurs to save over the same record version an ActiveRecord::StaleObjectError will be raised.

So now you can update your controllers to catch StaleObjectErrors and handle these however you wish...

This would look something like this.

def some_updating_method(an_object)
an_object.update_attributes(params[:an_object])
rescue ActiveRecord::StaleObjectError => e
flash[:notice] = "This record changed while you were editing it. Please try again."
#maybe perform a redirect here...
end

3) But Wait! Update methods aren't actually passed objects that's too much overhead!

How observant you are. This is where most of the example tend to fall short. In fact, try implementing your update method exactly like Recipe 3.18 from the Rails Cookbook and see what happens...

Generally an update method actually looks more like this.

def update
my_object = MyObject.find(params[id])
my_object.update_attributes(params[:my_object])
end

While this will stop to users from updating at the exact same time this isn't exactly what we want in many cases. More often than not we want to prevent two users from editing over each other without knowing.

This is easily fixed with the following...

def edit
my_object = MyObject.find(params[id])
session[lock_version] = my_object.lock_version
# whatever we want edit to do
# ...
# ...
# call update...
end

Now we update update (har har) to detect the concurrency issue...

def update
if my_object.lock_version != session[:lock_version]
raise ActiveRecord::StaleObjectError
end
my_object = MyObject.find(params[id])
my_object.update_attributes(params[:my_object])
rescue ActiveRecord::StaleObjectError => e
flash[:notice] = "This record changed while you were editing it. Please try again."
#maybe perform a redirect here...
end

Hopefully this helps someone out. If you have a better/cleaner way of doing this, let me know.

Thursday, August 2, 2007

Drop all tables in MySQL

From time to time I would like to be able to drop all of the tables in my MySQL database without actually dropping the database. This can be painful if there are many tables in the database. Recently I came across this little nugget that simplifies the process.

mysqldump -u[username] -p[password] --add-drop-table --no-data [database] | grep ^DROP | mysql -u[username] -p[password] [database]

Voila! All your tables are gone!