Chapter 8. Using plugins: adding Web 2.0 in 60 minutes


In this chapter

  • The basics of Grails plugins
  • Adding graphs and charts
  • Integrating email support
  • Adding full-text search
  • Performing a UI makeover

Few things in life do exactly what you want, and unsurprisingly Grails is no different. Fortunately, there’s a wealth of Java tools and libraries out there that will help you implement almost any feature you could want. Because Grails is inherently a Java-based framework, you can use pretty much any Java library out there. Many of them are robust and mature, so why reinvent the wheel?

You can use Java libraries as is, but there can be big benefits to having an adapter between Grails and the library that makes it easier to use and quicker to set up. This is the purpose of the Grails plugin system. The idea is that functionality is bundled into modules that can be loaded by the framework and integrated into the system. That functionality may be full-text search, tag clouds, or a fancy new UI technology. In fact, many of the features of Grails are implemented as plugins themselves, including GORM.

You can see how the plugins relate to Grails and each other in figure 8.1. If you’re familiar with Eclipse or any of the other Java-centric IDEs, their plugin systems are analogous to Grails’ own.

Figure 8.1. The plugin architecture

The upshot of all this is that anyone can provide extra functionality by writing a plugin, and that’s what many people have done. You’ll see how easy it is to install these plugins into your applications, effectively giving them a shot of steroids. From section 8.2 onwards, we’ll explore some of the popular and cool plugins that are available.

8.1. Taking advantage of others’ hard work

When might you want to install a plugin? If you find yourself with a feature requirement that doesn’t sound specific to your application—one that’s not directly related to your business logic—look out for existing plugins that promise to do the job for you. If you would use a separate library or tool to do the job in a plain Java project, then a plugin is probably the best solution in Grails. Remember, it’s rarely a good idea to reinvent the wheel.

Take Hubbub, for example. We’ll want to send emails from various parts of the application, such as a user registration module or a daily email digest module, but emailing isn’t what Hubbub is about. The feature is outside the core of Hubbub and is common to lots of different types of applications. That makes it an ideal plugin candidate, and you won’t be surprised to learn that an email plugin already exists.

Once you’ve decided that there might be a plugin that does what you need, it’s time to find the appropriate one.

8.1.1. Finding plugins

There are two main sources of information about plugins: the Grails Plugin repository and the Grails website.

The Grails Plugin Repository

Grails has a couple of commands that can be used to query for plugin information, both of which work against what is known as the Grails Plugin repository. This is a centralized online storage area that hosts many of the available Grails plugins, making it easy for Grails to query for, install, and upload them. As you will see a bit later, you can even set up your own local repository.

For now, we’re interested in the querying capabilities:

grails list-plugins

This command will produce output similar to this:

Plug-ins available in the Grails repository are listed below:
-------------------------------------------------------------

acegi <0.5.1> -- Grails Spring Security 2.0 Plugin
aop <no releases> -- No description available
audit-logging <0.4> -- adds hibernate audit logging
...

As the list-plugins name suggests, this command will list all the plugins currently available in the repository. For each one, it will display the name, latest version, and a short description. You may notice that some plugins show both “<no releases>” and “No description available.” In many cases, this is a sign that the plugin was added to the repository before Grails 1.0, and it hasn’t been updated. Proceed with caution.


Under the hood

The Grails Plugin repository is implemented as a Subversion repository accessible via HTTP, so you’ll need to be able to access the internet from your computer. The structure of the directories and files in the repository follows conventions that allow Grails to download specific versions of a plugin.

If you need to configure an HTTP proxy, run this command:

grails set-proxy

Enter the details as requested. The list of plugins will be cached locally in a file called plugins-list.xml, which is typically located in $HOME/.grails/1.1/plugins.


The list of plugins is quite long. If you’re running on a Unix-like system, such as Mac OS X or Linux, we suggest that you pipe the output through grep, like so:

grails list-plugins | grep "mail"

This isn’t possible on Windows (unless you use Cygwin), but the plugins are listed alphabetically, so you shouldn’t have much trouble browsing through the list.

Now that we have the list, what’s next? We want to find out whether there’s a plugin that will make sending emails from Hubbub easy, so we look for anything to do with “mail.” It shouldn’t take you long to find an entry in the list for the “mail” plugin, which according to its description “provides mail support to a running Grails application.” That looks like the ticket.

Once you have the name of a plugin, you can find out more about it with this command:

grails plugin-info mail

You’ll receive results something like this:

--------------------------------------------------------------------------
Information about Grails plugin
--------------------------------------------------------------------------
Name: mail | Latest release: 0.5
--------------------------------------------------------------------------
Provides Mail support to a running Grails application
--------------------------------------------------------------------------
Author: Graeme Rocher
--------------------------------------------------------------------------
Author's e-mail: [email protected]
--------------------------------------------------------------------------
Find more info here: http://grails.org/Mail+Plugin
--------------------------------------------------------------------------
This plug-in provides a MailService class as well as configuring the necessary
beans within the Spring ApplicationContext.
...
--------------------------------------------------------------------------
Available full releases: 0.1 0.1-ALPHA 0.3 0.4 0.5 0.6-SNAPSHOT
...

The important information here is the location of online documentation (http://grails.org/Mail+Plugin, in this example) and the long description, which should provide you with enough information to decide whether the plugin is suitable or not.

Although the Grails Plugin repository makes life easy for the user, not all plugins are available through it. Those that aren’t can often be found via the Grails website.

The Grails Website

Along with plenty of other useful information for the discerning Grails developer, the main Grails website has a page that lists plugins by category. It also has documentation for many of them, which means that searching for plugins on the website can be fruitful. The downside to the website is that it relies on users and plugin authors to keep it up to date, so its information may be out of date, incorrect, or nonexistent for some plugins.

You can find the categorized list at http://grails.org/plugin/home. This section of the site is known as the Plugin Portal, where you can browse and search for plugins and their reference information. The site also supports RSS feeds for staying up to date with new and changed plugins. You can even vote for the plugins you think are the best.

What should you do if you can’t find what you’re looking for either via the Grails commands or the website? As a last resort, you can always ask on the Grails user mailing list (http://grails.org/Mailing+lists)—the community is friendly and responsive.

Once you’ve found a plugin you want to use, you can install it into your application.

8.1.2. Installing plugins

As with almost any piece of software, you have to install a plugin before you can use it. Fortunately, Grails has a simple command that will do this for you:

grails install-plugin <plugin name>

Provide the name of the plugin you want, and Grails will fetch the latest version from the Grails Plugin repository and install it locally. Use the name displayed by the list-plugins command—in our previous example, the name is “mail”.

When you install a plugin, Grails stores a local copy of the plugin’s zip archive. This means that when you install the plugin in another project, Grails can reuse this cached version rather than downloading the plugin from the repository again. When a new version of the plugin is released, the install-plugin command has to retrieve it from the repository. You can see an overview of the logic that Grails uses in figure 8.2.

Figure 8.2. Grails plugin installation logic

What if you want to use the same plugin version for all your projects? It can be time-consuming to keep upgrading your projects if you have more than a few, particularly if the new plugin version requires changes to your code. Or perhaps you have tested a particular version thoroughly, and upgrading represents an unnecessary risk. Whatever the reason, specifying an explicit version often makes sense. You can achieve this in a variety of ways, but the simplest is to pass the version to the install-plugin command, like this:

grails install-plugin mail 0.1

An alternative approach is to specify the location of the plugin package file, which conventionally has the name grails-<name>-<version>.zip, such as grails-mail-0.1.zip. The location could be a URL, like this:

grails install-plugin http://somewhere.org/plugins/grails-mail-0.1.zip

Or it could be a local file path if you’ve downloaded the package manually or someone has sent it to you:

grails install-plugin /home/ben/plugins/grails-mail-0.1.zip

The file path can either be absolute, as in the preceding example, or relative. Some plugins are exclusively distributed this way, in which case they won’t appear in the output of the list-plugins command.

In chapter 16, we’ll cover another approach that involves setting up your own plugin repository, but first we need to cover a few more details of plugin installation.


Where are my plugins?

You’ve installed a plugin or two, but where has Grails put them? Although you don’t need to know where they are, they can be a great source of instruction and inspiration when you decide to write your own plugins.

By default, Grails stores plugins in,

$HOME/.grails/<grailsVersion>/projects/<project>/plugins

where $HOME represents the current user’s home directory, <grailsVersion> is the version of Grails used to install the plugin, and <project> is the name of the project you installed it into.

Global plugins go into a slightly different place because they aren’t associated with a particular project:

$HOME/.grails/<grailsVersion>/global-plugins

In chapter 16 we show you how to control where Grails stores project plugins.


Projects Remember their Plugins

Once a plugin has been installed into a project, that project remembers the fact. If you’re the sole developer of the application, this doesn’t really matter, but imagine you’re in a team and someone else has added a plugin to the project and committed the changes to your version control system (VCS). Because the project remembers which plugins have been installed, Grails will automatically fetch and install any that are missing from your local copy. You don’t have to do anything. When you run the application, it will work, at least as far as the plugins are concerned.

Global Plugins

Some plugins are universally useful, and you’ll want to install them in all your projects. Consider the debug plugin, which enables you to track the information that flows through a Grails application. Rather than adding a feature required by an application, it aims to support your development process. That makes it an ideal candidate for all projects.

Because installing the same plugin into every project is pretty tedious, Grails allows you to install it globally by supplying the -global option to the install-plugin command:

grails install-plugin -global debug

As we explained, a project normally remembers what plugins have been installed. In the case of global plugins, though, no such link is maintained, so if you install a plugin globally and commit the project changes to your VCS, Grails won’t automatically download and install that plugin for your teammates.


Tip

When deciding whether to install a plugin globally, ask yourself this: do the projects require the plugin? If the answer is yes, you should not install it globally.


Plugin Dependencies

Many plugins are self-contained. This means that everything they require is either available through Grails or packaged in the plugin. Some are more complex and require features provided by other plugins—they depend on those other plugins, which might in turn depend on other plugins.

Figure 8.3 shows a (rather contrived) example of plugin dependencies for a Grails application. As you can see, managing the plugins and their dependencies yourself would be a lot of work. That’s why Grails manages them for you. When you install a plugin, it automatically checks what plugins it depends on and installs those that haven’t been installed already.

Figure 8.3. Example plugin dependencies for a project

Consider figure 8.3 and imagine you want to install plugin A into your project. When you run the install-plugin command, Grails will automatically fetch and install plugins D, E, F, and G because plugin A either directly or indirectly depends on them. Alternatively, if you already have plugin B installed, then Grails will only install plugins D, F, and G—plugin E is already installed because of B.

As a user of plugins, all of this is transparent to you. Even if you don’t quite understand what is going on at this stage, that’s OK. One thing to be aware of is that if you install a plugin from a zip archive rather than a plugin repository, Grails will still attempt to install its dependencies from the repository. This may not be a problem for you, but it’s useful to know, just in case.

Uninstalling Plugins

Once something is installed, it can be irritating if there isn’t an easy way to remove it. Grails provides the imaginatively named uninstall-plugin command to remove an existing plugin from your system. Pass it the name of the plugin, and it will do the rest:

grails uninstall-plugin mail

In addition, recall that projects remember what plugins are installed. This means that Grails will automatically remove a plugin if it’s installed for a project that doesn’t use it. Again, this makes life easier in team environments.

Applying Our Knowledge: The Hubbub Extreme Makeover Begins

Now that you have a feel for how plugins are installed, uninstalled, and otherwise managed, it’s time to apply all of that theory to a real project. We’re going to spend the rest of this chapter introducing you to several popular Grails plugins and giving Hubbub an extreme makeover, adding popular Web 2.0 user-requested features like graphs and charts, email integration, full-text search, and a host of other UI goodies.

Once you get a feel for how plugins can be used to quickly add features to your application, you’ll find yourself addicted to the near-instant gratification that this style of functionality reuse provides. Let’s get started with charting.

8.2. Adding charts and graphs

Creating charts and graphs in web apps has traditionally been a bit of a pain. Most libraries pregenerate or autogenerate images on the fly, which is time-consuming and somewhat inflexible. And when it comes to aesthetics, only a mother could love some of those charts. If you want great visuals, you have to use Flash, which has its own issues with performance and accessibility.

When Google Chart came out, the world of web-charting suddenly became a whole lot simpler and more elegant. The way Google Chart works is conceptually simple: you create an <IMG> tag in your page that links to a Google Chart URL. You add parameters to a URL to configure the type of chart to create, the data to display, and the size of the chart. Google generates the chart with the typical instant Google performance you’re used to, and the charts look slick.

8.2.1. Installing the Google Chart plugin

For Grails developers, the charting story is even better. The Google Chart plugin offers a set of simple tags that make adding charts to your pages easy. Let’s install it and create some charts:

grails install-plugin google-chart 0.4.8

We’re specifying a particular version here, but you can leave the version out to get the latest one. The Google Chart plugin ships with a sample page that demonstrates the available charts. If you want to explore what you can do, run your application and open http://localhost:8080/hubbub/plugins/google-chart-0.4.8/. Note that you have to change the version number to match the version of the Google Chart plugin you installed.

Let’s take a tour of the basic charting tags and learn how to use them.

8.2.2. Creating your first chart

For our first chart, we’ll add some basic statistics tracking to Hubbub. We’ll create a stats page for each user that shows some details about their posting frequency.

As a first step, we’ll create a URL mapping so we have a friendly way to get to our user stats page:

"/users/$userId/stats" {
controller = "user"
action = "stats"
}

With our stats permalink in place, let’s add a stats action to our UserController. We’ll start with pie and bar charts showing which days of the week the user most frequently posts messages on.

In our action, we need to generate a map that contains the day name (Mon, Tues, and so on) and the number of posts on that day. This is demonstrated in listing 8.1.

Listing 8.1. Adding a stats action to the UserController

The only unusual thing here is the use of SimpleDataFormat to format the date of the post. It’s a sneaky way of passing in a date and converting it to a day name. We then key a map from the day name to a count of posts on that day. We could do this calculation more efficiently with a Hibernate projection (which we covered in chapter 4, section 4.3.3), but let’s go for a simple programmatic solution for now.

With our map set up, we next need to render the chart in our GSP. The Google Chart plugin offers a taglib with convenience tags for all the common chart types. Let’s start with the pie chart, as shown in listing 8.2.

Listing 8.2. Generating a 3-D pie chart
<g:pieChart type="3d"
title='Posts Per Day'
size="${[600,200]}"
labels="${postsOnDay.keySet()}"
dataType='text'
data='${postsOnDay.values().asList()}' />

We use the <g:pieChart> tag, and supply it with our titles, labels, and data. As you might expect, the labels attribute is a list of strings, and the data attribute is a list of numbers. Google Chart handles the rest. That dataType field is important, and we’ll get to it shortly, but first let’s take a look at the output in figure 8.4.

Figure 8.4. Our 3-D pie chart in action

That’s a pretty fine-looking chart for a one-liner! The colors are defaulted here, but you can customize them as you like—we’ll explore this shortly. Before we do, though, it’s time to explain how those data types work.

8.2.3. What’s the story with dataType?

Google Chart supports three types of dataType: text, simple, and extended. The one you use depends on the type and size of data you need to chart. Depending on the value you set for dataType, the Google Chart plugin will normalize your data and encode the result to send to Google.

Table 8.1 shows the available data types.

Table 8.1. Data types supported by Google Chart

Data type

Discrete values

Cost/benefit

text

100

This gives you an easy-to-debug URL, but a long one. You can see each data value as plain text.

simple

63

This is perfect for most small charts, and it gives the smallest URLs. Data values are compressed using a special encoder: 1=A, 2=B, and so on.

extended

4096

This is only useful for large charts that have a fine-grained scale. It uses a similar encoder to the simple type: 0=AA, 1=AB, 2=AC, and so on.

The bottom line is that the simple dataType is often the best option for data sets of more than a few elements, because the text dataType can result in a verbose URL.

8.2.4. Bar charts: setting custom colors and gridlines

With our pie chart done and a basic knowledge of data types under our belt, let’s look at how we might implement the same data in a bar chart. Like our pie chart, it all starts with a custom charting tag, as shown in listing 8.3.

Listing 8.3. Our Google-generated bar chart
<g:barChart type="bvs"
title="Posts Per Date (bar)"
size="${[200,200]}"
colors="${['00FF00']}"
fill="${'bg,s,efefef'}"
axes="x,y"
axesLabels="${
[0:postsOnDay.keySet(),
1:[0,postsOnDay.values().max()/2,
postsOnDay.values().max()]
]
}"
dataType="simple"
data="${postsOnDay.values().asList()}" />

Things are getting a little more complex now, but we’re starting to take advantage of some of the more advanced features of the library. First, let’s check out the results, shown in figure 8.5.

Figure 8.5. Our Google bar chart in action

Most of the new attributes we added are necessary because we’re working with a chart that has axes. We need to specify the axis labels, which have a format of [ bottom, middle, top ] label. The values for the labels are generated algorithmically to ensure that they work for charts with any data value. In our example, we only ever have two posts for a given day, so the calculated labels for min, middle, and max resolve to 0, 1, and 2 respectively.

Axis labels also have their own format that can be confusing. The labels are expressed as 0: (list of labels), 1: (list of labels), with 0 being the X-axis and 1 being the Y-axis.

Setting the type to bvs tells Google Chart we want a “Barchart Vertical Stacked” style. You can use this field to select horizontal or vertical bars, and you can use stacking or grouping for multiple data sets. Check out the Google Chart Developer’s Guide (http://code.google.com/apis/chart/) for the available types (there are dozens, depending on the type of chart you’re using).

We’ve also added a few customizations. The most obvious change is that we’re starting to customize the colors. We’ve added a light grey background fill color (fill="${'bg,s,efefef'}"), and we’ve changed the color of the bars to green rather than the default yellow (colors="${['00FF00']}"). You’ll notice that Google Chart uses hexadecimal-style colors, which you’ll be familiar with from CSS. The fill color is expressed as bg,s,efefef which translates as “background, solid fill, light gray.”

Google Chart is full of these magic codes—far too many to document here—so check out the online docs for the full set of options. You can customize almost any part of the chart’s appearance.

8.2.5. Line charts: handling multiple datasets and line styles

The third common type of chart is a line chart. Generating a line chart will also give us a chance to explore charting multiple data sets.

We only have one statistic at our disposal right now, so let’s fabricate a second one. We’ll copy our postPerDay values into a doubleData list that contains a doubled set of values. Listing 8.4 shows our updated stats.gsp file with the new lineChart calls.

Listing 8.4. Generating a multiseries line chart
<%
def doubleData = postsOnDay.values().collect { it * 2 }
def seriesData = [ postsOnDay.values().asList(), doubleData ]
%>

<g:lineChart type="lc"
title="Posts Per Date (line)"
size="${[600,200]}"
colors="${['FF0000','0000FF']}"
axes="x,y"
lineStyles="${[[2,2,2],[2,8,4]]}"
legend="${[ 'Original', 'Doubler' ]}"
gridLines="16.6,25"
axesLabels="${[0:postsOnDay.keySet(),
1:[0,doubleData.max()/2,
doubleData.max()]]}"
dataType="simple"
data="${ seriesData }" />

In this example, we’ve passed our data attribute as a list of lists, with each sublist being a separate data series. We’ve also customized the line styles and colors—notice that both calls take a list, with each value pertaining to a different data set. To sweeten the deal, we’ve added a legend for our data series and some gridlines, which make everything easier to read. Figure 8.6 shows our output.

Figure 8.6. A multiseries line chart

Other than passing a list of lists for the dataset, there’s nothing more that you need to be aware of when handling multiple datasets. You can pass in a list of colors and styles (one per series), and add your own legend by passing in a list of strings (again, one per series).

Gridlines can be a little confusing at first. The two values (16.5, 25) refer to the percentage points of the graph where gridlines appear. In our example, the X values appear in 16.5 percent intervals (that is, splitting the chart into seven pieces), and the Y values are in 25 percent intervals (splitting the chart into four). You probably want to calculate the X values dynamically if you have a variable number of categories.

The final thing you might want to customize is the line styles. The triplet values (2,2,2 and 2,8,4) represent line thickness, length on, and length off. Our first example is two pixels thick, and two pixels on, then two off, which gives us the nice dotted-line effect.

Space forbids us from covering every conceivable charting option, and Google offers a wealth of them, but these examples should get you started. You’ll have your first charts up and running in no time.

It’s time to consider what you can do when your users don’t have internet access.

8.2.6. Intranet charts: Google Chart without Google

Google Chart is great, but what if you’re deploying to your intranet and your users don’t have access to the internet? All your Google charts are unavailable, right? Wrong.

The good folks at JFreeChart (a nice, standalone Java-charting library) have created a servlet that mirrors the Google Chart API. It’s called Eastwood, and there’s even a Grails plugin to integrate the servlet with your Grails application:

grails install-plugin eastwood-chart

With the plugin in place, you tell Google Chart to use your local eastwood servlet rather than the remote Google Chart address. The Google Chart taglib exposes this via a static apiString property that you can set in BootStrap.groovy:

GoogleChartTagLib.apiString = "http://localhost:8080/hubbub/chart?"

If you’re sneaky, you could even make the switch between Google Chart and Eastwood conditional, depending on whether your app is running on an internal or external server.

Our exploration of charting is now complete. The next application feature we’ll add is mail support, and there’s a mail plugin waiting to be explored.

8.3. Adding mail support

If your application offers any sort of user sign-up capability, it won’t be long before you’ll need to look at implementing email support. Whether you need to send signup welcome emails, respond to forgotten-password messages, or support a daily digest, having email capability is now a pretty standard requirement. Grails offers the Mail plugin to make sending email simple. You can send mail from controllers or services, and even use complex GSP views to create fancy HTML emails.

Let’s install the plugin and get under way:

grails install-plugin mail

After you’ve installed the plugin, you need to add two lines of configuration to tell the plugin where to find your mail server and what the default From address should be. Let’s update our /grails-app/conf/Config.groovy file:

grails.mail.host="192.168.1.9"
grails.mail.default.from="[email protected]"

The default From address is optional. You can always specify your own From address each time you invoke the mail plugin, but it makes sense to set it here because there’s less maintenance later on if you need to change it.

If you have different mail servers for different environments, you can nest this value inside the development/test/production sections of Config.groovy so that you have environment-specific values.


Adding configuration properties for your own code

You might be wondering how the Mail plugin reads values from Config.groovy and how you can add your own property settings to your application.

Let’s imagine Hubbub had a setting for enabling a proxy server. In our Config.groovy file we’d add a value for the property:

hubbub.proxy.enabled = true

If you need access to settings from a controller, you can reference the implicit grailsApplication object to get your setting, with a line like this:

if (grailsApplication.config.hubbub.proxy.enabled) {
/* do proxy stuff */
}

But grailsApplication isn’t available inside services, so you need to do a bit more work. First, you need to import the underlying ConfigurationHolder object, then access the code statically like this:

import org.codehaus.groovy.grails.commons.ConfigurationHolder
class MyService {
def doStuff {
if (ConfigurationHolder.config.hubbub.proxy.enabled) {
/* do proxy stuff */
}
}
}


Tip

You might be worried whether the Mail plugin is up to supporting your mail server setup. Fear not, there are ample configuration options available for the plugin, including custom ports and SSL connections. Consult the Mail plugin page for the complete set of configuration options.


With our configuration all done, it’s time to send our first email.

8.3.1. Sending mail inline

The Mail plugin gives you two basic options for invoking the mail service:

  • Via a sendMail() method dynamically added to each controller
  • By calling the mailService.sendMail() method from within a service that has mailService injected

Tip

MailService is automatically injected into each controller, so there’s no need to def mailService.


Let’s start with the controller-based mechanism, because that gives us a few options that aren’t available in a service. For our first email, let’s send a “welcome aboard” signup message, as shown in listing 8.5.

Listing 8.5. Sending a welcome email

Using the inline version of Mail is a matter of passing in a closure with the appropriate values for to, subject, and body. You can also provide cc and bcc fields, and you can comma-delimit a list of addresses. In listing 8.5, we use a Groovy multiline string (""") so we can lay out the email inline with our controller code.

That’s fine for simple scenarios, but we don’t want layout logic embedded in a controller. It’s time to move all our layout logic into a GSP to make things more maintainable.

8.3.2. Using a view as your mail body

Having an embedded email layout in your controller classes means you can’t take advantage of the HTML editor in your IDE. It also makes it harder to get your graphic designer’s input into the process. Fortunately the Mail plugin lets you delegate your UI output to a view. Let’s take it for a spin.

First, let’s create a standard Grails view to host our content. We’ll include some CSS styling too, because it’s available, and set the content type to text/html so a rich HTML email is sent. Listing 8.6 shows our email template.

Listing 8.6. A template view for our email, with CSS styling

With our content in place, we need a way of wiring it up to our controller action. This is handled by some custom attributes on the sendMail tag. Listing 8.7 shows the updated action.

Listing 8.7. An updated welcome action that defers to the view for rendering
def welcomeEmail = {

if (params.email) {
sendMail {
to params.email
subject "Welcome to Hubbub!"
body(view: "welcomeEmail", model: [ email: params.email ])
}
flash.message = "Welcome aboard"
}
redirect(uri: "/")
}

We pass the view name and the model attributes via the body tag. We’re already inside the UserController, so we can use a relative view name for the corresponding GSP (minus the .gsp extension). If you’re sending email from a service or a Quartz job, you need to specify the full path to the view (in this example, it would be "/user/welcomeEmail") because Grails has no servlet request to work with.

Figure 8.7 shows our welcome email in action, sending our rich HTML email to the user’s inbox.

Figure 8.7. A new message arriving with suitable markup

That completes our tour of the Mail plugin. Next on the list is adding full-text search capability. Let’s look at the Searchable plugin.

8.4. Full-text search: rolling your own search

With users flocking to its social networking goodness every day, Hubbub post volumes are going to skyrocket. And users will want to search.

In the good old days, people implemented website search logic using SQL queries (such as post.content like '%grails%'), but that isn’t sufficient for today’s requirements. Using SQL like queries can be highly inefficient, and it’s increasingly complex as the number of searched fields grows. For most full-text searches, the user wants to search multiple fields, which effectively rules out SQL.

Fortunately, clever folks have implemented full-text indexing and search engines that handle indexing database contents and provide convenient ways to search. One of the most popular Java full-text solutions is Lucene, and its higher-level abstraction library Compass.

The Grails Searchable plugin wraps these full-text search libraries to give you a simple and transparent way to implement searching. Whenever you save a domain object (such as with post.save()), the plugin adds the object to the full-text index. When you delete or update an instance, the plugin alters the index accordingly. When you want to search, you just call Post.search("your search terms") and you have yourself a list of hits. There’s also a completely customizable domain-specific language (DSL) for specifying which domain class properties are indexed.

Let’s kick things off by installing the plugin:

grails install-plugin searchable

With the plugin installed, we’re ready to start configuring which objects we want to be searchable. That will require some thinking.

8.4.1. Making objects searchable

The first step in making use of the Searchable plugin is determining which objects to index. In the case of Hubbub, users will want to search on Post object content (to see posts matching a term), and also on the User object (does my friend “Joe Cool” have an account?).

The simplest way to add our Post and User objects to the searchable index is to add a searchable property, like this:

class User {

static searchable = true
...
}

class Post {

static searchable = true
...
}

When a domain class is marked searchable, the plugin indexes its primitive fields (strings, dates, numbers, and collections of those). Later on, we’ll customize which fields get indexed, but for now we’ll stick with the defaults.

That’s it—your basic search capability is implemented. If we now startup Hubbub, we can use the provided Searchable page to search our index. Open http://localhost:8080/hubbub/searchable and take it for a spin, as demonstrated in figure 8.8.

Figure 8.8. The default Searchable interface is pretty usable from the get-go.

Not too bad for five minutes effort, but there’s still some work to do. The data is being searched, but the output is not what we want. It found the user and post information relating to “glen”, but we need to skin the output with our Hubbub style, and it would be nice to format the link results to use our preferred permalink format (/user/profile/glen for profile info, and /users/glen for all the posts). Also, the user probably only wants to search for posts (Find a Post) or users (Find a Friend).

Let’s step outside the default search page and create a custom search page. We’ll create a custom search controller first, and then put some effort into the view:

grails create-controller com.grailsinaction.search

We’ll start our implementation by offering a search of all posts, because that’s the more common option. Listing 8.8 shows our first attempt at a custom search controller.

Listing 8.8. custom search controller

As we saw earlier, Searchable adds a dynamic search() method to each domain class. It has two parameters: the query string and a map of options. Typically the options map will contain values for maximum hits per page, offset for pagination, and sorting order. For now, we’ll pass in the params object, and we’ll worry about passing in explicit options later.

The search() method returns a map containing metadata about the search, along with a list of domain classes matching the criteria. Table 8.2 gives a breakdown of what’s available.

Table 8.2. The searchResult return value gives you a wealth of query information.

Field

Description

total

The total number of matching results in the index

results

A List of domain class instances matching the query

max

The maximum number of hits to return (typically used to paginate; defaults to 10)

offset

The number of entries to skip when returning the first hit of the result set—used for pagination

scores

A list of raw result confidence for each hit (a floating point value between 0.0 and 1.0)

Typically, in the view, you iterate over the results field, displaying each of the hit objects (and perhaps get fancy with a little keyword highlighting). You can also use the total and max values to display the diagnostics (“returned 10 of 326 hits,” for example).

With our controller ready to go, we just need to put together a small /views/search/search.gsp file to let the user enter some values and to display the results from the search. We’ll ignore pagination for now and get started with a bare-bones approach, as shown in listing 8.9.

Listing 8.9. A first custom search form

If there are results, we iterate over them and render them in divs. That lets us apply CSS styles to the results. Figure 8.9 shows our first customized search in action.

Figure 8.9. Our first custom search form in action

Those results look pretty nice, but there’s no keyword markup. It’s time to explore what Searchable offers us to help out.

8.4.2. Highlighting hit terms

If you’re displaying search results, the user will probably want the keyword hits highlighted. Searchable gives you the power to implement this, but it requires some work with closures. Listing 8.10 shows the updated controller code that highlights the hits.

Listing 8.10. An updated search controller with hit-term highlighting

The updated search code uses the withHighlighter closure , which takes a highlighter object (used to hold the word that was highlighted along with its surrounding text), an index counter (used to track the hit number), and the search result object itself.

We create a new highlights object on each search result if it doesn’t already exist , and we use it to hold the marked-up version of the search result (the version with the keyword highlighted).

For each result, we retrieve the fragment of the Post’s content field that matched the search , and we surround it with ellipses to show it’s an extract. The matched fragment will contain the word that was matched plus a few surrounding words for context. The matched word will be surrounded in <b> tags by the plugin.

Listing 8.11 shows our updated view code that will extract those matching phrases and render them in the browser.

Listing 8.11. Updated view code for handling hit terms
<g:each var="result" in="${searchResult.results}" status="hitNum">

<div class="searchPost">
<div class="searchFrom">
From
<g:link controller="users" action="${result.user.userId}">
${result.user.userId}
</g:link>
...
</div>
<div class="searchContent">
${searchResult.highlights[hitNum]}
</div>
</div>

</g:each>

This code uses a status attribute in the <g:each> tag. Then it accesses the hitnum.Hits marked up with <b> tag. Figure 8.10 shows the results of searching for “work”. With hit-term highlighting set up, the search is starting to look useful. But we don’t want our search page to display thousands of hits, so it’s time to implement pagination.

Figure 8.10. Hit-term highlighting in action

8.4.3. Implementing pagination

So far, we’ve only explored the first page of our results. We haven’t specified the max property for our searches, and the Searchable plugin has defaulted to returning the first ten. It’s time we gave users control over how many results are returned per page. The good news is that we can use the same pagination control you saw in chapter 6.

Let’s first add a combo box to let the user choose the number of hits to be displayed per page. If we call the field max, Searchable will pick it up for free, which means no changes to our controller code. Here’s the updated form:

<g:form>
<g:textField name="q" value="${params.q}"/>
<g:select name="max" from="${[1, 5, 10, 50]}"
value="${params.max ?: 10}" />
<g:submitButton name="search" value="Search"/>
</g:form>

Our backend controller is unchanged, but we’ll need to make some UI changes to the results section of our page to use the paginating aspects of the output (the total number of matching results, which page we’re on, and so on).

Handling the case when there’s only one page of results makes this trickier than it should be. Listing 8.12 shows the updated GSP.

Listing 8.12. Displaying marked up hit terms
Displaying hits
<b>${searchResult.offset+1}-
${searchResult.offset + searchResult.max}</b> of
<b>${searchResult.total}</b>:

<g:set var="totalPages"
value="${Math.ceil(searchResult.total / searchResult.max)}"/>
<g:if test="${totalPages == 1}">
<span class="currentStep">1</span>
</g:if>
<g:else>
<g:paginate controller="search" action="search"
params="[q: params.q]"
total="${searchResult.total}"
prev="&lt; previous" next="next &gt;"/>
</g:else>
<p/>

If there’s only one page to display, we don’t need to invoke the paginate tag at all. But if we’re spanning pages, we need to tell the tag the total size of the result list. As you’ll recall from chapter 6, the tag manages its own state and looks after creating the necessary links to navigate to next and previous pages.

Figure 8.11 shows it in use.

Figure 8.11. Implementing pagination on our search

Our paginating search capability is done. With that feature implemented, we’ve learned enough of the basics to implement the most common features you’ll need in your own search facilities. But there’s still more Searchable power to explore: it’s time to look at some advanced features.

8.4.4. Customizing what gets indexed

So far in our exploration of search, we’ve relied on the default indexing rules: we’ve marked our domain classes as searchable. But when you index lots of data, it’s useful to control what gets stored—it’s pointless indexing a million password fields, because you never want them exposed in a search. You also want to be careful not to index data that’s under high concurrency (for example, a clickCounter field stored on every domain object) because you’ll open yourself up to a lot of complex locking and exception handling around concurrent updates to the index.

Fortunately Searchable exposes a more complete DSL for fine-grained index creation, so you can configure what is searchable. Let’s upgrade our User object to make sure passwords never get indexed:

class User {

static searchable = {
except = ['password']
}
// more stuff...
}

Two common operations on the Searchable DSL are except (index all fields except these) and only (only index these).


Customizing the index location

When you install the Searchable plugin, it adds a new command to install its configuration file. If you don’t install a custom version, it will use default values. Most of these defaults are fine, except that the index file location defaults to the current user’s home directory. This isn’t what you want in production.

To install a custom config file, use the following command:

grails install-searchable-config

This will create the /grails-app/conf/Searchable.groovy file, which lets you customize the location of your index files (and other options—see the comments in the created file). For example, you might have a special location for index files on your production servers:

compassConnection = new File(
"/var/indexes/${appName}/${grailsEnv}"
).absolutePath

You can override the locations for the development, test, and production environments like in Config.groovy.


8.4.5. Query suggestions: did you mean “Grails”?

One of the coolest features in Google is the suggest-query capability. Can’t remember how to spell zucchini and spell it zukini? Google will show you results, but it’ll also prompt you, “Did you mean zucchini?” Searchable gives you that feature for free, if you tell it which domain classes are subject to the check.

To enable this, you use the Searchable DSL we introduced in the previous section. Let’s change our Post domain class to mark it searchable for the suggest-query option, which uses the spellCheck option:

static searchable = {
spellCheck "include"
}

Once the domain class is marked to use the suggest feature, you pass in the suggest-Query option to your search code. Here’s our updated search action using the new parameter:

params.suggestQuery = true
def searchResult = Post.search(query, params)

Although the controller changes were fairly painless, we’re in for a bit more work in the view to display the suggested terms. We’ll use some internal Searchable classes to do this. Perhaps one day this will be wrapped up in a Searchable taglib, but for now here’s how to display the search term:

<g:if test="${searchResult?.suggestedQuery}">
<%@ page import="org.codehaus.groovy.grails.plugins.searchable.util.StringQueryUtils" %>
<p>Did you mean
<g:link controller="search" action="search"
params="[q: searchResult.suggestedQuery]">
${StringQueryUtils.highlightTermDiffs(
params.q.trim(), searchResult.suggestedQuery)
}
</g:link>?
</g:if>

We’ve done all the work, so it’s time to run our app and give it a test. Figure 8.12 shows a search for a popular competing web framework.

Figure 8.12. The Searchable suggest-query feature in action

Although suggestQuery makes for a great demo, there’s one more core feature of search that we haven’t explored yet. What if we want to constrain the query to only match items within the current user’s posts? To implement that, we need to explore how search subcomponents work.

8.4.6. Searching across relationships

We’ve already explored how the indexing process handles domain classes that are in relationships with one another. We discovered that if a domain class has a relationship to another (a User has many Posts) and the related domain class is also marked searchable, the index will also contain that relationship data. That’s good, because we can navigate from parent to child without concern.

But what if we want to search all Posts that belong to a particular user? We need some way of storing fields from the related User object in the index related to the Post data. Searchable handles that relationship with component.

We’ll show you the syntax first, and then break down what’s happening behind the scenes. Listing 8.13 shows our updated Post object’s searchable mapping.

Listing 8.13. An updated Post object with more custom search tuning

We’ve told Searchable that we want to store all of the fields of our User object with the index for each Post. When the index is stored in this manner, we can search against the Post object, using constraints from the User object (for example, to find all posts where userId is “glen”).

In order to implement this, let’s update the search UI to handle the new option:

<g:if test="${session.user}">
Just My Stuff:
<g:checkBox name="justMine" value="${params.justMine}"/>
</g:if>

We’ll also update our search controller to handle the check box. The simplest way to do this is to append a constraint to the submitted query. You can do this with Google-style restrictions, by appending +userId:glen at the end of the query, for example.

The following code updates our controller to automatically add the constraint when the check box for justMine is checked:

if (params.justMine) {
query += " +userId:${session.user.userId}"
}
def searchResult = Post.search(query, params)

We now have our constrained user search. Using strings to do this is fine for simple search restrictions, but it can open up all sorts of security issues when trying to restrict sensitive searches. There’s all kinds of evil the user could work on the submitted queryString, and you have to be careful to remove all the nasties by hand-crafted regular expressions.

For those scenarios, a better option is to use the full-blown Searchable Search-Builder DSL. You could, for example, rework the preceding search using must() specifiers, which the user can’t tinker with. For example, you could do something like this:

def searchResult = Post.search params, {
must(queryString(query))
must(term("userId", session.user.userId))
}

Using the SearchBuilder DSL offers a much “safer” approach for guaranteeing the constraint will apply to the final query. Check out the Searchable SearchBuilder DSL on the Grails wiki for more information (http://www.grails.org/Searchable+Plugin+-+Searching+-+Query+Builder). Be warned, though, the DSL can be tricky to use, so you might want to familiarize yourself with the Compass API that it’s built on first.


Debugging indexes with Luke

Luke (http://www.getopt.org/luke/) is a tool for viewing your index files to see exactly what’s being stored. If you’ve having trouble working out how all of the component settings affect index creation, download (or Web Start) Luke, and point it at your index directory.

The Documents tab is handy for debugging search DSL issues—it lets you step through each element in the index and see exactly which keywords are stored, as shown here:


The Searchable plugin is complex but powerful. Take the time to explore the online docs and see the configuration options available.

It’s now time to look at our last UI plugin: GrailsUI.

8.5. GrailsUI makeover

With our full-text search capability in place, it’s time to polish our UI. There are numerous Grails plugins for adding more UI sizzle to your app, but one of the most popular (and easy to use) is the GrailsUI plugin.

GrailsUI is a wrapper for YahooUI (YUI)—the poplar JavaScript UI toolkit from Yahoo!—but the plugin’s Grails integration is what makes it so attractive. It includes everything from tooltips to date pickers, rich-text editors, dialog boxes, data tables, and even Ajax autocomplete fields—all with fantastic Grails integration to make consuming the service straightforward. Our goals for Hubbub are modest: some tooltips, rich-text editing, a nicer date picker, and some fancy Ajax autocomplete fields.

To get started, we first need to install the plugin:

grails install-plugin grails-ui

With the plugin up and running, it’s time to start working on our new features.

8.5.1. Adding tooltips

Tooltips are the easiest UI feature to implement, so let’s start there. For our tooltips, we’d like the user to be able to hover over a friend’s icon and see the person’s name.

All components in the GrailsUI plugin follow the same implementation pattern:

  1. Declare which components you want to use in your page in the header section.
  2. Use a GrailsUI tag to create the component.
  3. For fine-grained customization, supply any special YahooUI tags that you want passed through to the rendered control.

We start by declaring that our /post/timeline.gsp page will use the hover tag. We’ll add the definition to the head section of the page using a special GrailsUI gui:resources tag:

<gui:resources components="['tooltip']"/>

With our resource declared, we can enhance our sidebar to use the hover element, which is invoked with <gui:tooltip text="your tooltip">. We have already developed a custom tag to render the thumbnail itself (h:tinyThumbnail), but let’s enhance things to add the user’s ID when the user hovers over their thumbnail:

<div id="friendsThumbnails">
<g:each var="followUser" in="${following}">
<gui:toolTip text="${followUser.userId}">
<h:tinyThumbnail userId="${followUser.userId}"/>
</gui:toolTip>
</g:each>
</div>

We’ve set the tooltip text to be the user’s ID, which should be enough to let the user know who they are following. Figure 8.13 shows the tooltip in action.

Figure 8.13. Applying a tooltip to our followers section

Our tooltip tag has given you a taste for the basics of how GrailsUI handles components. It’s time to set our sights a little higher, and look at how we might use our newfound knowledge to implement a rich-text editor for our registration page.

8.5.2. Implementing rich-text editing

The rich-text editor component of GrailsUI is used to edit HTML content fields, and it’s useful for adding basic CMS (content management system) functionality to your application.

In the case of Hubbub, we’d like to let the user add an HTML bio field on registration, and we’d like to give them the freedom to use whatever markup they’d like. Users aren’t particularly fond of hand-editing HTML, so let’s give them the full bells and whistles of an HTML editor to play with.

As with our tooltip integration, everything starts with a resource definition. Here’s the definition for including the rich-text editor into /user/register.gsp:

<gui:resources components="['richEditor']"/>

With the definition in place, using the editor is a simple matter of invoking the tag with any default value you’d like included. Typically the value comes from an existing form field. Listing 8.14 demonstrates our updated bio field editor.

Listing 8.14. Implementing a rich-text bio field editor

This will render a full editor for the user to interact with. All the rich-text support you’d expect is in there: bold, italic, underlines, fonts, and hyperlinks. The only values you must supply to the richEditor tag are id and value, but we’ve chosen to pass in the height and width as well. For more advanced attributes, you need to invoke some JavaScript, so we’ve also taken advantage of the customization options to supply a custom toolbar title.

We can now update our bios with some new info, as shown in figure 8.14. That’s as full-featured a text editor as you’re likely to need. YUI gives you a large number of configuration options (including the ability to remove and add your own buttons, modify the look and feel, and set all sorts of event options). We won’t get into the details here, but check out the extensive YUI documentation available online (http://developer.yahoo.com/yui/) to see all the available attributes.

Figure 8.14. Using a rich-text editor to enter bio data

With our rich-text editor working, let’s turn our attention to giving our dates a makeover.

8.5.3. Implementing calendar-style dates

The standard Grails date-picker is functional, but in terms of UI beauty and ease of use, let’s just say “it’s got character”. It will happily let the user specify date and time values, but they need to select from a range of combo boxes, and the user experience not very inviting. In most cases, when the user selects a date, they expect a calendar-style view that’s been made popular by hotel and flight-reservation sites.

GrailsUI ships with one of these, and it’s a one-line change to switch between the standard and GrailsUI versions of the date picker. Let’s use it to add a feature to Hubbub that allows users to schedule the publishing of a post—the post won’t be displayed until a certain date is reached.

First, we need to update our header definition in /user/post.gsp to include the new control. Notice that you can supply a list of control names to the tag if your page uses more than one component:

<gui:resources components="['tooltip','datePicker']"/>

With our definition in good order, the next step is to include the tag itself in the view:

Schedule Post:
<gui:datePicker id='postOn' value="${new Date()}" includeTime="true"/>

In this example, we’ve included the time portion of the date, so the user can select the exact moment the post is released. The control looks better without the time portion, but if you need the granularity for a straight replacement for a Grails date, it’s good to have that power.

One final trap is in store for us, though. YUI components can be picky about their styling. For certain components, you need to change the body style on your page to include the YUI skin definition class, and the YUI Calendar component that GrailsUI Calendar is built on is one of them.

We’ll update the body class on our Post template directly (/layouts/user/post.gsp) rather than the target GSP file (/views/user/post.gsp)—that will let us avoid any special SiteMesh ${pageProperty(name: "body.style")} coding to merge tag attributes between layout and view. Here’s the updated definition in the layout:

<body class="yui-skin-sam">

With the skin definition in place, the calendar is ready to use. You can see the rendered view in figure 8.15.

Figure 8.15. Using a calendar picker for date selection

The text box still needs some CSS styling, but the fly-out calendar looks great. With our new date control implemented, it’s time to move on to our last control—Ajax autocomplete.

8.5.4. Introducing autocomplete

We’re going to finish with one of the most complex controls in the GrailsUI suite: autocomplete. We’re going to enhance Hubbub to allow the user to select a tag from the UI by typing a few characters and being presented with a list of available tags to select from. Implementing autocomplete requires changes in both the frontend and backend because the autocomplete drop-down list needs to be populated from our backend data source.

Let’s start with the client changes first. We include autocomplete in our header definitions for /views/user/post.gsp:

<gui:resources components="['tooltip','datePicker','autoComplete']"/>

Once our resource definition is in place, we can safely reference the autocomplete tag in the view. There’s a lot of configuration that goes with autocomplete. Let’s look at the tag first:

Tags: <gui:autoComplete
id="tags"
resultName="tags"
labelField="name"
idField="id"
controller="post"
action="autoTags"
delimChar=" "
/>

That’s quite an invocation. As you might expect, controller and action point to the backend controller that returns the data to populate the drop-down list. The delim-Char parameter lets you separate several entries with the same control. In our case, we want to be able to tag things like “groovy grails news” and get separate drop-down lists for the tag name each time we press the space bar.

The remaining attributes, resultName, labelField, and idField, relate to the JSON data returned by the backend controller. In a moment we’ll see that our backend action will return JSON data that looks like this:

{"tags":[{"id":1,"name":"grails"},{"id":2,"name":"groovy"}]}

The resultName field refers to the name of the JSON root element. The idField and labelField refer to the elements within the JSON data that contain the values for the autocomplete field (id and name). In this example, we only care about the name field, but the idField is a requirement of the control because it creates hidden fields (in the preceding example, tags and tags_id) containing the label and IDs respectively.

The last step is to implement the backend logic that does the query based on the user’s keypresses. Take a look at the code in listing 8.15.

Listing 8.15. The backend implementation for autocompleting tags

The user’s keypresses will be in params.query , so we need to search the user’s tags to find any that match. We need case-insensitive searching, so we retrieve all the user’s tags and iterate through them to find the ones that begin with the keypresses .

Once we’ve got our target set of responses, we need to convert them into a format suitable for the autocomplete control. First, we create a list of id/name pairs for each tag , and then we return our result as a JSON object . Notice that the name of the root element of our jsonResult object (tags) is the element that matches the resultName attribute on our original view tag.

With all the plumbing done, there’s nothing more to do than sit back and enjoy our autocomplete control in action. You can see the results in figure 8.16.

Figure 8.16. Our autocompleting tags in action

With autocomplete in place, we’ve concluded our tour of popular Grails plugins and given Hubbub a complete makeover.

8.6. Summary and best practices

We’ve covered a lot of plugins in this chapter, but we’ve given you a thorough grounding in a host of popular features that you’re likely to want to add to your next application.

We introduced you to the basics of how plugins are installed and bundled, and we covered creating graphs and charts and integrating email into your application. We also explored numerous searchable options for making your app’s full-text search facility hum.

Finally, we gave Hubbub a complete makeover using the GrailsUI plugin, adding tooltips, a rich-text editor, a date picker, and an autocomplete capability. It was a lot to take in, but our app’s usability has increased tremendously.

Here are a few best-practices to take away from the chapter:

  • Keep it simple. Prefer the simple dataType when generating charts. The URLs are much shorter, which gives you space for a lot more data.
  • Use Mail config. Set the mail.grails.default.from attribute in Config.groovy so there’s a single place to maintain your From address. You then no longer need to use the From field when invoking mail services.
  • Customize search config. Always install a custom searchable config file so you can place your index data somewhere sensible.
  • Index selectively. Be careful which fields you include in your index. Watch out for sensitive data, as well as data that’s under high concurrency (if you index your clickCounter field, only bad can come of it).
  • Explore GrailsUI. The GrailsUI plugin has a wealth of custom components you can use. You know all the basics now, so adding new components to your library will be easy. Take the time to browse the GrailsUI documentation to see what UI features are available.

We’ve spent a lot of time focusing on the UI in this chapter, so it’s time to balance that frontend work with some backend work. In the next chapter, you’ll have a chance to develop your frontend and backend Grails skills as you handle wizard-style workflows by developing a basic online store for Hubbub products.

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

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