Grails 1.1 Web Application Development
The expectations of our users are increasing, and rightly so. The Internet is no longer the
playground of geeks and nerds. Businesses use it to communicate with and support their
customers; families use it to keep in touch while whole communities share their
experiences with like-minded people. The democratization of the Internet has brought a
new wave of software into the lives of people who would otherwise rarely use computers.
The most successful of the new generation of web applications have not been written for
the expert user, but for the casual user, focusing on ease of use. Web application
development frameworks that focus on developer productivity have improved the ability
of developers to respond to the demands of their users. Simpler and more intuitive
frameworks have allowed the rapid development and refinement of new features.
Java web application development has something of a checkered history; simple isn’t it.
There may be more than one way to skin a cat, but there are almost infinite numbers of
ways to build a Java web application. The options that are available are mind-boggling.
Which database server to use? What about the application server? You also better choose
an MVC framework while you’re at it. Should you use a persistence framework, or hand
code SQL statements? If you use an ORM framework, which one is best? Don’t forget to
choose a logging library. How should the project be laid out? Once you’ve finished
making all these decisions, it’s time to start on the configuration to get all of these
disparate frameworks working together. Now, eventually you are ready to start coding!
No wonder the latest and greatest web applications are built in PHP and Ruby on Rails.
Java still has a lot to offer. It is faster than most other languages that are used for web
application development. It is an extremely mature platform, with lots of high quality
libraries available. Moreover, its static, strong typing gives you less rope to hang yourself
with. However, Java developers need to find technologies that deal with the common
activities of web development. Sometimes we need a few choices taken away to help us
focus on the problem at hand, creating great software that provides value to our users at
less cost to the people paying the bills.
Grails does just this. It removes the need for reams of configuration through a
convention-based approach that constrains developers in a healthy way. The decisions
concerning project layout and which frameworks to use are removed. This leaves the
developers free to use their creative talents for producing great software, rather than
tweaking configuration files.
Throughout this book, you will learn how to build a web application using Grails and a
number of key plug-ins. You will see that it is possible to achieve a great deal with very
little code. Who knows, you may even rediscover your passion for web development on
the Java platform!
What This Book Covers
Chapter 1 presents a short state of the nation of Java web development and makes the
case for a framework like Grails. At the end of the chapter, we will install and create a
Grails project.
Chapter 2 covers the use of Grails scaffolding to generate some simple pages to manage
users and roles for our application.
Chapter 3 shows how to post messages, where we write the first basic functionality for
the application by allowing users to post messages that can be shared with other users.
This chapter introduces a number of basic concepts for Grails development including:
controllers, validation, Groovy Server Pages (GSP), and Grails Object-Relational
Mapping (GORM).
Chapter 4 covers an introduction to Groovy. Here we take a short break from the Grails
framework to get a better understanding of the Groovy programming language. We will
cover just enough of the language to be able to proceed through the rest of the book.
Chapter 5 shows how to use our first external plug-in to add authentication and
authorization to the application.
Chapter 6 covers testing, where we introduce the different levels of automated testing
that are available in the Grails framework. We see how to write, unit tests with new
support for testing in Grails 1.1. We also cover integration tests, and install a functional
testing plug-in.
Chapter 7 covers file sharing, where we allow users to share files through the application
by introducing file uploads.
Chapter 8 covers some advanced querying techniques, using Hibernate criteria support in
GORM, to implement file version history.
Chapter 9 introduces Grails services in more depth. We see how to extract logic from our
controllers into services to keep the application maintainable.
Chapter 10 introduces more advanced GORM techniques, such as: persisting inheritance
and performing polymorphic queries to enable tagging. We also delve into GSP a bit
more by using templates to encapsulate view components.
Chapter 11 covers AJAX and RIA Frameworks—Where we improve the user experience
with AJAX to allow users to edit tags in-line and use the RichUI plug-in to create tag
clouds and perform auto suggestion when editing tags.
Chapter 12 shows us how to use the Searchable plug-in to add a search feature to our site
in a matter of minutes. We also provide an RSS feed and a REST based API for
managing messages.
Chapter 13 show us how to build our own plug-in, where we follow the example of the
Grails plug-in community and extract our tagging code into a plug-in that we can use on
future projects.
Chapter 14 shows how to package and deploy the application to a production ready for
use in a production environment. We then discuss some next steps that may be worth
investigating to handle real world situations.
Managing Content through Tagging
Over the last few chapters, we added the ability for our users to upload and share files
with their teammates. As with messages, files are displayed on the home page in the
order they are added to the system. Currently all messages and files are displayed
on the home page. Over time, our home page is going to become rather large and
unwieldy. We need a user’s home page to show only the files and messages that they
are interested in. To do this, users need to be able to tag their content.
We will implement a simple tagging solution, restructure the home page and then
add some new pages to the application for viewing all of the messages and files.
The new Grails concepts that will be introduced in this chapter are:
- Working with inheritance in the domain classes, and looking at which
strategies GORM supports for persistence - Using polymorphic queries over a domain inheritance hierarchy
- Encapsulating view-rendering logic in GSP templates
- Manipulating collections with the Groovy collect and sort methods
Add basic tagging
Tagging is a loose, community-based way of categorizing content. It allows a
group of people to categorize by consensus. Anyone is able to tag a piece of
content. The more a tag is used, the more meaning it takes on and the more widely
used it becomes. This categorization by consensus has been dubbed as folksonomy
(http://en.wikipedia.org/wiki/Folksonomy) in recent times.
So let’s get started by building our tagging support.
Tagging domain model
When implementing tagging in our system, we need to consider the following:
- We must be able to have many tags in our system
- We must be able to associate a single tag with many different files
and messages - We need to make sure that new domain objects can be easily tagged without
having to change the tagging logic - We want to know when a domain object was tagged
To satisfy these requirements, we need to create the following new domain classes:
- Tag—to store the name of the tag. There is one instance of this class per
unique tag name in the application. - Tagger—to store the relationship from domain objects to a tag. This allows
us to store the date a tag was added to a domain object.
Let’s create these domain classes and then write a test to prove that we can tag a
message using this tagging structure.
The Tag class
We are going to separate the tagging classes out from our application domain classes.
Create a folder under grails-app/domain called tagging. This is where we will put
the domain model to implement tagging.
Our Tag class is extremely simple and holds only a name property:
package tagging
class Tag {
String name
static constrains = {
name( blank: false )
}
}
The Tagger class
The next class that we are going to create is the Tagger class. In relational terms,
this object represents a link table between a Tag and any other domain class. It is
important that the relationship between tagged domain classes and the Tagger
relationship class is unidirectional. By this, we mean the domain classes are allowed
to know that they can be tagged, but tags do not know which domain classes can be
tagged, otherwise every tagged domain class would need a special relationship class.
Create the Tagger class as a domain class in the tagging package as follows:
package tagging
class Tagger {
Tag tag
static constraints = {
tag( nullable: false )
}
}
The basics of our tagging model are complete! We now need some logic to allow
tags to be created. Create a new service class called TagService under grails-app/
services/tagging, as shown below:
package tagging
class TagService {
boolean transactional = true
def createTagRelationships(String spaceDelimitedTags) {
return spaceDelimitedTags?.split(' ')?.collect { tagName ->
createTagRelationship( tagName )
}
}
def createTagRelationship(String tagName) {
def tag = Tag.findByName(tagName)?:
new Tag(name: tagName).save()
return new Tagger( tag: tag )
}
This service provides two utility methods to create new relationships by tag name
or by a space delimited string of tag names. The important behavior of these two
methods is that they do not allow duplicate tags to be created in the application. If a
tag name already exists, the tag will be retrieved from the database and used as the
tag in the relationship.
Notice that the createTagRelationships method is using the collect method
to simplify what would normally take a few more lines of code to achieve. We did
briefl y cover the collect method back in our introduction to Groovy in Chapter 4,
but to reiterate: the collect method is dynamically added to any object that can be
iterated over. For example, collections, arrays, strings and so on. It takes a closure
as its argument and executes this closure for each item in the collection. The return
value from each execution of the closure is added to a new collection that the collect
method builds up and then returns once it has finished iterating the original collection.
In createTagRelationship, we are using another neat language feature of Groovy
called the “Elvis operator”. It is named so, as it looks like Elvis’ hair style. This is a
shorter version of the normal Java ternary operator. If the operand being checked
is true then the checked operand will be returned as the default, otherwise the
alternative operand will be used. So in our example:
def tag = Tag.findByName(tagName) ?: new Tag(name: tagName).save()
If a tag can be found from the database then it is used, otherwise a new tag is created.
Tagging a message
The next step is to allow a message to be tagged. Write some integration tests to
make sure the relationships are working before using tagging in the application.
In the folder test/integration/app, create the file TaggableIntegrationTests.
groovy and add the following code:
package app
import tagging.Tag
class TaggableIntegrationTest extends GroovyTestCase {
User flancelot
protected void setUp() {
flancelot = User.findByUsername('flancelot')
Tag.list().each { it.delete() }
Message.list().each { it.delete() }
}
}
The code above sets up the test data needed to create messages and associate tags to
messages. Remember that the flancelot user already exists because it was created by
the BootStrap class.
The first test will determine that we can add tags to a message and then retrieve
messages by tag. Add the following test method to your test class:
void testCanRetrieveMessagesByTags() {
Message message = new Message(user: flancelot, title: 'tagged',
detail: "I've been tagged.").save(flush: true)
Message secondMessage = new Message(user: flancelot,
title: 'other tagged',
detail: "I've been tagged.").save(flush: true)
message.addTag('urgent')
message.addTag('late')
secondMessage.addTag('urgent')
def taggedMessages = Message.withTag( 'urgent' )
assertEquals(2, taggedMessages.size())
assertEquals(2, Tag.list().size())
def secondMessages = Message.withTag( 'late' )
assertEquals(1, secondMessages.size())
assertEquals(2, Tag.list().size())
}
The test above does the following:
- Creates two new messages
- Adds the urgent tag to both messages
- Adds the late tag to one message
- Checks if we can retrieve both messages by using the urgent tag
- Checks if only one message is returned for the late tag
Notice that the highlighted lines of code have not been implemented yet. To allow
this test to pass, we need to add the following methods to the Message domain class:
- addTag—instance method to allow a message to be tagged
- withTag— class method to retrieve all messages with a particular tag
Add the following method to the Message class (don’t forget to import
tagging.Tagger):
def addTag(String tagName) {
tags = (tags)?:[]
tags << tagService.createTagRelationship( tagName )
}
This method simply delegates the creation of the tag relationship off to the
TagService class, and then stores the relationship in the tags list.
Add the following method to the Message class that retrieves all messages with a
given tag name:
def static withTag(String tagName) {
return Message.withCriteria {
tags {
tag {
eq('name', tagName )
}
}
}
}
This method must be static on the Message class, as it is used to load message
instances for a given tag. We do not want to have to instantiate a message before we
can perform the search.
Before running the test, you will notice both of these new methods assume that there
is a property on the Message class called tags. This has not yet been created. We
need to create a one-to-many relationship from Message to Tagger that will allow
messages to be tagged. We also need to inject the TagService into new instances of
the Message class so the work for creating a new tag relationship can be delegated.
Add the relationship to the Message class and inject TagService as shown below:
class Message {
def tagService
static hasMany = [tags:Tagger]
…
}
Now we can run our tests by entering the following on the command line:
grails test-app
We should see some output in the command line similar to:
Running test app.TaggableTest...
testCanRetrieveMessagesByTags...SUCCESS
Tagging a file
Now that we have implemented tagging for messages, we need to make tagging
available for files.
Currently the logic for creating and fetching tags is in the Message domain class. We
need to extract this logic so the File domain class can reuse it. It's time to look at
how GORM supports inheritance.