In Chapter 1, you got your first introduction to the Grails framework and a feel for the basic command-line interface while creating the basis for the gTunes application. In this chapter, we're going to build on that foundation by showing how you can use Grails' scaffolding feature to quickly build a prototype application that can generate simple CRUD (Create, Read, Update, Delete) interfaces.
Then we'll start to explain some of the basic concepts within the Grails ecosystem such as environments, data sources, and deployment. Get ready—this is an action-packed chapter with loads of information!
Scaffolding is a Grails feature that allows you to quickly generate CRUD interfaces for an existing domain. It offers several benefits, the most significant of which is that it serves as a superb learning tool, allowing you to relate how Grails' controller and view layers interact with the domain model that you created.
You should note, however, that Grails is not just a CRUD framework. And scaffolding, although a useful feature in your repertoire, is not Grails' main benefit. If you're looking for a framework that provides purely CRUD-oriented features, better options are at your disposal.
As with a lot of Grails features, scaffolding is best demonstrated visually, so let's plunge right in and see what you can do.
Grails' domain classes serve as the heart of your application and business-model concepts. If you were constructing a bookstore application, for example, you would be thinking about books, authors, and publishers. With gTunes you have other thoughts in mind, such as albums, artists, and songs.
The most significant attribute that differentiates domain classes from other artifacts within a Grails application is that they are persistent and that Grails automatically maps each domain class onto a physical table in the configured database. (You'll learn more about how to change the database setup later in the chapter.)
The act of mapping classes onto a relational database layer is also known as object-relational mapping (ORM). Grails' ORM layer, called GORM, is built on the ever-popular Hibernate library (http://www.hibernate.org).
Domain classes reside snugly in the grails-app/domain
directory. You create a domain class by using the grails create-domain-class
helper command, or your favorite IDE or text editor. Type the helper command shown in Listing 2-1 into a command window from the root of the gTunes project.
Listing 2-1. Creating the Song Domain Class
$ grails create-domain-class com.g2one.gtunes.Song
Listing 2-1 shows that you'll be using a package to hold your domain classes. Groovy follows exactly the same packaging rules as Java, and as with Java, it is good practice to use packages. You might not see the benefit of packages in the beginning, but as your application grows and you begin taking advantage of Grails plugins and integrating more Java code, you will appreciate the organization that they provide (for more about plugins, see Chapter 13).
Once the command in Listing 2-1 completes, the result will be a new Song
domain class located in the grails-app/domain/com/g2one/gtunes
directory as dictated by the package prefix specified. Figure 2-1 shows the newly created structure and the Song.groovy
file containing the domain class definition.
Figure 2-1. The Song domain class and the Song.groovy file
Currently, the Song
domain isn't doing a great deal; it's simply a blank class definition as shown in Listing 2-2.
Listing 2-2. The Song Domain Class
package com.g2one.gtunes
class Song {
}
At this point, you should think about what aspects make up a "Song". Songs typically have a title, an artist, and a duration, among other things. If you really want to go overboard, you could model your Song
domain class on all the fields you can populate in an MP3 file's ID3 tag. But in this case, keep it simple: add only the three previously mentioned properties as shown in Listing 2-3.
Listing 2-3. Adding Properties to the Song Domain Class
package com.g2one.gtunes
class Song {
String title
String artist
Integer duration
}
That was simple enough, and the class doesn't look much different from your typical Groovy bean (see the Appendix for information about Groovy beans). GORM essentially maps the class name onto the table name and each property onto a separate column in the database, with their types relating to SQL types. Don't get too hung up on this now; we'll be digging more deeply into domain classes and GORM in Chapters 3 and 10. For the moment, let's move on to seeing the application in action.
Scaffolding comes in two flavors: dynamic (or runtime), and static (or template-driven). First we'll look at dynamic scaffolding, where a CRUD application's controller logic and views are generated at runtime. Dynamic scaffolding does not involve boilerplate code or templates; it uses advanced techniques such as reflection and Groovy's metaprogramming capabilities to achieve its goals. However, before you can dynamically scaffold your Song
class, you need a controller.
You had a brief introduction to creating controllers in Chapter 1, and the controller code necessary to enable scaffolding is minimal. Create the controller for the Song
class either manually or via the command line, as shown in Listing 2-4.
Listing 2-4. Creating the SongController
$ grails create-controller com.g2one.gtunes.Song
Again, you should use the package prefix with the grails create-controller
command, which will create the SongController
within the grails-app/controllers/com/g2one/gtunes
directory (see Figure 2-2).
Figure 2-2. Locating the SongController in the directory
To enable dynamic scaffolding, within the SongController
create a scaffold
property with the name of the target class as its value. In this case, it is the Song
class, as shown in Listing 2-5.
Listing 2-5. Enabling Dynamic Scaffolding
package com.g2one.gtunes
class SongController {
def scaffold = Song
}
Note Groovy automatically resolves class names, such as Song
in Listing 2-5, to the java.lang.Class
instance without requiring the .class
suffix. In other words Song
== Song.class
.
With that done, simply start up Grails with the grails run-app
command, open a browser, and navigate to the gTunes application at the usual link: http://localhost:8080/gTunes.
The Grails welcome page, first demonstrated in Chapter 1, will show the SongController
instance in the list of available controllers as well as the usual comforting welcome message. Click the SongController
link to pull up a page listing all the Song
objects (perhaps none, as the case may be), as depicted in Figure 2-3.
Figure 2-3. The Song List page
Without breaking a sweat, and in a grand total of three lines of code (excluding the package declaration), you have managed to create a useful CRUD interface that lets you create and fully manage the Song
instances within the gTunes application.
The magic doesn't end here. By clicking the "New Song" link at the top of the screen, you can create new songs. While generating the views, Grails does its best to guess what type of field is required to edit a property's value. For example, if Grails finds a String
, it will create a text field; if it finds a java.util.Date
, it will render drop-down boxes that allow you to select the date and time. Figure 2-4 shows an example of what the generated song-creation interface looks like.
Grails' built-in validation mechanism, called constraints, can also affect how the interface is rendered, including the order in which fields are displayed and the type of field that is rendered. Try clicking the "Create" button; you'll get a validation error stating that the duration must be specified, as pictured in Figure 2-5. The validation messages hook into Grails' internationalization support (often referred to with the abbreviation i18n). But for now, all you need to know is that Grails is pulling these messages from the properties files within the grails-app/i18n
directory. (We'll discuss constraints in Chapter 3 and internationalization in Chapter 7.)
Figure 2-4. The Create Song page
Figure 2-5. How Grails handles validation
You could customize the message at this point, but for now the defaults will do. Now let's try to create a song with some valid data. Specifically, try to enter these values into the provided fields:
Artist: Kings of Leon
Duration: 176000
Title: The Bucket
Now click the "Create" button and move on to the next section of the chapter.
Grails has obeyed instructions and duly created a new Song
instance with the necessary data in the database. You are then redirected to the "Show Song" screen where you can view and admire a rendered view of the Song
instance you just created.
Additionally, as pictured in Figure 2-6, the "Show Song" screen provides two buttons to let you edit or delete the Song
instance from the database.
Figure 2-6. The Show Song screen
Currently, you're dealing with a trivial domain model with only a single Song
domain class to account for. However, another attribute of domain classes is that they typically have relationships such as one-to-many, one-to-one, and so on. If you think about a Song for a moment, it is typically part of a collection of Songs within an album. Let's create an Album
domain class to model this using the grails create-domain-class
command as shown in Listing 2-6.
An Album
has attributes of its own, such as a title, but it also contains many songs. Listing 2-7 shows how to set up a one-to-many relationship between Album
and Song
using the hasMany
static property of domain classes. The hasMany
property is assigned a Groovy map where the key is the relationship name and the value is the class, in this case Song
, to which the association relates.
Listing 2-7. Defining a One-to-Many Relationship
package com.g2one.gtunes
class Album {
String title
static hasMany = [songs:Song]
}
The preceding association is unidirectional. In other words, only the Album
class knows about the association, while the Song
class remains blissfully unaware of it. To make the association bidirectional, modify the Song
class to include an Album
local property as shown in Listing 2-8. Now Album
and Song
have a bidirectional, one-to-many association.
Listing 2-8. Making the Relationship Bidirectional
package com.g2one.gtunes
class Song {
...
Album album
}
In Chapter 3, we'll delve into other kinds of relationships and how they map onto the underlying database. For now, create another scaffolded controller that can deal with the creation of Album
instances. Use the grails create-controller
command and add the def scaffold = Album
property to the class definition (see Listing 2-9).
Listing 2-9. Scaffolding the Album Class
package com.g2one.gtunes
class AlbumController {
def scaffold = Album
}
Now if you return to your browser and refresh the Song list, you'll notice that the Song you entered previously has mysteriously vanished. The reason for this is quite simple: Grails by default is running with an in-memory database, and updating domain classes creates a new instance of it. You might find this useful for testing, but you can configure a different database if you require a less volatile storage mechanism (we'll discuss that later in this chapter).
More significant, however, is the fact that on the welcome page we have an additional AlbumController
. Click the AlbumController
link, followed by the "New Album" button. Enter a title for the Album such as "Aha Shake Heartbreak" and click the "Create" button to see your newly created Album displayed (see Figure 2-7).
Figure 2-7. The Show Album screen
You'll also notice that the Album has a blank Songs field. Let's fix that next.
You can perform updates by clicking the "Edit" button. In this case, you want to add a Song
, so click the "Add Song" link to see the "Create Song" interface. This time, you'll get a useful drop-down box that lets you select which Album
the Song
should be part of (as shown in Figure 2-8). You'll notice that scaffolding's default behavior is simply to call toString()
on each element in the drop-down list. The default toString()
that Grails provides uses the class name and instance id
, which is not the most pleasant thing to present to a user. You can override this behavior by implementing your own toString()
method inside the Album
class.
Next, populate the fields as described in the "The Create Operation" section and click the "Create" button. You'll notice that the "Show Song" screen provides a link back to the Album
; clicking the link shows the Album
with the newly created Song
instance appearing in the list of songs (see Figure 2-9). Grails' scaffolding, although not exuding genius, is clever enough to figure out what a one-to-many relationship is and how to manage it accordingly.
Figure 2-8. The Create Song screen
Figure 2-9. Show Album screen with a list of songs
Finally, to complete the CRUD acronym, you can delete a particular Song
or Album
by clicking the "Delete" button. Grails is kind enough to inquire whether you are completely sure that you'd like to proceed with such a destructive operation.
This completes the tour of Grails' dynamic-scaffolding capabilities; in the next section you'll see how to get access to the underlying controller and view code that makes up these CRUD interfaces.
Dynamic scaffolding can serve a number of purposes, from creating administration interfaces to providing the basis of a real application. However, it often becomes useful to take customization to a new level, particularly in terms of views. Fortunately, Grails provides the ability to take a domain class and generate a controller and associated views from the command line through the following targets:
grails generate-views
: Generates views for the specified domain classgrails generate-controller
: Generates a controller for the specified domain classgrails generate-all
: Generates both a controller and associated viewsCalled "static" or "template-driven" scaffolding, this approach offers benefits beyond simple code generation. Notably, it provides an excellent learning tool to help you familiarize yourself with the Grails framework and how everything fits together.
You've already created a domain model that relates specifically to the problem you're attempting to solve. Now you can generate code that relates to your domain, increasing your chance of understanding the generated code. Let's start by looking at how to generate a controller.
To generate a controller that implements the CRUD functionality you saw in the section about dynamic scaffolding, you can take advantage of the grails generate-controller
command. Like the other generate
commands, generate-controller
takes a domain class name as its first argument. For example, Listing 2-10 shows how to use the generate-controller
command to output a new controller from the Album
class.
Listing 2-10. Outputting a New Controller
$ grails generate-controller com.g2one.gtunes.Album
Generating controller for domain class com.g2one.gtunes.Album ...
File /Developer/grails-dev/apps/gTunes/grails
app/controllers/com/g2one/gtunes/AlbumController.groovy already exists.
Overwrite?y,n,a
y
Finished generation for domain class com.g2one.gtunes.Album
Notice that, because the AlbumController
class already exists, the generate-controller
command will ask whether you want to overwrite the existing controller. Entering the value "y" for "yes" followed by hitting Enter will complete the process.
At this point, you should probably examine the contents of this mysterious controller to see how many thousands of code lines have been generated. If you're coming from a traditional Java web-development background, you might expect to implement a few different classes. For example, you would likely need a controller that calls a business interface, which in turn invokes a Data Access Object (DAO) that actually performs the CRUD operations.
Surely the DAO will contain mountains of ORM framework code, and maybe a few lines of Java Database Connectivity (JDBC) mixed in for good measure. Surprisingly (or not, depending on your perspective), the code is extremely concise at well under 100 lines. That's still not quite short enough to list in full here, but we will step through each action in the generated controller to understand what it is doing.
The index
action is the default, which is executed if no action is specified in the controller Uniform Resource Identifier (URI). It simply redirects to the list
action, passing any parameters along with it (see Listing 2-11).
Listing 2-11. The index Action
def index = {
redirect(action:list, params:params)
}
The list
action provides a list of all albums, as shown in Listing 2-12. It delegates to the static list
method of the Album
class to obtain a java.util.List
of Album
instances. It then places the list of Album
instances into a Groovy map literal (a java.util.LinkedHashMap
under the covers), which is then returned as the "model" from the controller to the view. (You'll begin to understand more about models and how they relate to views in Chapters 4 and 5.)
Listing 2-12. The list Action
def list = {
if(!params.max) params.max = 10
[ albumList: Album.list( params ) ]
}
But hold on a second: before we get ahead of ourselves, have you noticed that you haven't actually written a static list
method in the Album
class? At this point, you will start to see the power of GORM. GORM automatically provides a whole array of methods on every domain class you write through Groovy's metaprogramming capabilities, one of which is the list
method. By looking through this scaffolded code, you will get a preview of the capabilities GORM has to offer.
For example, the show
action, shown in Listing 2-13, takes the id
parameter from the params
object and passes it to the get
method of the Album
class. The get
method, automatically provided by GORM, allows the lookup of domain instances using their database identifiers. The result of the get
method is placed inside a model ready for display, as shown in Listing 2-13.
def show = {
def album = Album.get( params.id )
if(!album) {
flash.message = "Album not found with id ${params.id}"
redirect(action:list)
}
else { return [ album : album ] }
}
Notice how, in Listing 2-13, if the Album
instance does not exist the code places a message inside the flash
object, which is rendered in the view. The flash
object is a great temporary storage for messages (or message codes if you're using i18n); we'll discuss it in more detail in Chapter 4.
The action that handles deletion of albums is aptly named the delete
action. It retrieves an Album
for the specified id
parameter and, if it exists, deletes it and redirects it to the list
action (Listing 2-14).
Listing 2-14. The delete Action
def delete = {
def album = Album.get( params.id )
if(album) {
album.delete()
flash.message = "Album ${params.id} deleted"
redirect(action:list)
}
else {
flash.message = "Album not found with id ${params.id}"
redirect(action:list)
}
}
While similar to the show
action, which simply displays an Album
's property values, the edit
action delegates to an edit view, which will render fields to edit the Album
's properties (see Listing 2-15).
Listing 2-15. The edit Action
def edit = {
def album = Album.get( params.id )
if(!album) {
flash.message = "Album not found with id ${params.id}"
redirect(action:list)
}
else {
return [ album : album ]
}
}
You might be wondering at this point how Grails decides which view to display, given that the code for the edit
and show
actions are almost identical. The answer lies in the power of convention. Grails derives the appropriate view name from the controller and action names. In this case, since you have a controller called AlbumController
and an action called edit
, Grails will look for a view at the location grails-app/views/album/edit.gsp
with the album
directory inferred from the controller name and the edit.gsp
file taken from the action name. Simple, really.
For updating you have the update
action, which again makes use of the static get
method to obtain a reference to the Album
instance. The magical expression album.properties = params
automatically binds the request's parameters onto the properties of the Album
instance. You then save the Album
instance by calling the save()
method. If the save succeeds, an HTTP redirect is issued back to the user; otherwise, the edit view is rendered again. You can find the full code in Listing 2-16.
Listing 2-16. The update Action
def update = {
def album = Album.get( params.id )
if(album) {
album.properties = params
if(!album.hasErrors() && album.save()) {
flash.message = "Album ${params.id} updated"
redirect(action:show,id:album.id)
}
else {
render(view:'edit',model:[album:album])
}
}
else {
flash.message = "Album not found with id ${params.id}"
redirect(action:edit,id:params.id)
}
}
To facilitate the creation of new Albums, the create
action delegates to the create view. The create view, like the edit view, displays appropriate editing fields. Note how the create
action inserts a new Album
into the model to ensure that field values are populated from request parameters (Listing 2-17).
Listing 2-17. The create Action
def create = {
[album: new Album(params)]
}
Finally, the save
action will attempt to create a new Album
instance and save it to the database (see Listing 2-18).
Listing 2-18. The save Action
def save = {
def album = new Album(params)
if(!album.hasErrors() && album.save()) {
flash.message = "Album ${album.id} created"
redirect(action:show,id:album.id)
}
else {
render(view:'create',model:[album:album])
}
}
In both the save
and update
actions, you alternate between using the redirect
and render
methods. We'll cover these further in Chapter 4, but briefly: the redirect method issues an HTTP redirect that creates an entirely new request to a different action, while the render
method renders a selected view to the response of the current request.
Clearly, we've given only a brief overview of the various CRUD operations and what they do, without elaborating on a lot of the magic that is going on here. There is, however, method in our madness. The nitty-gritty details of controllers and how they work will surface in Chapter 4. For the moment, however, let's try out the newly generated controller by running the gTunes application once again via the grails run-app
target.
Once the server has loaded, navigate your browser to the AlbumController
at the address http://localhost:8080/gTunes/album. What happens? Well, not a great deal, actually. The result is a page-not-found (404) error because the generated controller is not using dynamic scaffolding. Dynamic scaffolding renders the views at runtime, but what you have here is just a plain old controller—there's nothing special about it, and there are no views.
Note We can of course set the scaffold
property to the Album
class, and the views will be generated with each action overridden.
It would be nice to have some views for your actions to delegate to. Fortunately, you can generate them with the grails generate-views
command, which is executed according to the same process described in the section "Generating a Controller" (see Listing 2-19).
Listing 2-19. Generating Views
$ grails generate-views com.g2one.gtunes.Album
...
Running script /Developer/grails-dev/grails/scripts/GenerateViews.groovy
...
Generating views for domain class com.g2one.gtunes.Album ...
Finished generation for domain class com.g2one.gtunes.Album
The resulting output from the command window will resemble Figure 2-10.
Figure 2-10. The generated scaffolding views
All in all, you can generate four views:
list.gsp
: Used by the list
action to display a list of Album
instancesshow.gsp
: Used by the show
action to display an individual Album
instanceedit.gsp
: Used by the edit
action to edit a Album
instance's propertiescreate.gsp
: Used by the create
action to create a new Album
instanceNote All the views use the main layout found at grails-app/views/layouts/main.gsp
. This includes the placement of title, logo, and any included style sheets. We'll discuss layouts in detail in Chapter 5.
You now have a controller and views to perform CRUD. So what have you achieved beyond what you saw in dynamic scaffolding? Well, nothing yet. The power of command-line scaffolding is that it gives you a starting point to build your application. Having started with nothing, you now have a controller in which you can place your own custom business logic. You have views, which you can customize to your heart's content. And you accomplished all this while writing minimal code. The developers we know are on a constant mission to write less code, and scaffolding proves a useful tool toward achieving this goal.
With the AlbumController
and associated views in place, delete the existing SongController
and repeat the steps in Listings 2-10 and 2-19 to generate a controller and views for the Song
domain class. You're going to need the generated code as you build on the basic CRUD functionality in later chapters.
In the meantime, let's move on to understanding more of what's necessary to kick-start your Grails development, beginning with environments.
Typically in any web-application production team, you have a development configuration for the application that might be configured to work with a locally installed database. This configuration sometimes even differs from developer to developer, depending on their specific desktop configurations.
In addition, QA staff who test the work produced by developers have separate machines configured in a similar way to the production environment. So we have two environments so far: the development configuration and the test configuration. The third is the production configuration, which you need when the system goes live.
This scenario is ubiquitous across pretty much every development project, with each development team spinning custom automated-build solutions via Ant or another custom-build system, instead of getting the solution from the framework itself.
Grails supports the concept of development, test, and production environments by default and will configure itself accordingly when executed. Some of this is done completely transparently to the developer. For example, autoreloading is enabled when Grails is configured in development mode but disabled when it's in production mode (to increase performance and minimize any security risk, however small).
Executing Grails under different environments is remarkably simple. For instance, the following command will run a Grails application with the production settings:
$ grails prod run-app
If you recall the output of the grails help
command, you will remember that the basic usage of the grails
command is as follows:
Usage (optionals marked with *):
grails [environment]* [target] [arguments]*
In other words, the first optional token after the grails
executable is the environment, and three built-in options ship with Grails:
Of course, Grails is not limited to just three environments. You can specify your own custom environment by passing in a system property called grails.env
to the grails
command. For example:
grails -Dgrails.env=myenvironment test-app
Here you execute the Grails test cases using an environment called myenvironment
. So all this environment switching is handy, but what does it mean in practical terms? For one thing, it allows you to configure different databases for different environments, as you'll see in the next section.
Armed with your newly acquired knowledge of environments and how to switch between them, you'll see the implications when you start configuring data sources. What initial configuration steps are required to get a Grails application up and running? None. That's right; you don't have to configure a thing.
Even configuring the data source is optional. If you don't configure it, Grails will start up with an in-memory HSQLDB database. This is highly advantageous to begin with, particularly in terms of testing, because you can start an application with a fresh set of data on each load.
However, since it is a pretty common requirement, we will delve into data sources because you'll certainly need to configure them; plus, they'll help you develop your knowledge of environments.
When you create a Grails application, Grails automatically provides a grails-app/conf/ DataSource.groovy
file that contains configuration for each environment (see Figure 2-11). You might find this convenient, because it means most of the work is done for you, but you might prefer to use another database such as MySQL rather than the provided HSQLDB database.
Figure 2-11. The DataSource.groovy file
Defining a data source is one area where the strength of the Java platform becomes apparent. Java's database connectivity technology, JDBC, is extremely mature, with drivers available for pretty much every database on the market. In fact, if a database provider does not deliver high-quality, stable JDBC drivers, its product is unlikely to be taken seriously in the marketplace.
A data-source definition is translated into a javax.sql.DataSource
instance that supplies JDBC Connection
objects. If you've used JDBC before, the process will be familiar, with the first step ensuring that the driver classes, normally packaged within a JAR archive, are available on the classpath.
The DataSource.groovy
file contains some common configuration setup at the top of the data-source definition, an example of which is presented in Listing 2-20.
Listing 2-20. Common Data-Source Configuration
dataSource {
pooled = true
driverClassName = "org.hsqldb.jdbcDriver"
username = "sa"
password = ""
}
The snippet indicates that by default you want a pooled data source using the HSQLDB driver with a username of "sa" and a blank password. You could apply defaults to several other settings. Here's a list of the settings that the DataSource.groovy
file provides:
driverClassName
: This is the class name of the JDBC driver.username
: This is the username used to establish a JDBC connection.password
: This is the password used to establish a JDBC connection.url
: This is the JDBC URL of the database.dbCreate
: This specifies whether to autogenerate the database from the domain model.pooled
: This specifies whether to use a pool of connections (it defaults to true).configClass
: This is the class that you use to configure Hibernate.logSql
: This setting enables SQL logging.dialect
: This is a string or class that represents the Hibernate dialect used to communicate with the database.Now we get to the interesting bit. Following the global dataSource
block, you'll see environment-specific settings for each known environment: development, test, and production. Listing 2-21 presents a shortened example of the environment-specific configuration.
Listing 2-21. Environment-Specific Data-Source Configuration
environments {
development {
dataSource {
dbCreate = "create-drop"
url = "jdbc:hsqldb:mem:devDB"
}
}
test {
...
}
production {
...
}
}
You'll notice that by default the development environment is configured to use an in-memory HSQLDB, with the URL of the database being jdbc:hsqldb:mem:devDB
. Also note the dbCreate
setting, which allows you to configure how the database is autocreated.
Note Hibernate users will be familiar with the possible values because dbCreate
relates directly to the hibernate.hbm2ddl.auto
property.
The dbCreate
setting of the development environment is configured as create-drop
, which drops the database schema and re-creates it every time the Grails server is restarted. This setting can prove useful for testing because you start off with a clean set of data each time. The available settings for the dbCreate
property are as follows:
create-drop
: Drops and re-creates the database schema on each application loadcreate
: Creates the database on application loadupdate
: Creates and/or attempts an update to existing tables on application load[blank]
: Does nothingThe production and test environments both use update
for dbCreate
so that existing tables are not dropped, but created or updated automatically. You might find it necessary in some production environments to create your database schema manually. Or maybe creating your database schema is your DBA's responsibility. If either is the case, simply remove the dbCreate
property altogether and Grails will do nothing, leaving this task in your or your colleague's hands.
Building on the knowledge you've gained in the previous section about configuring an alternative database, you're now going to learn how to set up MySQL with Grails. You're going to configure Grails to use MySQL within the production environment, and to achieve this you need to tell Grails how to communicate with MySQL. You're using JDBC, so this requires a suitable driver. You can download drivers from the MySQL web site at http://www.mysql.com.
In this book's examples, we'll be using version 5.1.6 of MySQL Connector/J. To configure the driver, drop the driver's JAR file into the lib
directory of the gTunes application, as shown in Figure 2-12.
Figure 2-12. Adding the driver's JAR file to the application's lib directory
With the driver in place, the next thing to do is configure the Grails DataSource
to use the settings defined by the driver's documentation. This is common practice with JDBC (and equivalent technologies on other platforms) and essentially requires the following information:
Currently the production DataSource
is configured to use an HSQLDB database that persists to a file. Listing 2-22 shows the production-database configuration.
Listing 2-22. The Production Data-Source Configuration
production {
dataSource {
dbCreate = "update"
url = "jdbc:hsqldb:file:prodDb;shutdown=true"
}
}
Notice that the remaining settings (username, password, driverClassName
, and so on) are inherited from the global configuration, as shown in Listing 2-20. To configure MySQL correctly, you need to override a few of those defaults as well as change the database URL. Listing 2-23 presents an example of a typical MySQL setup.
Listing 2-23. MySQL Data-Source Configuration
production {
dataSource {
dbCreate = "update"
url = "jdbc:mysql://localhost/gTunes"
driverClassName = "com.mysql.jdbc.Driver"
username = "root"
password = ""
}
}
This setup assumes a MySQL server is running on the local machine, which has been set up with a blank root
user password. Of course, a real production environment might have the database on a different machine and almost certainly with a more secure set of permissions. Also note that you must specify the name of the MySQL driver using the driverClassName
setting.
Another common way to set up a production data source in Grails is to use a container-provided Java Naming and Directory Interface (JNDI) data source. This kind of setup is typical in corporate environments where the configuration of a data source is not up to you, but to the deployment team or network administrators.
Configuring a JNDI data source in Grails couldn't be simpler; specifying the JNDI name is the only requirement. Listing 2-24 shows a typical JNDI setup.
Listing 2-24. JNDI Data-Source Configuration
production {
dataSource {
jndiName = "java:comp/env/jdbc/gTunesDB"
}
}
Of course, this assumes that the work has been done to configure the deployment environment to supply the JNDI data source correctly. Configuring JNDI resources is typically container-specific, and we recommend that you review the documentation supplied with your container (such as Apache Tomcat) for instructions.
Because Grails leverages Hibernate, it supports every database that Hibernate supports. And because Hibernate has become a de facto standard, it has been tried and tested against many different databases and versions.
As it stands, the core Hibernate team performs regular integration tests against the following database products:
In addition, although not included in the Hibernate QA team's testing processes, these database products come with community-led support:
A few, mostly older, database products that don't support JDBC metadata (which allows a database to expose information about itself) require you to specify the Hibernate dialect explicitly using the dialect
property of the data-source definition. You can find available dialects in the org.hibernate.dialect
package. You'll learn more about data-source definitions in future chapters, including Chapter 12. For now, since we have readied our application for the production environment, let's move on to the next step: deployment.
When you execute a Grails application using the run-app
command, Grails configures the application to be reloaded upon changes at runtime, allowing quick iterative development. This configuration does, however, affect your application's performance. The run-app
command is thus best suited for development only. For deployment onto a production system, you should use a packaged Web Application Archive (WAR) file. Doing this follows Java's mature deployment strategy and the separation of roles between developers and administrators.
As a significant added bonus, Grails' compliance with the WAR format means that IT production teams don't need to learn any new skills. The same application servers, hardware, profiling, and monitoring tools that you use with today's Java applications work with Grails, too.
If you are satisfied with the built-in Jetty container as a deployment environment, you can quickly deploy your application by setting up Grails on your production environment and then checking out your Grails application from the version-control system you have locally. Once you've done this, simply type:
grails run-war
This command packages up Grails as a WAR file and then runs Jetty using the packaged WAR on port 8080. If you wish to change the port, you can follow the instructions in the "Step 6: Running the Application" section of Chapter 1.
As for the Jetty configuration itself, modifying the GRAILS_HOME/conf/webdefault.xml
file can customize that.
The run-war
command is convenient, but you might want more control over your deployment environment. Or you might want to deploy onto another container, such as Apache Tomcat or BEA WebLogic, instead of Jetty.
What you need in these cases is a WAR file. The WAR file is the standardized mechanism for deployment in the Java world. Every Java EE–compliant web container supports the format. But some older containers might have quirks, so check out the http://grails.org/Deployment page on the wiki for helpful info on container-specific issues.
To create a WAR archive, use Grails' war
command:
$ grails war
By default, if no environment is specified, Grails assumes use of the production environment for a WAR file. However, as with other commands, you can change the environment if needed. For example:
$ grails test war
Once you've run the command, a brand-new WAR file appears in the root of your project directory (see Figure 2-13).
Figure 2-13. The gTunes WAR file
If the root directory is not a convenient location for the WAR file, you can always change it by specifying the target WAR location as the last argument to the war
command:
$ grails test war /path/to/deploy/gTunes.war
With the WAR file created, you just need to follow your container's deployment instructions (which might be as simple as dropping the file into a particular directory), and you're done. Notice how the WAR file includes a version number? Grails features built-in support for application versioning. You'll learn more about versioning and deployment in Chapter 12.
Wow, that was a lot of ground to cover. You generated a simple CRUD interface, configured a different data source, and produced a WAR file ready for deployment. You learned some of the basics about how controllers work in Grails and previewed what is to come with GORM, Grails' object-relational mapping layer.
You also played with Grails' support for running different environments and configured a MySQL database for production. All of this should have given you a solid grounding in the basics of working with Grails. However, so far we've only touched on concepts such as domain classes, controllers, and views without going into much detail. This is about to change as we plunge head first into the gory details of what makes Grails tick.
Starting with Chapter 3, we'll begin the in-depth tour of the concepts in Grails. As we do that, we'll begin to build out the gTunes application and transform it from the prototype it is now into a full-fledged, functional application.