Grails WebAlbum

After completing my programming course, which introduced the students to Ruby on Rails, and developing a simple photo album application called WebAlbum, I decided that it was time to explore one of the more interesting of the new web application frameworks in the Java arena – Grails.
This article outlines my first attempt using Grails, though I already had experience with Groovy, the JVM compatible dynamic language, the Spring framework, Hibernate, and of course, Java. I used Grails version 1.0.2, see the Grails installation article for further details on downloading and installing Grails.

Getting Started

The original application was explained in the programming course during two lessons, each two hours long. I won't go over the details again here, but you can take a look at the articles I wrote in Ruby on Rails, Part 1, and Ruby on Rails, Part 2. You can also check out the entire course, there is also a PDF available.
The application itself is relatively straightforward, there are users, who have albums, which contain pictures, that are actually a small set of four images. Only the respective users can change albums and pictures, but all the information is available to the casual visitor. Lightbox is used to produce a slide show effect for the images.
Documentation
I spent most of my time with “The Definitive Guide to Grails” balanced on my knee, though it's a little outdated now – see the addendum for updates. There's also the InfoQ book(let) “Getting Started with Grails”, and the excellent user guide and reference documentation. The InfoQ booklet is short and free, the Definitive Guide is longer and broader in scope, but a little shallow for my tastes, and the reference documentation is just wonderful. It has almost everything and is pretty concise.
Generating the Application
If only that were true! Actually Grails provides generators which produce scaffolding code for the generic application – we're still going to have to write some code. First, we'll build the application structure. Select a folder where you want the application to be generated (I chose Grails) then open a command console in that folder and type:
grails create-app WebAlbum
which produces the following output:
Welcome to Grails 1.0.2 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: C:\java\grails-1.0.2

Base Directory: C:\Grails
Environment set to development
Note: No plugin scripts found
Running script C:\java\grails-1.0.2\scripts\CreateApp.groovy
    [mkdir] Created dir: C:\Grails\WebAlbum\src
    [mkdir] Created dir: C:\Grails\WebAlbum\src\java
    [mkdir] Created dir: C:\Grails\WebAlbum\src\groovy
    [mkdir] Created dir: C:\Grails\WebAlbum\grails-app
    [mkdir] Created dir: C:\Grails\WebAlbum\grails-app\controllers
    [mkdir] Created dir: C:\Grails\WebAlbum\grails-app\services
    [mkdir] Created dir: C:\Grails\WebAlbum\grails-app\domain
    [mkdir] Created dir: C:\Grails\WebAlbum\grails-app\taglib
    [mkdir] Created dir: C:\Grails\WebAlbum\grails-app\utils
    [mkdir] Created dir: C:\Grails\WebAlbum\grails-app\views
    [mkdir] Created dir: C:\Grails\WebAlbum\grails-app\views\layouts
    [mkdir] Created dir: C:\Grails\WebAlbum\grails-app\i18n
    [mkdir] Created dir: C:\Grails\WebAlbum\grails-app\conf
    [mkdir] Created dir: C:\Grails\WebAlbum\test
    [mkdir] Created dir: C:\Grails\WebAlbum\test\unit
    [mkdir] Created dir: C:\Grails\WebAlbum\test\integration
    [mkdir] Created dir: C:\Grails\WebAlbum\scripts
    [mkdir] Created dir: C:\Grails\WebAlbum\web-app
    [mkdir] Created dir: C:\Grails\WebAlbum\web-app\js
    [mkdir] Created dir: C:\Grails\WebAlbum\web-app\css
    [mkdir] Created dir: C:\Grails\WebAlbum\web-app\images
    [mkdir] Created dir: C:\Grails\WebAlbum\web-app\META-INF
    [mkdir] Created dir: C:\Grails\WebAlbum\lib
    [mkdir] Created dir: C:\Grails\WebAlbum\grails-app\conf\spring
    [mkdir] Created dir: C:\Grails\WebAlbum\grails-app\conf\hibernate
[propertyfile] Creating new property file: C:\Grails\WebAlbum\application.properties
     [copy] Copying 2 files to C:\Grails\WebAlbum
     [copy] Copying 2 files to C:\Grails\WebAlbum\web-app\WEB-INF
     [copy] Copying 5 files to C:\Grails\WebAlbum\web-app\WEB-INF\tld
     [copy] Copying 87 files to C:\Grails\WebAlbum\web-app
     [copy] Copying 17 files to C:\Grails\WebAlbum\grails-app
     [copy] Copying 1 file to C:\Grails\WebAlbum
     [copy] Copying 1 file to C:\Grails\WebAlbum
     [copy] Copying 1 file to C:\Grails\WebAlbum
[propertyfile] Updating property file: C:\Grails\WebAlbum\application.properties
Created Grails Application at C:\Grails/WebAlbum
C:\Grails>
That's it. Now we have the application structure. The most important folders (where we'll be doing almost all the work) are grails-app/domain, grails-app/views, and grails-app/controllers, which make up the triumvirate Model / View / Controller pattern
The next step is to create the domain models; User, Album, Picture, and Image. Type:
grails create-domain-class User
which produces the following output:
C:\Grails\WebAlbum>grails create-domain-class User

Welcome to Grails 1.0.2 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: C:\java\grails-1.0.2

Base Directory: C:\Grails\WebAlbum
Environment set to development
Note: No plugin scripts found
Running script C:\java\grails-1.0.2\scripts\CreateDomainClass.groovy
     [copy] Copying 1 file to C:\Grails\WebAlbum\grails-app\domain
Created  for User
     [copy] Copying 1 file to C:\Grails\WebAlbum\test\integration
Created Tests for User
C:\Grails\WebAlbum>
This produces an empty User domain class, which we'll have to flesh out in a moment. Grails also produces an integration test skeleton class, but that is another story. Now rinse and repeat for Album, Picture, and Image.
Before we move on, we'll need to add some content to these files, since they'll be read by the controller view generators. The more work we do now, the more the generators will do for us.
So, for grails-app/domain/User.groovy:
class User {
    String name
    String motto
    String salt
    String password
    String passwordConfirmation
    SortedSet albums
    SortedSet pictures
    Integer albumsCount = 0
    Integer picturesCount = 0
    Date dateCreated = new Date()
    Date lastUpdated = new Date()

    static hasMany = [ albums: Album, pictures: Picture ]

    static transients = [ 'passwordConfirmation' ]

    static constraints = {
        name(size: 1..40, blank: false, unique: true)
        motto(size: 0..80, nullable: true)
        salt(maxSize: 40, blank: false, nullable: false)
    }
}
Now, that might look a little frightening (and it's not the whole story – check the source code), but it pretty well covers the Groovy bean, and the GORM mapping. The first block defines the User properties, then we get the associations – a User hasMany Albums and Pictures, followed by the transients (which aren't persisted to the database), and finally the constraintsname must be in this size range, can't be blank, and must be unique, etc. Reads nicely doesn't it?
Please note that you can do all of this in stages, start with the properties, then add associations and constraints later. I'm just trying to be a little more concise.
I'll show you the Album class next, but then I'll skip Picture and Image – they're pretty much similar. In grails-app/domain/Album.groovy:
class Album implements Comparable {
    User user
    String caption
    String description
    SortedSet pictures
    Integer picturesCount = 0
    Date dateCreated = new Date()
    Date lastUpdated = new Date()

    static belongsTo = User
    static hasMany = [ pictures : Picture ]

    static optionals = [ 'description' ]

    static constraints = {
        caption(size: 1..40, blank: false)
        description()
    }

    int compareTo(obj) {
        obj.id.compareTo(id)
    }
}
Similar, but not identical. The Album belongsTo a User (that's the other side of the association), and hasMany Pictures. Entering a description is optional, the constraints are so and so, you get the idea. This class implements Comparable, so that it can be stored as a SortedSet in the User domain class (most recent to least recent, using the id value).
Next we'll need the controllers and views for User, Album and Picture. Since you're getting the hang of things, you'll expect that we'll use a generator:
grails generate-all User
which produces:
C:\Grails\WebAlbum>grails generate-all User

Welcome to Grails 1.0.2 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: C:\java\grails-1.0.2

Base Directory: C:\Grails\WebAlbum
Environment set to development
Note: No plugin scripts found
Running script C:\java\grails-1.0.2\scripts\GenerateAll.groovy
  [groovyc] Compiling 6 source files to C:\Documents and Settings\...\.grails\1.0.2\projects\WebAlbum\classes
[native2ascii] Converting 10 files from C:\Grails\WebAlbum\grails-app\i18n to ...
     [copy] Copying 1 file to C:\Documents and Settings\...\.grails\1.0.2\projects\WebAlbum\classes
     [copy] Copying 1 file to C:\Documents and Settings\...\.grails\1.0.2\projects\WebAlbum\resources
...
Generating views for domain class User ...
Generating controller for domain class User ...
Finished generation for domain class User
C:\Grails\WebAlbum>
Rinse and repeat for Album and Picture. We don't want a controller or views for Image.
Next we need to do a little configuration (no not in XML) in grails-app/conf/DataSource.groovy, change the following (highlighted) lines:
...
environments {
  development {
    dataSource {
      dbCreate = "update" // one of 'create', 'create-drop','update'
      url = "jdbc:hsqldb:file:db/devDB"
    }
  }
...
That way we'll have a file based HSQLDB database, which will be stored in db/devDB.*. Because I can't stand writing WebAlbum all the time in the URL, we'll change the web application root for Jetty (as explained in this article) by creating a file web-app/WEB-INF/web-jetty.xml with the following contents:
<?xml version="1.0"  encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"
  "http://jetty.mortbay.org/configure.dtd">
<Configure class="org.mortbay.jetty.webapp.WebAppContext">
  <Set name="contextPath">/</Set>
</Configure>
Which is XML, of course. However this is for Jetty, not Grails.
Ready for the First Run
Now we're ready for our first run. Yes, two hours from the start, most of which was spent fiddling with the domain class associations. Type:
grails run-app
The console will prattle on for a while, finishing with:
Server running. Browse to http://localhost:8080/WebAlbum
Now open up your favourite web browser and type http://localhost:8080, and you'll see the Grails home page:
GrailsWebAlbumImages/welcome.png
There are our three controllers, so click on UserController, and we'll see what will become the home page:
GrailsWebAlbumImages/users.png
Hmm, seven HTML errors. Not happy about that. Nice layout though, better than most programmers put together (myself included). Let's try the New User page next:
GrailsWebAlbumImages/create-user.png
Hmm, twelve HTML errors. Again, the layout is good. What about the error messages? Click Create:
GrailsWebAlbumImages/create-user-errors.png
Feedback looks alright, but those messages read like a compiler output. Some work still to be done, but there's plenty that's been done for us.
First Impressions
If you know Ruby on Rails, well there's nothing new here. Seen it, done it. If you're a Java developer on the other hand, there's a very large Wow! factor, or at least there was for me. Changed two lines of configuration code, added a six line XML file (which could have been skipped). Maybe a hundred lines of code for the domain class files. That's all? Yes, that's all. Really, I kid you not. The controllers and views were free of charge - and effort.

Pause for Thought

Of course, the application is not quite ready for prime time. We will have to write a little more code ourselves. Two things, in fact. I want the user password to be encrypted, and I need to scale down the images, just as I did for the Ruby on Rails version. Also I need to get rid of those HTML errors, and create more human error messages. Then there's uploading the image file. That's five things, damn.
The password encryption is actually quite easy to do, thanks to Grails encoding. Same for file uploading, thanks to the Spring framework. The error messages can be tailored by defining the right messages in the i18n/messages*.properties file. The HTML can be fixed by modifying the templates (see below). Which leaves the image scaling.
The images can be scaled using Java – but that's another story again. It took me five hours, five, to do that little task. And I never got the thing to save in GIF format properly. I had to use PNG in the end, and to hell with transparent backgrounds in IE6. Thankfully Chet Haase has written a lot about ImageIO and BufferedImage, and the other fifteen different image packages in Java. Phew!
Modifying the Templates
Firefox (actually the HTMLTidy plugin) showed us that the generated pages have errors. That means the browser will probably switch to quirks mode, which gives, uhm, quirky results. Don't want that. So let's fix the generated pages – via the templates.
But, where are the templates? They're in three places in the %GRAILS_HOME%directory; %GRAILS_HOME%/src/war/css/main.css, %GRAILS_HOME%/src/grails/grails-app/views/layouts/main.gsp and %GRAILS_HOME%/src/grails/templates/scaffolding/*.gsp. There are others, all within the %GRAILS_HOME%/src folder, but I'm only interested in the ones I've listed.
I've included the modified templates in the WebAlbum/templates folder, and I won't go into the details of all the modifications here. The point is that it is easy to do, and saves a lot of time – especially if you have many controllers and views.
I'll make just one observation though. %GRAILS_HOME%/src/war/css/main.css and %GRAILS_HOME%/src/grails/grails-app/views/layouts/main.gsp are both generated by grails create-app. To get rid of almost all the HTML errors I added this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
to main.gsp. There was no point in regenerating the application for such a simple change, so I did the same in the grails-app/views/layouts/main.gsp file. In a similar vein, the CSS file can simply be copied back and forth.
Play it again, Sam!
The remaining templates are used to generate code by the grails generate-views command. But, wait a minute, we've already created the controllers and views.
The grails generate-views command is well behaved, it will prompt you before attempting to overwrite a file. So all you have to do is delete the view files you want regenerated, run grails generate-views again, and answer n every time you're asked if you want to overwrite the existing file.
I wish there was a flag to set, you know –-overwrite=false, but I couldn't find one.

Security, and other Tidbits

We need to make sure that certain operations can only be performed under certain conditions. For example, a User can only be edited if the logged in user has the same id, or a picture can only be created if the user is logged in and has at least one album.
There are two ways of handling this; either using a beforeInterceptor or by creating a xxxFilters class in the grails-app/conf folder. I tried both, but found the interceptor mechanism much more friendly.
So let's take a quick look at UserController:
...
  def beforeInterceptor = [ action: this.&intercept, except: [ 'logout', 'list', 'show', 'create' ] ]
...
  boolean intercept() {
    def methods = actionMethods()
    if (methods && (flash.warning = validMethod(methods))) {
      redirect(action: 'list')
      return false
    }
    if (actionName == 'save' || actionName == 'login') {
      return true
    }
    if ((flash.warning = sameUser())) {
        redirect(action: 'list')
        return false
    }
    true
  }
...
We're using a little Groovy magic (this.&intercept) to convert our method into a closure, but the method itself has full access to params, redirect, and all the other controller stuff. Something I couldn't do with the xxxFilters alternative.
The code basically checks the request.method type ('GET', 'POST', 'PUT','DELETE', etc), and if that's alright, we're done if the action is save or login. Otherwise we then check that the logged in User is the same as the requested User.
Tag Libraries
Tag libraries. Well, that certainly made me shudder. TLDs, gruesome code, yuk! Not going to do that, much as very few others do either. But then I read Graeme Rocher's article about them, and I realised, well, this is Grails, which is bending over backwards to make my life easier, so tag libraries it is.
This turned out to be one of those Wow! moments. Although even Grails can't bind the controller data, or GSP data to the tag library, it is just so simple to use. Here's an extract from grails-app/taglib/WebAlbumTagLib.groovy:
class WebAlbumTagLib {

  static namespace = "wa"

  // if development environment
  def ifDevEnv = { attrs, body ->
    def map = grailsApplication.metadata
    String env = map[grailsApplication.ENVIRONMENT]
    if (env == grailsApplication.ENV_DEVELOPMENT) {
      out << body()
    }
  }

  // if user logged in...
  def ifUser = { attrs, body ->
    boolean valid = session.userId != null
    if (attrs.not) {
      valid = !valid
    }
    if (valid) {
      out << body()
    }
  }
...
In the GSP pages, this looks like:
...
  <div class='nav'>
    <span class="menuButton">
      <g:link controller="user" class="create" action="create">
        New User
      </g:link>
    </span>
    <wa:ifUser>
      <span class="menuButton">
        <g:link controller="album" class="create" action="create">
          New Album
        </g:link>
      </span>
      <wa:ifUserHasAlbums>
        <span class="menuButton">
          <g:link controller="picture" class="create" action="create">
            New Picture
          </g:link>
        </span>
      </wa:ifUserHasAlbums>
    </wa:ifUser>
  </div>
...
Which is actually cleaner than the usual <% code %> blocks that you find in ASP or Rails, as far as I'm concerned. The important point is that it takes very little extra effort to do this. No Tag Library Descriptor configuration in sight.
Transferring objects from the GSP to the tag library is also surprisingly simple, and a little magical. For example, the following tag library 'method':
  def pictureLightboxAnchor = { attrs ->
    def picture = attrs.remove('picture')
    def size = attrs.remove('size')
    def lightboxSize = attrs.remove('lightboxSize')
    def caption = picture.caption
    if (!caption) {
      caption = '...'
    }
    ...
is picking up the picture object (and referencing its properties) as an attrs property, which we gave it via this GSP code:
<wa:pictureLightboxAnchor picture="${picture}" size="${Image.Small}" lightboxSize="${Image.Large}" />
Clever stuff, indeed.
Partial to Partials
Well, that's really Rails talk, but they're there nonetheless. Using the render tag, you can 'call' templates from your GSP pages. You can also pass a model to them, which is better than not having any binding at all.
Internationalisation
Internationalisation is covered with the message tag and method. Using the grails-app/i18n/messages*.properties files you can personalise error messages, and add your own. Here's a short extract of the messages I added:
# WebAlbum Application messages
user.name.blank=You must specify at least one character for the name
user.name.size.toosmall=You must specify at least one character for the name
user.name.size.toobig=The name cannot exceed 40 characters
user.name.unique=The name you have chosen has already been taken
user.motto.size.toobig=The motto cannot exceed 80 characters
user.password.length=The password must be between 6 and 40 characters
user.password.match=The password does not match the confirmation
...
intercept.not.owner.user=You must be the owner to change the user information
intercept.not.owner.album=You must be the owner to change the album information
intercept.not.owner.picture=You must be the owner to change the picture information
intercept.no.albums=You must have an album to create a picture
My only complaint is that I had some problems with apostrophes in the text. I didn't have time to investigate properly, so I just got rid of 'em, er, them.
Configuration
In grails-app/conf/UrlMappings.groovy I made a couple of modifications, to map image/ requests to the PictureController, and to set the root URL / to the UserController list action:
class UrlMappings {
  static mappings = {

    "/$controller/$action?/$id?" {
      constraints {
        // apply constraints here
      }
    }

    "/image/$id/$size?/$filename?" {
      constraints {
          size(matches: /\d+/)
      }
      controller = 'picture'
      action = 'view'
    }

    "500" (view: '/error')

    "/" (controller: 'user', action: 'list')

  }
}
Again, Grails tries hard to help, allowing us to constrain the size parameter to numeric values.

The Final Cut

I've written rather more than I had originally intended. Part of that is due to my enthusiasm for Grails. I don't usually get enthusiastic about frameworks these days, but Grails (together with Groovy, of course), just seems to emit small packets of happiness all over the place.
The final statistics were: 3 controllers with 571 lines of code, 4 domain classes with 165 lines of code, and a tag library with 150 lines of code, 0 lines of SQL, and about 12 lines of configuration. Oh, and four days development time, almost one of which spent messing around with the Java imaging libraries. That also included the time spent learning the framework. The results look like this:
GrailsWebAlbumImages/final-create-user-errors.png
As you can see, we've removed the HTML errors, and the error messages read a little better.
GrailsWebAlbumImages/final-edit-delete.png
The edit-delete view. I moved the delete button and added a checkbox (paranoid tactics, perhaps). The typographical error is at no further charge. Sigh.
GrailsWebAlbumImages/final-show-album.png
The show page. The albums pictures are listed below the album information. Finally, Lightbox in all it's glory:
GrailsWebAlbumImages/final-lightbox.jpg
The WebAlbum application, and the modified templates used can be found in the GrailsWebAlbum.zip archived file. Be warned, though – this is a 5 MB file. The password for both users (jhl and lug) is villafranca. The source code is licensed identically to Grails, using the Apache version 2.0 license.
Addendum
Nothing is perfect. I received an email from Alex, which showed that WebAlbum didn't work with MySQL, complete with stack trace. The culprit was the byte[] data field in Image.groovy. Because the size wasn't set, MySQL was using a 256 byte field, whereas HSQLDB is much less fussy. Alex made a three line change to Image.groovy:
  static constraints = {
    data(size: 0..10000000)
  }
and he was back in business. Obviously GORM sets the correct type (BLOB in this case) based on the maximum size, and the actual RDBMS – with help from Hibernate, I think. In any case, it is always a good idea to limit the upload size, so I have made the necessary changes. PictureController.groovy will now give an error message if the file size is above 10 MB in size (10,485,760 bytes). Thanks, Alex.

Contacts

Syger can be contacted for consultancy work on any of the topics mentioned in this article, by sending an email to info@syger.it.

Valid CSS

Valid XHTML 1.0

Valid Atom 1.0