CHAPTER 4

Introduction to Grails

Let's face it: developing web applications is hard. This problem has been exacerbated in today's environment, where applications deemed to fall into the Web 2.0 category involve lots of technologies, such as HyperText Markup Language (HTML), Cascading Style Sheets (CSS), Asynchronous JavaScript and XML (Ajax), XML, web services, Java, and databases. Then on top of the technologies sit lots of open source framework choices like model-view-controller (MVC) frameworks and Ajax frameworks. To make matters worse, while the complexity of building applications continues to grow, expected turnaround times continue to shrink.

In recent years, the Java community has tried solving these issues by building applications using Java Platform, Enterprise Edition (Java EE) and its predecessor, Java 2 Platform, Enterprise Edition (J2EE). While these platforms have proven to be scalable and robust, they don't allow for rapid agile development. Java EE has proven over and over again that it was not written with an application level of abstraction but rather with a much lower technical level. This is the reason that all the recent application frameworks have been created and most notably the popularity in such frameworks as Struts, Spring, and Hibernate. Furthermore, the development cycle of coding, compiling, packaging, deploying, testing, and debugging takes entirely too long for any real productivity and requires developers to switch context too frequently.

Enter Grails. Grails is an open source web development framework that packages best practices such as convention over configuration and unit testing with the best-of-the-best open source application frameworks such as Spring, Hibernate, and SiteMesh. Together with the productivity of the Groovy scripting language, everything runs on top of the robust Java and Java EE platforms.

In this chapter, you will learn about the features and open source frameworks included with Grails. Then you'll learn how to take advantage of Grails' powerful scaffolding feature to build your first Grails application.

What Is Grails?

Grails is not only an open source web framework for the Java platform, but a complete development platform as well. Like most web frameworks, Grails is an MVC framework, but it's not your average Java MVC framework. Like other Java MVC frameworks, it does have models referred to in Grails as domain classes that carry application data for display by the view. However, unlike other MVC models, Grails domain classes are automatically persistable and can even generate the underlying database schema. Like other MVC frame-works, Grails controllers handle the requests and orchestrate services or other behavior. Unlike most MVC frameworks, though, services and other classes can be automatically injected using dependency injection based on naming conventions. In addition, Grails controllers are request-scoped, which means a new instance is created for each request. Finally, the default view for Grails is Groovy Server Pages (GSP) and typically renders HTML. The view layer also includes a flexible layout, a templating feature, and simple tag libraries.

Other Grails advantages include minimal configuration and a more agile development cycle. Grails eliminates most of the standard MVC configuration and deployment descriptors by using initiative conventions. Also, because Grails takes advantage of Groovy's dynamic language features, it is usually able to shorten the development cycle to just coding, refreshing, testing, and debugging. This saves valuable development time and makes development much more agile than with other Java MVC frameworks or Java EE.

Grails is also a complete development platform, including a web container, database, build system, and test harness out of the box. This combination can reduce project startup time and developer setup time to minutes rather than hours or days. With Grails, you typically don't have to go find and download a bunch of server software or frameworks to get started. You also don't have to spend time creating or maintaining complicated build scripts. Everything you need to get started comes bundled in one simple-to-install package.


Note Grails is licensed under the flexible Apache 2.0 license and is hosted by the Codehaus open source community.1


Grails has an impressive list of features and is able to provide so much by integration of proven open source projects.

Grails Features

Grails really has too many features to mention them all. In this section, we'll highlight some of the more important ones.

__________

Convention Over Configuration

Rather than using lots of XML configuration files, Grails relies on conventions to make application development easier and more productive. This also helps encourage the Don't Repeat Yourself (DRY) principle. Many of the conventions relate to its directory structure, which we'll discuss later in this chapter in the "Creating the Application" section. However, Grails also includes a command-line interface that you use to generate Grails artifacts and enforce the conventions.

Unit Testing

Unit testing is now recognized as a critical best practice to improving the quality of software deliverables and enabling long-term maintainability of an application. Furthermore, unit testing is even more important for applications written using dynamically typed languages such as Groovy, because identifying the effects of changes without the help of the compiler and unit tests can be difficult. This is why unit testing is a major Grails convention. As you will learn later in this chapter in the "Implementing Integration Tests" section, a unit test is created automatically when you use Grails to generate domain or controller classes.

Grails separates its unit tests into two categories: unit and integration. Grails unit tests are freestanding unit tests with no dependencies other than possibly mock objects. Integration tests, on the other hand, have access to the entire Grails environment, including the database.

Grails also includes functional testing for automating the web interface. In Chapter 5, you'll learn how to write functional tests.

Scaffolding

As you'll experience in the second half of this chapter, Grails has a scaffolding framework that generates applications with create, read, update, and delete (CRUD) functionality with very little code, allowing you to focus on defining the Groovy domain by creating classes with properties, behaviors, and constraints. At either runtime or development time, Grails can generate the controller behavior and GSP views associated with the domain classes for CRUD functionality. At the same time, it can even generate a database schema, including tables for each of the domain classes.

Object Relational Mapping

Grails includes a powerful object relational mapping framework called Grails Object Relational Mapping (GORM). Like most object-relational mapping (ORM) frameworks, GORM can map objects to relational databases and represent relationships between those objects, such as one-to-one or one-to-many. But what sets GORM apart from other ORMs is the fact it is built for a dynamic language like Groovy. It injects the CRUD methods right into the class without having to implement them or inherit from a persistent super classes. Once more, it is able to provide an ORM DSL for dynamic finder methods and search criteria. You will learn more about GORM in Chapter 6.

Plug-Ins

Grails does not propose to have all the answers to every web development problem. Instead, it provides a plug-in architecture and a community where you can find plug-ins for things like security, Ajax, testing, searching, reporting, and web services. This plug-in architecture makes it easy to add complicated functionality to your application. For example, Chapter 7 will show how to use a CAPTCHA plug-in to ensure real people are registering with your application.

Integrated Open Source

Grails does not suffer from the Not Invented Here (NIH) syndrome. Rather than reinvent the wheel, it integrates the best of the best industry-standard and proven open source frameworks, as you'll see in the "Grails Architecture" section.

Groovy

Groovy is one of the pillars of Grails. As you learned in Chapters 13, Groovy is a powerful and flexible open source language that stands on its own. However, its integration with Java, dynamic scripting features, and simple syntax makes it a perfect complement to Grails and provides the agile nature of the entire solution.

Spring Framework

Spring Framework2 (Spring) is best described by its creator Rod Johnson as providing an application level of abstraction on top of the Java EE API. For example, rather than having to deal with the details of handling transactions, Spring provides a means for declaring transactions around regular Plain Old Java Objects (POJOs), so you can focus on implementing business logic. In addition, because Spring brings Java EE features to POJOs, you're able to develop and test your application code outside a Java EE container, thereby increasing productivity. Along with Hibernate, Spring was a major influence on the new Enterprise JavaBeans (EJB) 3.0 spec, which attempts to simplify Java EE development.

Grails implicitly handles much of the Spring integration. However, in the "Injecting into the Service" section of Chapter 6, you will learn how to explicitly configure and integrate with Spring if you find it necessary.

__________

Hibernate

Hibernate3 is an object-relational persistence framework that provides the foundation for GORM. It's able to map complex domain classes written as POJOs or POGOs to relational database tables, as well as map relationships between the tables. As mentioned in the previous section, Hibernate had a big influence on the EJB 3.0 specification, specifically the Java Persistence API (JPA). Hibernate is one of the many JBoss projects.

SiteMesh

SiteMesh4 is a web page layout framework that implements the decorator design pattern for rendering HTML with constant components such as the header, footers, and navigation. It is one of the components found in the OpenSymphony suite and is hosted on the OpenSymphony site.5 Grails hides most of the SiteMesh details from you as a developer, but in Chapter 5, you'll see how to create page layouts and other web components such as GSP.

Ajax Frameworks

Web 2.0 functionality has become so popular that Grails includes three popular Ajax frameworks by default in every web application: script.aculo.us,6 Rico,7 and Prototype.8 Some of the Grails tag libraries even integrate with them to make standard Ajax behavior simple even for the Ajax beginner. Chapter 8 will explain how to add Ajax functionality to your application to increase usability.

Jetty

To ensure Grails has a complete development environment, it includes the popular and fast Jetty web container/server.9 Grails makes application and container life-cycle management easy by using its command-line interface to start and stop the server while taking care of packaging and deploying the application behind the scenes. But by no means are Grails applications limited to running in a Jetty container. Chapter 12 will explain how to deploy Grails applications to other containers.

__________

HSQLDB

Having a complete development environment out of the box requires a relational database. Grails includes the 100% Java database called HSQLDB.10 You can use this database either as a standalone database server or as an embedded database. You can also config ure it to run in memory or persisted to disk. By default, Grails uses the embedded in-memory configuration, so that each time the application is run, the database is rebuilt from scratch and all data is lost. Chapter 12 will explain how to configure Grails to use other databases such as MySQL.11

JUnit

For unit testing, Grails uses the popular JUnit framework.12 In the "Implementing Integration Tests" section, you will learn to write and run unit tests.

Grails Architecture

Now that you know some of the features and open source frameworks included in Grails, you are more prepared to understand the Grails architecture. Figure 4-1 depicts the architecture graphically.

image

Figure 4-1. Grails architecture

In Figure 4-1, notice that the foundation of Grails is the Java Virtual Machine (JVM). Also, notice the separation in the architecture from the Java language and the JVM. In the past couple of years, the Java community has seen a rash of new and ported languages being run on the JVM. This is particularly important in Grails, because in the next level up from the JVM, you see that both the Java and Groovy languages are being used.

__________

Above the languages you find the Grails framework itself, which, as you know from the previous section, is made up of several industry-standard open source projects such as Spring, SiteMesh, and GORM/Hibernate, to name just a few. However, as an application developer, you're not limited to the libraries and frameworks Grails has to offer. Your application can use just about any Java library, whether open source or proprietary. The final layer of the architecture is the applications you will build with Grails. Typically, this layer follows the MVC pattern. Grails also makes it easy to organize your application to make coarse-grained services.

To simplify development, Grails includes a command-line tool for creating many Grails artifacts and managing Grails projects. The Grails command line is built on top of Gant,13 a build system that uses the Groovy language to script Apache Ant14 tasks rather than Ant's XML format. You will learn more about Gant and adding your own scripts to the Grails command line in Chapter 12.

From a runtime perspective, you can think of Grails out of the box as looking like Figure 4-2.

image

Figure 4-2. Grails default runtime

In Figure 4-2, you see a web browser making a request to a Jetty web container. The container forwards the request on to a controller in a similar fashion to the standard MVC model. The controller may set or use data from a domain class (model). As mentioned earlier, all Grails domain classes are persistable through the GORM framework. You don't need to use a Data Access Object (DAO) pattern or write SQL to persist objects. In Chapter 6, you will learn how to take full advantage of the persistable domain classes.

__________

Out-of-the-box Grails uses an embedded HSQLDB database, which means the database runs in the same JVM as your application and the Jetty web container. When the controller is done, it forwards the request to a GSP, which is the view technology to render the HTML that is returned to the requesting browser.

Installing Grails

Considering the alternative to using Grails—downloading and installing a web container or application server, database, and MVC framework—installing Grails seems almost too easy. All you need to do is uncompress a file and set up some environment variables, and you're done. Most everything is self-contained. Grails does require two prerequisites, though: you must have a JDK 1.4 or greater, and you must have the JAVA_HOME environment variable configured for that JDK.

Follow these steps to install Grails:

  1. Download the latest Grails .zip or .tar/.gz file release from http://grails.codehaus.org/Download.
  2. Extract the archive to your preferred location.
  3. Create a GRAILS_HOME environment variable that points to the path where the Grails archive was extracted.
  4. Append the GRAILS_HOMEin directory to the PATH environment variable.

Once you complete these steps, the Grails command line will be available. You can use it to create the project, create artifacts, run the application, and package the application.

Collab-Todo Application

Throughout this book, we'll use a single web application example to demonstrate how to write a web application using the Grails framework. The application name is Collab-Todo, and it is a collaborative Web 2.0 to-do application. The application allows users to create and manage to-dos in categories. It also allows users to create buddy lists of other Collab-Todo users to make it easy to assign tasks to other users. In addition, it includes reports and batch e-mails, along with a thick client and web service access. Figure 4-3 shows what Collab-Todo will look like by the end of the book.

image

Figure 4-3. Final version of the Collab-Todo application

Getting Started with Scaffolding

We feel it is important for you to experience for yourself the power and productivity of Grails early on. So for the remainder of this chapter, you will be learning to take advantage of Grails conventions and scaffolding to create a simple but functional version of the Collab-Todo application. This initial version of the application will not be a production-suitable application from a usability and design perspective. However, the Grails scaffolding is able to render a simple but functional CRUD web application with almost no code besides your domain class code. In addition, Grails will generate a database schema and populate a database with the schema when the application is run. This scaffolding-based version of the application is suitable for testing domain objects as well as quick application prototyping.

Figure 4-4 shows an example of what the CRUD to-do pages will look like by the end of the chapter.

image

Figure 4-4. Todo List page

As you can see in Figure 4-4, the Todo List page provides the ability to view all the to-dos in the database as well as create new to-dos or delete existing to-dos. It also provides a link to the edit page, where you can create a new to-do or updating an existing to-do.

Figure 4-5 shows the Edit Todo page, which is basically the same page used for updating and creating to-dos.

image

Figure 4-5. Edit Todo page

As you can tell from Figures 4-4 and 4-5, this is not the most attractive application, and you probably won't want to release this to your users. In Chapter 5, you'll learn how to make the application more usable and pleasing to your users' eyes, and in Chapter 8, you'll learn how to make it even better by adding Web 2.0 features.

Figure 4-6 is a Unified Modeling Language (UML) diagram of a subset of the Collab-Todo domain classes you'll be creating in this chapter.

image

Figure 4-6. Subset of the Collab-Todo domain classes

Notice the domain class in Figure 4-6 is very simple and includes only three domain classes. In Chapter 6, you will extend this domain model and learn about the Grails persistence framework, GORM. Initially, the domain consists of a User class, which has a Todo (task) class, and user-defined Category classes used for organizing to-dos into logical groups.

Understanding the Scaffolding Process

Creating a Grails application using its scaffolding is really quite simple. The following seven basic steps summarize the process:

  1. Create an application.
  2. Run the application.
  3. Create the domain class.
  4. Implement the integration test.
  5. Run the test harness and update the domain classes until the tests pass.
  6. Create the controller.
  7. Repeat steps 3 through 6 until the domain class and the controllers are complete.

Notice that none of these steps say anything about creating a view, HTML, or anything related to the user interface (UI). You don't even have to run anything to generate the presentation. The scaffolding uses introspection to determine the appropriate interface to render at runtime.

Because Grails is an agile framework that tries to provide feedback quickly, you want to run the application immediately after creating it so you can observe how changes to the domain class or controllers affect the application.

Creating the Application

To create the application, you follow a pattern that is repeated for creating just about everything in Grails: you execute a Grails target on the command line. You use the create-app target to create a new application. This generates the basic application structure and populates the structure with some basic files.

To create the Collab-Todo application, you need to execute the create-app target using an optional project name, as shown here:

>grails create-app collab-todo

If you don't supply the project name, you will be prompted for one.

The output of executing the create-app target is shown here:


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

Base Directory: C:devlworkspace
Environment set to development
Note: No plugin scripts found
Running script C:devljavagrails-1.0scriptsCreateApp.groovy
    [mkdir] Created dir: C:devlworkspacecollab-todosrc
    [mkdir] Created dir: C:devlworkspacecollab-todosrcjava
    [mkdir] Created dir: C:devlworkspacecollab-todosrcgroovy
    [mkdir] Created dir: C:devlworkspacecollab-todograils-app
    [mkdir] Created dir: C:devlworkspacecollab-todograils-appcontrollers
    [mkdir] Created dir: C:devlworkspacecollab-todograils-appservices
    [mkdir] Created dir: C:devlworkspacecollab-todograils-appdomain
    [mkdir] Created dir: C:devlworkspacecollab-todograils-app aglib
    [mkdir] Created dir: C:devlworkspacecollab-todograils-apputils
    [mkdir] Created dir: C:devlworkspacecollab-todograils-appviews
    [mkdir] Created dir: C:devlworkspacecollab-todograils-appviewslayouts
    [mkdir] Created dir: C:devlworkspacecollab-todograils-appi18n
    [mkdir] Created dir: C:devlworkspacecollab-todograils-appconf
    [mkdir] Created dir: C:devlworkspacecollab-todo est
    [mkdir] Created dir: C:devlworkspacecollab-todo estunit
    [mkdir] Created dir: C:devlworkspacecollab-todo estintegration
    [mkdir] Created dir: C:devlworkspacecollab-todoscripts
    [mkdir] Created dir: C:devlworkspacecollab-todoweb-app
    [mkdir] Created dir: C:devlworkspacecollab-todoweb-appjs
    [mkdir] Created dir: C:devlworkspacecollab-todoweb-appcss
    [mkdir] Created dir: C:devlworkspacecollab-todoweb-appimages
    [mkdir] Created dir: C:devlworkspacecollab-todoweb-appWEB-INFclasses
    [mkdir] Created dir: C:devlworkspacecollab-todoweb-appMETA-INF
    [mkdir] Created dir: C:devlworkspacecollab-todolib
    [mkdir] Created dir: C:devlworkspacecollab-todograils-appconfspring
    [mkdir] Created dir: C:devlworkspacecollab-todograils-appconfhibernate
[propertyfile] Creating new property file:
         C:devlworkspacecollab-todoapplication.properties
     [copy] Copying 2 files to C:devlworkspacecollab-todo
     [copy] Copying 2 files to C:devlworkspacecollab-todoweb-appWEB-INF
     [copy] Copying 5 files to C:devlworkspacecollab-todoweb-appWEB-INF ld
     [copy] Copying 87 files to C:devlworkspacecollab-todoweb-app
     [copy] Copying 16 files to C:devlworkspacecollab-todograils-app
     [copy] Copying 1 file to C:devlworkspacecollab-todograils-appconfspring
     [copy] Copying 1 file to C:devlworkspacecollab-todo
     [copy] Copying 1 file to C:devlworkspacecollab-todo
     [copy] Copying 1 file to C:devlworkspacecollab-todo
[propertyfile] Updating property file:
         C:devlworkspacecollab-todoapplication.properties
Created Grails Application at C:devlworkspace/collab-todo

After the create-app target has run, you will have a new directory matching the name of your project. This will be the root of your new project, and you must make all subsequent Grails command-line calls from within this directory. It's a good idea to use the cd command to get into the directory now so you don't forget. Within the new project directory, you will find a structure matching the directory structure found in Figure 4-7.

image

Figure 4-7. Directory structure created by running the create-app target

As mentioned earlier, the directory structure generated from the create-app target is a part of the Grails practice of convention over configuration. The target provides locations for placing common artifacts. Throughout the book, you will learn details about each directory. For now, Table 4-1 provides a summary of the more important directories.

Table 4-1. Important Directories in the Grails Convention

Directory Description
grails-app/conf Common configuration files such as bootstrapping, logging, data source, and URL mapping (see the "Configurations" sidebar)
grails-app/conf/hibernate Custom Hibernate mappings, which are rarely needed (covered in Chapter 6)
grails-app/conf/spring Custom Spring mapping files
grails-app/controllers Application controllers that handle requests (covered in Chapter 5)
grails-app/domain Domain model classes (covered in Chapter 6)
grails-app/i18n Internationalized message bundles (covered in Chapter 5)
grails-app/services Services (covered in Chapter 6)
grails-app/taglib Custom dynamic tag libraries
grails-app/views GSP (covered in Chapter 5)
grails-app/views/layout Commonly shared page layouts (covered in Chapter 5)
lib Third-party JAR files, such as database drivers
scripts Gant script for automating tasks (covered in Chapter 12)
src/java Additional Java source files
src/groovy Additional Groovy files
test/integration Integration tests (covered in Chapter 5)
test/unit Unit tests (introduced later in this chapter)
web-app Web artifacts that will ultimately comprise a web application archive (WAR) (many of the artifacts are covered in Chapter 5
web-app/css Cascading Style Sheets (covered in Chapter 5)
web-app/images Web graphics (covered in Chapter 5)
web-app/js JavaScript (covered in Chapter 8)
web-app/WEB-INF Common Servlet specification WEB-INF directory containing private artifacts such as configuration files like web.xml, the Spring application context, and the SiteMesh config

At this point, you're able to open the project in your development tool of choice. Not only does the create-app target generate the directory structure, but it also generates project files for Eclipse15 and TextMate.16 Another option is to use IntelliJ IDEA,17 because it understands how to consume Eclipse project files.

__________



Running the Application

At this point, you have a functional application that you can run and access via a web browser. It really does not do much yet, but running it now will enable you to get instant feedback as you add domain and controller classes.

To run a Grails application, execute the run-app target from your project root directory as shown here:

> grails run-app

The output of executing the run-app target is shown here:


...
Running script C:devljavagrails-1.0scriptsRunApp.groovy
    [mkdir] Created dir: C:devlworkspacecollab-todoweb-appWEB-INFlib
    [mkdir] Created dir:
    C:Documents and SettingsAdministrator.grails1.0projectscollab-todoclasses
Compiling 4 source files to
  C:Documents and SettingsAdministrator.grails1.0projectscollab-todoclasses
    [mkdir] Created dir:
    C:devlworkspacecollab-todoweb-appWEB-INFgrails-appi18n
     [copy] Copying 8 files to
    C:devlworkspacecollab-todoweb-appWEB-INFgrails-appi18n
     [copy] Copying 1 file to C:devlworkspacecollab-todoweb-appWEB-INFspring
     [copy] Copying 1 file to
    C:Documents and SettingsAdministrator.grails1.0projectscollab-todoclasses
     [copy] Copying 1 file to
    C:Documents and SettingsAdministrator.grails1.0projectscollab-todoclasses
Running Grails application..
2007-10-12 23:16:09.891::INFO: jetty-6.1.4
...

2007-10-12 23:16:09.891::INFO: Started [email protected]:8080
Server running. Browse to http://localhost:8080/collab-todo

Running the run-app target does some initial project setup by copying files into your web application's WEB-INF directories. Then it starts a Jetty web container that listens on localhost port 8080. Jetty then loads the application, causing the Grails internal classes to get initialized for filtering URL mappings and requests along with GrailsDispatchServlet. Ultimately, you will know that the application server is available and you can start testing your application when you see this:


Server running. Browse to http://localhost:8080/collab-todo.

To test your app, point your web browser of choice to that URL, as shown in Figure 4-8.

Figure 4-8 shows the standard default Grails page. As you can see, it is not very interesting; it looks like a static page. However, as you'll see soon, there is some dynamism to this page. If you wish to modify this page, it is the GSP web-appindex.gsp.

image

Figure 4-8. Default Grails page

Creating a Domain Class

At this point, the application doesn't really do anything, so you will have to add some additional classes for the application to take shape. Typically, you begin by creating a domain class and then follow it up with an associated controller class. To create a domain class, use the Grails create-domain-class target. This creates a new Grails domain class in the grails-app/domain directory, as well as an integration test for the domain class in test/integration.


Note The create-domain-class target creates an integration test rather than a unit test, because as you will learn in Chapter 6, domain classes have dependencies on the database. Making the domain test an integration test by putting it in the test/integration directory enables the test to have access to the entire Grails environment, including the database. Without this, the domain class would not have access to the dynamic methods available to domain classes for persistence.


To create the Todo domain class, you need to execute the create-domain-class target using an optional class name, as shown here:

>grails create-domain-class todo

If you don't supply the class name, you will be prompted for one.

The output of executing the, create-domain-class target is shown here:


Running script C:devljavagrails-1.0scriptsCreateDomainClass.groovy
     [copy] Copying 1 file to C:devlworkspacecollab-todograils-appdomain
Created for Todo
     [copy] Copying 1 file to C:devlworkspacecollab-todo estintegration
Created Tests for Todo

Notice that when running the create-domain-class target with the optional class name, you can leave the class name in lowercase, and Grails will automatically uppercase it for you so that it follows the standard Groovy class-naming convention. Listing 4-1 shows the generated Todo domain class.

Listing 4-1. Grails-Generated Todo Domain Class

class Todo {
}

The Todo domain class in Listing 4-1 appears to be the most basic of Groovy classes. As you will learn in the next couple of sections and in the next two chapters, there is more to Grails domain classes than meets the eye.

Implementing Integration Tests

As you saw in the previous section, Grails is true to its testing convention by generating a corresponding integration test every time you create a domain class. Listing 4-2 shows the generated integration test for the Todo domain class.

Listing 4-2. Grails-Generated Integration Test for the Todo Domain Class

class TodoTests extends GroovyTestCase {

  void testSomething() {
  }
}

Notice that the integration test in Listing 4-2 extends the standard Groovy test case, which extends the JUnit test case and adds some convenience assert methods. The generated integration test also includes a test method template.


Note As is true with all JUnit test cases, any method prefixed with test will be treated as a test and is executed when the test harness is run.


Listing 4-3 shows the Todo integration tests updated with two tests: one for testing the toString() method and one for persisting Todo objects.

Listing 4-3. Todo Integration Tests

1  class TodoTests extends GroovyTestCase {
2
3    void setUp() {
4      Todo.list()*.delete()
5    }
6
7    void testPersist() {
8      new Todo(name: "1", createdDate:new Date(), priority: "", status:"").save()
9      new Todo(name: "2", createdDate:new Date(), priority: "", status:"").save()
10     new Todo(name: "3", createdDate:new Date(), priority: "", status:"").save()
11     new Todo(name: "4", createdDate:new Date(), priority: "", status:"").save()
12     new Todo(name: "5", createdDate:new Date(), priority: "", status:"").save()
13
14     assert 5 == Todo.count()
15   }
16
17   void testToString() {
18     def todo = new Todo(name: "Pickup laundry")
19     assertToString(todo, "Pickup laundry")
20   }
21 }

The integration test in Listing 4-3 contains three methods. The setUp() method on lines 3–5 is a standard JUnit life-cycle method. It is called prior to executing any method prefixed with test and should be used to put your tests into a known state. In this case, the known state is making sure there are no Todos in the database. It does this by using the dynamic list() method on the Todo class. You were warned that Grails domain classes are more complicated than they appear.

On a Grails domain class, GORM provides the list() method, which returns a list containing all the records of that type in the database. In the setup() method, you want to delete all the Todos returned in the list. Rather than iterate through each object in the list, you can use the Groovy spread operator (*.) to call the following method on each of the objects in the list. In this case, the delete() method removes that object instance from the database.


Caution Take care when using the list() method, because it returns all records of the specified type and can cause large memory consumption, depending on the number of records in the database.


The testPersist() method on lines 7–15 creates five new instances of the Todo domain class and then saves them to the database using another dynamic Grails domain class method, save(). Finally, this test method validates that there are five Todos in the database by using another dynamic Grails domain class method of count() to get the number of records in the database. It also uses the Groovy assert keyword to validate the proper number of records.


Note Whenever unit tests are run or the default configured application is started, an in-memory HSQLDB database is loaded. This in-memory database does not save anything to disk, so each time the application or tests are run, you will have a new, clean database. In addition, Hibernate will use the domain classes to create or update a schema in that database.


The testToString() test on lines 17–20 is a pretty basic and self-explanatory unit test that tests the toString() method of the Todo domain class. The only item of interest is the assertToString() method. You may not recognize this as the standard JUnit assert method. That's because it is implemented by the GroovyTestCase class.

Running the Test Harness

At this point, you want to run the test harness. Tests are expected to fail, because the Todo class still has no implementation. After seeing the failures, you add functionality to the Todo class and iterate over implementing and testing until all the tests pass.

To run the test harness, simply execute the Grails test-app target. This executes all the unit and integration tests and displays the results of each test. In addition, it creates several reports for diagnosing any test errors or failures:

>grails test-app

The output of executing the test-app target is shown here:


...
Running script C:devljavagrails-1.0scriptsTestApp.groovy
...
No tests found in test/unit to execute ...
--------------------------------------------------------
Running 2 Integration Tests...
Running test TodoTests...
                    testPersist...SUCCESS
                    testToString... FAILURE
Integration Tests Completed in 711ms
--------------------------------------------------------
[junitreport]
  Processing C:devlworkspacecollab-todo est eportsTESTS-TestSuites.xml
[junitreport]
  Loading stylesheet jar:file:/C:/devl/java/grails-1.0/lib/ant-junit.jar
  !/org/apache/tools/ant/taskdefs/optional/junit/xsl/junit-frames.xsl
[junitreport] Transform time: 751ms
Tests failed: 0 errors, 1 failures, 0 compilation errors. View reports in
  C:devlworkspacecollab-todo/test/reports

The results of running the test harness show no unit tests were run, because no unit tests have been added to the test/unit directory yet. They also show that two integration tests were run—one that was successful, and one that failed. Because the Todo class does not have any implementation yet, you expected both tests to fail. So, you should evaluate the testPersist test to understand why it passed and to improve the test to make sure it fails before writing the Todo implementation.

Listing 4-4 shows the original implementation of the testPersist test.

Listing 4-4. Original testPersist

void testPersist() {
  new Todo(name: "1", createdDate:new Date(), priority: "", status:"").save()
  new Todo(name: "2", createdDate:new Date(), priority: "", status:"").save()
  new Todo(name: "3", createdDate:new Date(), priority: "", status:"").save()
  new Todo(name: "4", createdDate:new Date(), priority: "", status:"").save()
  new Todo(name: "5", createdDate:new Date(), priority: "", status:"").save()

  assert 5 == Todo.count()
}

In Listing 4-4, five Todos are saved to the database and the count of the inserted Todos are validated. This currently works because even at this point with no Todo implementation, Todo objects can be saved to the database. However, looking at the database, you should see a record with no name, createdDate, priority, or status, because those properties don't exist yet. The parameterized constructor happily accepts parameters that do not map to properties, so this test passes with no problems. To make the test more valuable, try retrieving at least one of the Todos based on the value of one of its properties, as shown in Listing 4-5.

Listing 4-5. Improved testPersist

void testPersist() {
  new Todo(name: "1", createdDate:new Date(), priority: "", status:"").save()
  new Todo(name: "2", createdDate:new Date(), priority: "", status:"").save()
  new Todo(name: "3", createdDate:new Date(), priority: "", status:"").save()
  new Todo(name: "4", createdDate:new Date(), priority: "", status:"").save()
  new Todo(name: "5", createdDate:new Date(), priority: "", status:"").save()

  assert 5 == Todo.count()
  def actualTodo = Todo.findByName('1')
  assert actualTodo
  assert '1' == actualTodo.name
}

Listing 4-5 is the same as Listing 4-4 with three additional lines at the end. After the saves and validating the count, the test now looks up a Todo by name, using the dynamic findByName() method that we'll discuss in Chapter 6. Then the test asserts the object returned is not null, and then it validates a value.

Running the test harness again shows that both integration tests fail. In addition, it produces a JUnit HTML report, shown in Figure 4-9 and found in the test/reports/html directory. This can help you determine why the tests failed.

image

Figure 4-9. Example unit test report

Drilling into TodoTests, you can see that testPersist errored because there is no property of name found on Todo, so the findByName() method could not be invoked properly. You also learn testToString() fails because the return value does not equal the expected value. Until you override toString() in the Todo class, the class returns the Grails domain class default toString().

In addition to the HTML report shown in Figure 4-9, a text version and an XML version of the unit test results are also generated in the test/reports/plain andtest/reports directories, respectively.

Implementing a Domain Class

Now that the tests are implemented and failing as expected, it is time to update the domain class until all tests pass. Listing 4-6 shows the Todo domain class after adding the fields shown in the UML diagram in Figure 4-6.

Listing 4-6. Todo Domain Class After Adding Attributes, Constraints, and the toString()Method

1  class Todo {
2
3    String name
4    String note
5    Date createdDate
6    Date dueDate
7    Date completedDate
8    String priority
9    String status
10
11    static constraints = {
12      name(blank:false)
13      createdDate()
14      priority()
15      status()
16      note(maxSize:1000, nullable:true)
17      completedDate(nullable:true)
18      dueDate(nullable:true)
19    }
20
21    String toString() {
22      name
23    }
24 }

In Listing 4-6, you see that lines 3–9 are several fields of both String and Date types that you would expect to find in a Todo domain class. However, there are some additional properties you don't see that are implicit to a Grails domain class by convention. They include the id and version properties.

The id property, as you might expect, represents a unique autoincrementing identifier and is null until the object is initially saved. The version property is a Hibernate mechanism for managing optimistic locking. Each time an object is saved, its version number gets incremented, and like the id, it is initially null. Before Hibernate saves any object, it first checks the version number in the database, and if the versions don't match the object about to be saved—meaning it was already modified since the last read—Hibernate will throw an org.hibernate.StaleObjectStateException.

Lines 11–19 demonstrate the Grails construct of constraints. These are basically rules governing the values of the properties. For example, line 12 states the name property is required and may not be empty using a blank:false constraint, while the note, completedDate, and dueDate properties on lines 16–18 are allowed to be null. Also, note that the note property on line 16 has a maximum length of 1,000 characters. In addition to constraining the properties, the constraints dictate the order of fields on the edit pages as well as the types of HTML form fields rendered by the Grails scaffolding. The order of the constraints represents the order of the fields on the page. While a String is usually represented by an HTML input field of type text, a String property with a maxSize is usually rendered as an HTML input field of textarea to support the larger amounts of input data.

Domain classes can also have behavior implemented as methods. Lines 21–23 show the toString() method being overridden. The default toString() behavior of a Grails domain class is to print the class name followed by a colon and the object ID. To make the toString() a little more helpful, line 21 prepends the name property of the Todo instance.

This section has not even scratched the surface of domain classes. You will learn lots more about them in Chapter 6.

Now that the Todo class is complete, rerunning the test harness will result in both tests passing.


Creating the Controller

The last step before iterating over these steps over and over again is to create the controller. As mentioned earlier, the controller is responsible for the interaction between the view and the domain classes. Fortunately, the Grails scaffolding makes this simple. The controller consists of only a single line of code that instructs the scaffolding to do its magic and generate the basic CRUD UI.

To create a controller class, use the Grails create-controller target. This creates a new Grails controller class in the grails-app/controllers directory, as well as an integration test for the controller class in test/integration. It also creates a grails-app/views/<controller name> directory if it doesn't exist already.

To create the TodoController class, you need to execute the create-controller target using an optional class name, as shown here:

>grails create-controller todo

If you don't supply the class name, you will be prompted for one.

The output of executing the create-controller target is shown here:


...
Running script C:devljavagrails-1.0scriptsCreateController.groovy
     [copy] Copying 1 file to C:devlworkspacecollab-todograils-appcontrollers
Created Controller for Todo
    [mkdir] Created dir: C:devlworkspacecollab-todograils-appviews odo
    [copy] Copying 1 file to C:devlworkspacecollab-todo estintegration
Created ControllerTests for Todo

Notice that when running the create-controller with the optional class name, you can leave the class name in lowercase, and Grails will automatically uppercase it for you so that it follows the standard Groovy class-naming convention. Listing 4-7 shows the generated TodoController class.

Listing 4-7. Grails-Generated TodoController Class

class TodoController {
  def index = { }
}

The TodoController class in Listing 4-7 contains an empty index action. Chapter 5 will explain actions on the controller.

For now, to use the Grails scaffolding, change the index action to a scaffold property and assign it the domain class, as shown in Listing 4-8. That's all there is to it. This causes List Page, Create Page, Edit Page, and Show Page views, as well as delete functionality, to be generated for the specified domain class.

Listing 4-8. Scaffolding-Enabled TodoController

class TodoController {

  def scaffold = Todo
}

After changing TodoController to look like it does in Listing 4-8, refresh your browser to reveal a new TodoController link on the initial page, as shown in Figure 4-10.


Tip You can make most changes, including changes to the domain and controller classes, without having to restart the web server. Sometimes, however, changes require a restart. Creating a new controller sometimes requires a restart, so if the controller doesn't appear in the controllers list, stop the server using Ctrl+C and the Grails run-app target to restart.



image

Figure 4-10. Initial Grails page with the TodoController link added

Selecting the TodoController link in Figure 4-10 brings you to the Todo List page shown in Figure 4-11. The Todo List page is a paginated list of domain objects.

image

Figure 4-11. Todo List page

From the list page shown in Figure 4-11, you have the ability to create new domain objects (as shown in Figure 4-12) by clicking the New button on the navigation bar. You can also show domain objects (as shown in Figure 4-14) by clicking on the ID.

The save page displays the list of properties in the order in which the constraints are ordered as shown in the Create Todo page in Figure 4-12. Note that the Grails scaffolding displays the edit fields in the appropriate date type format. A String is displayed as an HTML text input field, while createdDate is displayed as a series of drop-downs for the day of month, month, year, hour, and seconds. In addition, the note property is displayed as an HTML text-area input due to the maxSize constraint. That's not all: the Grails scaffolding is also smart enough to use the constraints for doing form validation, as shown in Figure 4-13.

image

Figure 4-12. Create Todo page

image

Figure 4-13. Validation errors

Notice in Figure 4-13 that a validation message is displayed at the top of the page if the form is submitted with an empty name. The blank:false constraint makes the name property required.

Figure 4-14 shows a read-only view that gets displayed when the ID is selected from the Todo List page.

image

Figure 4-14. Show Todo page

The Show Todo page displays all the properties and values. It also provides access to the Edit Todo page, as shown in Figure 4-15, where you can delete the domain object.

image

Figure 4-15. Edit Todo page

The Edit Todo page, shown in Figure 4-15, is really the same as the Create Todo page, except the data is repopulated with the domain object values, and it has Update and Delete buttons at the bottom of the page.

Finishing the Remaining Domain and Controllers

Now that you've created the Todo domain and controller classes, the steps for creating the domain classes and controller classes can be repeated for the remaining domain and controllers: Category and User. Then you can add the relationships between the domain classes.

As illustrated in the UML diagram in Figure 4-6, the Category class is very simple. Listing 4-9 shows the code after generating the class using the Grails create-domain-class target and adding the properties from the UML diagram.

Listing 4-9. Category Domain Class

class Category {
  String name
  String description

  static constraints = {
    name(blank:false)
  }

  String toString() {
    name
  }
}

In Listing 4-9, you see the Category class just has name and description properties and an overloaded toString() method that returns the name property. This overloaded toString() method will become important later, because it is used to populate an HTML select field. Without it, the select field would only display the category's id, making it difficult to differentiate categories in the list. The Category class also includes a constraint, which requires the name property.

The User domain class follows the same pattern as both the Todo and Category classes. Listing 4-10 shows the code after generating the class with the Grails create-domain-class target and adding the properties from the UML diagram.

Listing 4-10. User Domain Class

class User {
  String userName
  String firstName
  String lastName

  static constraints = {
    userName(blank:false,unique:true)
    firstName(blank:false)
    lastName(blank:false)
  }

  String toString () {
    "$lastName, $firstName"
  }

}

In Listing 4-10, you see the User class contains userName, firstName, and lastName properties. It also contains constraints that make all properties required and forces the userName property to be unique in the database. Finally, the overridden toString() method returns the user's name in a last-name-first format.


Note If you're making changes to domain or controller classes while the application is running with the default development data-source configurations, it is common for all the data to disappear. This happens because the changes to the domain class cause Hibernate to regenerate the schema. In Chapter 12, you'll learn how to change the default development data-source configurations once the domain classes have been completed.


The controller classes are carbon copies of TodoController. Listing 4-11 shows CategoryController after it has been created with the Grails create-controller target and the scaffolding variable set.

Listing 4-11. CategoryController

class CategoryController {

  def scaffold = Category
}

Listing 4-12 showsUserController.

Listing 4-12. UserController

class UserController {

  def scaffold = User
}

After completing the Category and User domain and controller classes, the application start page displays all three controllers. In addition, CRUD pages for both Category and User are available when clicking on the respective controller links.

Creating Domain Relationships

At this point, you have three standalone domain classes—Todo, Category, and User—with no relationships between them. But remember that the UML diagram in Figure 4-6 showed that users had user-defined categories as well as to-dos that were organized by categories.

Now it's time to represent those one-to-many relationships between the domain classes using the belongsTo and hasMany properties.

Listing 4-13 shows the new Todo class, which shows the relationships with User and Category.

Listing 4-13. Todo Domain Class with Relationships to the User and Category Domain Classes

class Todo {
  String name
  String note
  Date createdDate
  Date dueDate
  Date completedDate
  String priority
  String status
  User owner
  Category category

  static belongsTo = [User, Category]

  static constraints = {
    name(blank:false)
    createdDate()
    priority()
    status()
    note(maxSize:1000, nullable:true)
    completedDate(nullable:true)
    dueDate(nullable:true)
  }

  String toString() {
    name
  }

}

Notice in Listing 4-13 that the relationship is defined with the belongsTo property to the User and Category classes. We've also added new owner and category properties.belongsTo tells GORM to delete the to-do if either the associated user or the category is deleted. Scaffolding renders these relationships as the select fields on the Create Todo and Edit Todo screens, as shown in Figure 4-16.

image

Figure 4-16. Create Todo edit page with Category and Owner select fields

Notice in Figure 4-16 that Category and Owner are select fields that display the domain class toString() method results.

Like the Todo class, the Category class has a belongsTo relationship with User, but it also has a collection of Todos, as shown in Listing 4-14.

Listing 4-14. Category Domain Class with Relationships to the User and Todo Domain Classes

class Category {
  String name
  String description
  User user
  static belongsTo = User
  static hasMany = [todos: Todo]

  static constraints = {
    name(blank:false)
  }

  String toString() {
    name
  }

}

Notice that in addition to belongsTo associated with User, Category contains a hasMany property, which is a map designating a one-to-many relationship with the Todo class. Also, the collection is available via the todos property.

Finally, the User class in Listing 4-15 has now defined its relationships with Todo and Category.

Listing 4-15. User Domain Class with Relationships to the Todo and Category Domain Classes

class User {
  String userName
  String firstName
  String lastName

  static hasMany = [todos: Todo, categories: Category]

  static constraints = {
    userName(blank:false,unique:true)
    firstName(blank:false)
    lastName(blank:false)
  }

  String toString () {
    "$lastName, $firstName"
  }

}

As shown in Listing 4-15, the changes required to include the relationships with the Todo and Category classes only take a single line. The hasMany property identifies collections named todos and categories.


Note We'll discuss relationships in more detail in Chapter 6.


As stated earlier, when you run the application, Hibernate creates a database schema based on domain classes. Figure 4-17 shows an entity relational diagram of the domain classes so far.

image

Figure 4-17. Entity relational diagram

Notice that the entity relational diagram looks almost identical to the UML diagram shown in Figure 4-6. Hibernate creates three tables with table names based on the class names. It also creates fields for each of the properties using underscores instead of CamelCase. Also, notice the id and version columns are the Hibernate columns for object identity and optimistic locking. Hibernate also creates foreign keys to represent the relationships between the domain classes.

During development, out-of-the-box Grail uses a Jetty web container and an embedded HSQLDB database. However, many operational environments use other web containers or applications servers and use server-based databases. In Chapter 12, we will look at how to package and deploy an application to an alternative operating environment.

Summary

In this chapter, you were introduced to the fact that Grails is a new web development framework that combines the best of Java open source, conventions, the Groovy dynamic language, and the power of the Java platform.

You also saw how easy it is to develop a fully functional application using Grails scaffolding to do most of the work. In Chapter 5, you will learn how to make the application pretty and customize it to your own look and feel, as well as make the controllers more functional. Then in Chapter 6, you will learn how to enhance the domain classes, including accessing more of the persistence features of GORM.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset