CHAPTER 13
Plugins

Up until now, you have been a consumer of the Grails plugin system at various points throughout the book. In Chapter 8, you used the Searchable plugin to add full-text search to your Grails application and explored how to use the Yahoo UI plugin as an alternative Ajax provider. And in the previous chapter, you had the chance to use both the Mail and Quartz plugins. Now it's time to turn the tables and become a plugin author. Plugins are, quite simply, the cornerstone of Grails. Grails itself is basically a plugin runtime with little knowledge beyond how to load and configure an installed set of plugins.

The Grails plugin system is very flexible—so much so that it would be quite reasonable to write an entire book on the subject. In this chapter, we aim to summarize the core concepts and demonstrate some common use cases for the plugin system. However, the full extent of what is achievable with the plugin system is left to your imagination.

Even if you don't plan to write a plugin to distribute to the world, we recommend you take the time to read this chapter. Grails plugins are not just a way to enhance the functionality of an existing Grails application; they are also an effective way to modularize your code. Later in this chapter, we will demonstrate how you can use plugins to split your Grails application into separate maintainable plugins that are composed together at runtime.

Plugin Basics

The core of Grails is a plugin runtime environment. However, to make it immediately useful, it ships with a default set of plugins that you have already been learning about, including GORM and Grails' MVC framework. Along with the default set of plugins, Grails ships with a set of commands to automatically discover and install new plugins. Let's take a look at these first.

Plugin Discovery

The Grails plugin community is a hive of activity and one of the most exciting areas of Grails. As of this writing, more than 80 plugins are available from the central repository. Providing a range of functionality from job scheduling to search to reporting engines, all the plugins are discoverable through the grails list-plugins command. To run the list-plugins command, simply type grails list-plugins in a command window, as shown in Listing 13-1.

Listing 13-1. Running the list-plugins Command

$ grails list-plugins

What this will do is go off to the Grails central repository and download the latest published plugin list. The list is then formatted and printed to the console. You can see some typical output from the list-plugins command in Listing 13-2, shortened for brevity.

Listing 13-2. Output from the list-plugins Command


Plug-in list out-of-date, retrieving..
      [get] Getting: http://plugins.grails.org/.plugin-meta/plugins-list.xml
.................................................................................

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

acegi              <0.3>             --  Grails Spring Security 2.0 Plugin
aop                <no releases>     --  No description available
audit-logging      <0.4>             --  adds hibernate audit logging and onChange
                                               event handlers to GORM domain classes
authentication     <1.0>             --  Simple, extensible authentication services
                                               with signup support
...

In the left column, you can see the name of the plugin, while in the middle is the latest released version of the plugin. Finally, on the right of the output, you can see the short description for any given plugin. If you want to obtain more information about a particular plugin, you can use the plugin-info command. Listing 13-3 shows how to obtain more information about the audit-logging plugin from Listing 13-2 using the plugin-info command.

Listing 13-3. Using the plugin-info Command to Get Detailed Plugin Information


$ grails plugin-info audit-logging
...
--------------------------------------------------------------------------
Information about Grails plugin
--------------------------------------------------------------------------
Name: audit-logging    | Latest release: 0.4
--------------------------------------------------------------------------
adds hibernate audit logging and onChange event handlers to GORM domain classes
--------------------------------------------------------------------------
Author: Shawn Hartsock
--------------------------------------------------------------------------
Find more info here: http://www.grails.org/Grails+Audit+Logging+Plugin
--------------------------------------------------------------------------
The Audit Logging plugin adds an instance hook to domain
objects that allows you to hang Audit events off of them.
The events include onSave, onUpdate, onChange, onDelete and
when called the event handlers have access to oldObj and newObj definitions that
will allow you to take action on what has changed.

--------------------------------------------------------------------------
Available full releases: 0.3 0.4 0.4-SNAPSHOT

As you can see with the plugin-info command, you get more information about the plugin including a long description, a link to the documentation (in this case http://www.grails.org/Grails+Audit+Logging+Plugin), who the author is, and all the past release version numbers.

Plugin Installation

This brings us nicely to the topic of plugin installation. To install the audit-logging plugin, you can use the install-plugin command as follows:

$ grails install-plugin audit-logging

However, if you require a specific version of the plugin, you can use one of the version numbers displayed in the Available full releases: field of Listing 13-3. Listing 13-4 demonstrates how to install version 0.3 of the audit-logging plugin.

Listing 13-4. Installing a Specific Version of a Plugin with the install-plugin Command

$ grails install-plugin audit-logging 0.3

After you install a Grails plugin, you can find out what plugins you already have installed by running the list-plugins command discussed in the previous section. You'll notice that, after the list of plugins available in the repository, the list-plugins command shows the plugins you currently have installed, as shown in Listing 13-5.

Listing 13-5. Finding Out Which Plugins You Have Installed with list-plugins


Plugins you currently have installed are listed below:
-------------------------------------------------------------

audit-logging       0.4              --  adds hibernate audit logging and onChange
                                              event handlers to GORM domain classes

If you have multiple Grails applications in development that share a common set of plugins, it may well be useful to install a plugin globally for all applications. To do this, you can pass the -global flag to the install-plugin command. For example, Listing 13-6 shows how to install the code-coverage plugin, which provides test coverage reports powered by Cobertura, for all applications.

Listing 13-6. Installing a Plugin Globally Using the -global Flag

$ grails install-plugin -global code-coverage

If you no longer need a particular plugin, then you can use the counterpart to the install-plugin command, which is called, unsurprisingly, uninstall-plugin. The uninstall-plugin command works exactly like the install-plugin command; it simply takes the name of the plugin to uninstall, as shown in Listing 13-7.

Listing 13-7. Uninstalling Plugins with the uninstall-plugin Command

$ grails uninstall-plugin audit-logging

Local Plugins

Of course, the plugins you create may not necessarily live in the central Grails repository. Grails plugins are packaged as simple zip files, and if you downloaded a plugin from elsewhere, you can install it by simply running the install-plugin command and passing in the location on disk of the plugin. Listing 13-8 shows how to install a plugin located in your home directory on a Unix system.

Listing 13-8. Installing a Local Plugin

$ grails install-plugin ~/grails-audit-logging-0.3.zip

To ease distribution within your team, instead of keeping your plugins locally on disk, you may decide to host your plugins on a local web server. In that case, the install-plugin command also supports plugin installation over HTTP. Listing 13-9 shows how to install the audit-logging plugin over HTTP, bypassing Grails' plugin autodiscovery mechanism.

Listing 13-9. Installing Plugins Over HTTP

$ grails install-plugin http://plugins.grails.org/grails-audit-logging/tags/figs/U002.jpg
LATEST_RELEASE/grails-audit-logging-0.4.zip

Now that you've learned the basics of plugin discovery and installation, let's move onto how you actually go about creating a plugin. We'll be demonstrating the basics of plugin creation and distribution. After that, we'll show you how to create some useful plugins to enhance and modularize the gTunes sample application.

Creating Plugins

Creating plugins in Grails is as simple as creating regular applications. All you need to do is run the grails create-plugin command and specify a name for your plugin. In fact, what you will soon discover is that a Grails plugin is a Grails application. To understand this, create a simple Grails plugin called simple-cache that can provide caching services to a Grails application. You do this using the create-plugin command, as shown in Listing 13-10.

Listing 13-10. Creating a Plugin with the create-plugin Command

$ grails create-plugin simple-cache

The result is what looks like a regular Grails application. You have all the typical resources that make up an application, including a grails-app directory. However, on closer inspection, you'll notice there is a file called SimpleCacheGrailsPlugin.groovy in the root of the project. This file contains a class that represents the plugin descriptor. Figure 13-1 shows the plugin descriptor residing snugly in the root of the project.

image

Figure 13-1. The simple-cache plugin descriptor

Providing Plugin Metadata

The plugin descriptor serves a number of purposes. The first and primary purpose is for the plugin author to provide metadata about the plugin such as the author name, version number, description, and so on. Listing 13-11 shows the SimpleCacheGrailsPlugin class and the placeholder fields used to supply this information.

Listing 13-11. The SimpleCacheGrailsPlugin Plugin Descriptor

class SimpleCacheGrailsPlugin {
    def version = 0.1
    def dependsOn = [:]
    // TODO Fill in these fields
    def author = "Your name"
    def authorEmail = ""
    def title = "Plugin summary/headline"
    def description = 'Brief description of the plugin.'
    ...
}

Properties such as author, title and so on, appear in the list-plugins and plugin-info commands when a plugin is published to a Grails plugin repository. The following list summarizes the available properties and what they represent:

  • author: The name of the plugin author
  • authorEmail: An e-mail contact address for the author
  • title: A short title for the plugin to appear in the right column of the list-plugins command (see Listing 13-2)
  • description: A longer, more detailed description that is displayed by the plugin-info command
  • documentation: A link to the location of the documentation for the plugin

All the properties in this list are optional; however, providing this information will help others understand the purpose of your plugin. Listing 13-12 shows the simple-cache plugin's metadata information.

Listing 13-12. The simple-cache Plugin Descriptor with Metadata Provided

class SimpleCacheGrailsPlugin {
    def version = 0.1
    def dependsOn = [:]

    def author = "Graeme Rocher"
    def authorEmail = "[email protected]"
    def title = "A simple caching plugin"
    def description = 'A plugin that provides simple caching services'
    ...
}

You may have noticed the dependsOn property, which is currently assigned an empty Map literal. This property allows you to specify which plugin or plugins this plugin depends on. As an example, say your plugin depends on the presence of GORM in a Grails application; you can specify this by using the plugin name and version number:

def dependsOn = [hibernate:"1.1"]

As well as specifying a simple version number, the dependsOn version syntax also allows version ranges, including wildcards. As an example, the following two dependsOn expressions are equally valid:

def dependsOn = [hibernate:"1.0 > 1.1"]
def dependsOn = [hibernate:"* > 1.1"]

The first example specifies that the plugin depends on any version of the hibernate plugin between versions 1.0 and 1.1, while the second expression says that the plugin supports any version of the hibernate plugin up to version 1.1, inclusive.

Once the dependsOn property is specified, when a user installs a plugin via the install-plugin command, Grails will automatically attempt to install any dependent plugins if they aren't already installed. This technique is often referred to as transitive dependencies resolution, and it is implemented by many build systems (such as Ivy, which is discussed in Chapter 12) for JAR dependencies.

Supplying Application Artefacts

One of the more obvious ways a plugin can enhance an existing application is by providing a new artefact, such as a controller, tag library, or service.


Note Throughout the source code and documentation of Grails, the word artefact is used to refer to a Groovy file that fulfills a certain concept (such as a controller, tag library, or domain class). It is spelled using the British English spelling of artefact as opposed to artifact, so we will be using that spelling throughout the book to maintain consistency with the APIs.


Because a Grails plugin is simply a Grails application, supplying an artefact is a simple matter of creating it just as you would in a regular application. For the simple-cache plugin, you're going to implement a service that provides application layer caching. To do so, simply use the create-service command from the root of the plugin:

$ grails create-service com.g2one.cache.Cache

Once completed, you'll end up with a new service at the location grails-app/services/ com/g2one/cache/CacheService.groovy. Because it's pretty simple to do, you'll also be implementing a little tag library to perform content-level caching. To create the tag library, run the create-tag-lib command:

$ grails create-tag-lib com.g2one.cache.Cache

Note that since a Grails plugin is simply a Grails application, you can run it just like a Grails application! Just use the grails run-app command, and you're on your way. This has significant benefits for the plugin developer in that plugin development is not very different from regular application development. You can run your plugin like a regular application, and you can also test your plugin like a regular application using the test-app command. You can even install other plugins into a plugin, something that is critical when developing a plugin that has dependencies on other plugins.

As for the CacheService and the CacheTagLib, we'll get to the implementation details of these later. For the moment, all you need to know is that, when you package up your plugin for distribution, it will provide two new artefacts: a tag library and a service.

Plugin Hooks

Let's return to the plugin descriptor. As well as providing metadata about the plugin, the descriptor also enables you to supply hooks into the plugin runtime. Each hook is defined as a closure property and allows the plugin to participate in the various phases of the plugin life cycle. The hooks are listed here in the order of their execution:

  • doWithWebDescriptor: This gets passed the XML for the web.xml file that has been parsed by Groovy's XmlSlurper into a GPathResult. See the "Modifying the Generated WAR Descriptor" section later in the chapter for more information on this one.
  • doWithSpring: This allows participation in the runtime configuration of Grails' underlying Spring ApplicationContext. See the "Providing Spring Beans" section for more information.
  • doWithDynamicMethods: Executed after the construction of the ApplicationContext, this is the hook that plugins should use to provide new behavior to Grails classes. See the "Using Metaprogramming to Enhance Behavior" section later in the chapter for more information.
  • doWithApplicationContext: This is executed after Grails' ApplicationContext has been constructed. The ApplicationContext instance is passed to this hook as the first argument.

By default, the simple-cache plugin you created earlier comes with empty implementations of all of these. If you don't plan to implement any of these hooks, you can simply delete them from the plugin descriptor. Listing 13-13 shows the various plugin hooks, just waiting to be implemented.


Note If you merely want to use plugins to provide application modularity, then you may want to skip to the "Packaging and Distributing a Grails Plugin" section because the following sections go into significant detail on how to hook into all aspects of the Grails plugin system.


Listing 13-13. Plugin Hooks in the simple-cache Plugin

class SimpleCacheGrailsPlugin {
    def version = 0.1
    ...
    def doWithWebDescriptor = { xml -> }
    def doWithSpring = {}

    def doWithDynamicMethods = { applicationContext -> }

    def doWithApplicationContext = { applicationContext -> }
}

Plugin Variables

A number of implicit variables are available within the context of these hooks that allow you to inspect the conventions within a running Grails application. The following are the available variables and associated descriptions:

  • application: An instance of the org.codehaus.groovy.grails.commons.GrailsApplication class that provides information about the loaded classes and the conventions within them
  • manager: An instance of the org.codehaus.groovy.grails.plugins.GrailsPluginManager class that allows you to find out what other Grails plugins are installed
  • plugin: A reference to the org.codehaus.groovy.grails.plugins.GrailsPlugin class, which allows you to find out various information about the plugin including its name, version, and dependencies

The GrailsApplication class is typically the most critical to understand if you plan to implement any hooks that work with the Grails conventions. Essentially, it defines a number of dynamic properties that map to each concept in a Grails application. For example, to obtain a list of the controller classes in a GrailsApplication, you can do this:

def controllerClasses = application.controllerClasses

Note that when we refer to classes, we're not talking about instances of the java.lang.Class interface but of the org.codehaus.groovy.grails.commons.GrailsClass interface that defines a number of methods to inspect the conventions within a GrailsApplication for a particular artefact type.

For example, given the CacheService you created earlier, Listing 13-14 demonstrates some of the methods of the GrailsClass interface and how they behave.

Listing 13-14. Using the Grails Convention APIs

GrailsClass serviceClass =
                 application.getServiceClass("com.g2one.cache.CacheService")

assert "CacheService" == serviceClass.shortName
assert "Cache" == serviceClass.name
assert "com.g2one.cache.CacheService" == serviceClass.fullName
assert "cacheService" == serviceClass.propertyName
assert "cache" == serviceClass.logicalPropertyName
assert "com.g2one.cache" == serviceClass.packageName
assert true == serviceClass.getPropertyValue("transactional")

You'll notice from Listing 13-14 the usage of the getServiceClass method to obtain the CacheService by name. The getServiceClass method is another dynamic method available on the GrailsApplication class. Essentially, for each artefact type, the GrailsApplication class provides dynamic methods to access the artefacts of that type, which are summarized here:

  • get*Classes: Obtain a list of all the GrailsClass instances for a particular artefact type, such as with getControllerClasses() or via property access such as controllerClasses.
  • get*Class(String name): Obtain a specific GrailsClass instance by name, as in getControllerClass("HelloController").
  • is*Class(Class theClass): Inquire if a given java.lang.Class is a particular artefact type, as in isControllerClass(myClass).

The asterisk in the previous method names can be substituted for the relevant artefact type you are interested in. Table 13-1 summarizes the different artefact types, as well as shows an example of the typical usage for each.

Table 13-1. Summary of Existing Artefact Types

Artefact Type Example
Bootstrap def bootstrapClasses = application.getBootstrapClasses()
Codec def codecClasses = application.getCodecClasses()
Controller def controllerClasses = application.getControllerClasses()
Domain def domainClasses = application.getDomainClasses()
Filters def filterClasses = application.getFiltersClasses()
Service def serviceClasses = application.getServiceClasses()
TagLib def tagLibClasses = application.getTagLibClasses()
UrlMappings def urlMappingClasses = application.getUrlMappingsClasses()

All of the artefact types in Table 13-1 cover existing artefacts, but Grails also allows you to add your own artefact types, which we'll look at in the next section.

Custom Artefact Types

Out of the box, Grails ships with a set of features, including controllers, domain classes, and so on. As you saw in the previous section, you can access all aspects of these via the GrailsApplication interface. However, what if you want to add a new artefact type? Take, for example, the existing Quartz plugin. As you discovered in Chapter 12, Quartz is a job-scheduling API that runs specified tasks on a scheduled basis. For example, you may want to run some code at 12 p.m. on the last Friday of every month. Quartz aims to solve these kinds of problems.

Now if you look at the existing artefact types, none of them models the idea of a job. So, how can you extend Grails and provide new knowledge to it about what a job is? Fortunately, you can find the answer in Grails' org.codehaus.groovy.grails.commons.ArtefactHandler interface. Listing 13-15 shows the key methods of the ArtefactHandler interface.

Listing 13-15. The ArtefactHandler Interface

public interface ArtefactHandler {
    String getType();
    boolean isArtefact(Class aClass);
    GrailsClass newArtefactClass(Class artefactClass);
}

The getType() method returns the type of the GrailsClass, which will be one of the values shown in the first column of Table 13-1. The isArtefact(Class) method is responsible for identifying whether a given class is of the current artefact type based on some convention. For example, does the class end with the convention Controller? If so, then it's a controller class.

The newArtefactClass(Class) method will create a new GrailsClass instance for the given java.lang.Class. The ArtefactHandler interface has other methods, but most of them are abstracted away from you because when implementing a custom ArtefactHandler, you'll typically extend the org.codehaus.groovy.grails.commons.ArtefactHandlerAdapter class. For example, take a look at Listing 13-16, which shows a possible implementation for the Quartz plugin.

Listing 13-16. An ArtefactHandler for the Quartz Plugin

1 class JobArtefactHandler extends ArtefactHandlerAdapter {
2
3    static final TYPE = "Job"
4
5    JobArtefactHandler() {
6        super(TYPE, GrailsClass, DefaultGrailsClass, TYPE)
7    }
8
9    boolean isArtefactClass(Class clazz) {
10        // class shouldn't be null and shoudd ends with Job suffix
11        if(!super.isArtefactClass(clazz)) return false
12        // and should have an execute method
13        return clazz.methods.find { it.name == 'execute' } != null
14    }
15  }

There are a few key things to look at in the JobArtefactHandler in Listing 13-16. First take a look at the constructor on lines 5 to 7:

5    JobArtefactHandler() {
6        super(TYPE, GrailsClass, DefaultGrailsClass, TYPE)
7    }

The constructor calls the super implementation, passing four arguments:

  • The artefact type: In this case, you're using a constant called TYPE that has the value Job.
  • The interface to use for the artefact type: You could extend the GrailsClass interface to provide a more specific interface such as GrailsJobClass.
  • The implementation of the interface for the artefact type: Grails provides a default implementation in the DefaultGrailsClass, but you could subclass this if you want to provide custom logic within the artefact type.
  • The suffix that the class name should end with for a java.lang.Class to be considered of the artefact type: The default implementation of the isArtefactClass method in ArtefactHandlerAdapter will perform a check on the passed java.lang.Class to ensure that the class name ends with the specified suffix. As you can see on line 11 of Listing 13-16, the logic from the superclass isArtefact method is being reused.

The next thing to note about the code in Listing 13-16 is the implementation of the isArtefactClass(Class) method, which checks that the class ends with the appropriate suffix by calling the superclass implementation of isArtefactClass(Class) and whether the class possesses an execute method. You can assert your expectations of the behavior of the JobArtefactHandler by writing a simple unit test, as shown in Listing 13-17.

Listing 13-17. Testing an ArtefactHandler

class JobArtefactHandlerTests extends GroovyTestCase {
    void testIsArtefact() {
        def handler = new JobArtefactHandler()
        assertTrue handler.isArtefactClass(TestJob)
        assertFalse handler.isArtefactClass(JobArtefactHandlerTests)

        GrailsClass jobClass = handler.newArtefactClass(TestJob)
        assertEquals "TestJob", jobClass.shortName
        assertEquals "Test", jobClass.name
        assertEquals "TestJob", jobClass.fullName
        assertEquals "testJob",jobClass.propertyName
        assertEquals "test",jobClass.logicalPropertyName
        assertEquals "", jobClass.packageName
    }
}
class TestJob {
    def execute() {}
}

At this point, there is one thing left to do. You have to tell your plugin about the ArtefactHandler. Say you were creating the Quartz plugin and you have a QuartzGrailsPlugin descriptor. If you add an artefacts property that contains a list of provided artefacts, the plugin will make Grails aware of the JobArtefactHandler:

def artefacts = [new JobArtefactHandler()]

So once the Quartz plugin is installed, if there is a class within the grails-app/jobs directory that looks like the one in Listing 13-18, the JobArtefactHandler will approve the class as being a "job."

Listing 13-18. An Example Job

class SimpleJob {
    def execute() {
        // code to be executed
    }
}

An added bonus of going through these steps is that suddenly the GrailsApplication object has become aware of the new artefact type you just added. With this hypothetical Quartz plugin installed, you can use all the dynamic methods on the GrailsApplication object first shown in Listing 13-14. Listing 13-19 demonstrates a few examples using the SimpleJob from Listing 13-18.

Listing 13-19. Using the GrailsApplication Object to Inspect Jobs

def jobClasses = application.getJobClasses()
GrailsClass simpleJobClass = application.getJobClass("SimpleJob")

assert application.isJobClass(SimpleJob)

The key thing to learn from this section is that Grails provides you with an extensible convention-based API. You are in no way restricted by the existing conventions and can easily start adding your own ideas to the mix. In the next section, we'll be looking at how the idea of Convention over Configuration (CoC) extends to the runtime configuration of Spring.

Providing Spring Beans

The doWithSpring hook allows you to specify new Spring beans to configure at runtime using Grails' BeanBuilder domain-specific language (DSL) for Spring. The intricacies of BeanBuilder will be described in far more detail in Chapter 16; however, we'll cover some of the basics here. Essentially, Grails is built completely on the Spring Framework. Grails has what is known as an ApplicationContext, which is essentially a container provided by Spring that holds one or more beans. By default, each bean is a singleton, meaning there is only one of them in the ApplicationContext.

As you learned in Chapter 11, Grails allows services to be autowired into controllers and tag libraries. This autowire feature is powered by the Spring container and is often referred to as dependency injection. It is an extremely powerful pattern that allows you to effectively separate out dependencies and the construction of those dependencies. That's the theory...now let's take a look at an example.

Earlier, you created a new service in the simple-cache plugin called CacheService. The CacheService is going to work in conjunction with a cache provider to provide application-layer caching to any user of the simple-cache plugin. Since it is a little pointless to reinvent the wheel and implement your own homegrown caching implementation, you're going to take advantage of the Ehcache library.

You may remember from Chapter 8 that you defined a bean in the grails-app/conf/ spring/resources.groovy file for the gTunes application that used the EhCacheFactoryBean class provided by Spring. You're going to use that again here, within the context of doWithSpring. Listing 13-20 shows how to define a globalCache bean.

Listing 13-20. Defining Beans in doWithSpring

class SimpleCacheGrailsPlugin {
    ...
    def doWithSpring = {
        globalCache(org.springframework.cache.ehcache.EhCacheFactoryBean) {
            timeToLive = 300
        }
    }
}

As a reminder, the name of the bean is the name of the method, which in this case is globalCache. The bean class is the first argument, while the closure passed as the last argument allows you to set property values on the bean. In this case, a globalCache bean is configured to expire entries every 5 minutes (300 seconds). We've really only touched the surface of what is possible with BeanBuilder here and in Chapter 8, so if you're keen to know more, you could skip forward to Chapter 16, which contains detailed coverage.

With that done, let's begin implementing the CacheService. First you need to get a reference to the globalCache bean defined by the plugin. To do this, simply add a property that matches the name of the bean to the CacheService, as shown in Listing 13-21.

Listing 13-21. Obtaining Beans Supplied by doWithSpring

import net.sf.ehcache.Ehcache
class CacheService {
    static transactional = false

    Ehcache globalCache
    ...
}

The globalCache property is in bold in Listing 13-15. Note that transactions have been disabled for the service by setting static transactional = false, since transactions won't be a requirement for this service.

Now let's implement the caching logic. When implementing caching, the pattern is typically that you look up an object from the cache, and if it doesn't exist, you execute some logic that obtains the data to be cached. Listing 13-22 shows some pseudocode for this pattern.

Listing 13-22. The Caching Pattern

def obj = cache.get("myentry")
if(!obj) {
    obj = ... // do some complex task to obtain obj
    cache.put("myentry", obj)
}
return obj

However, given that you have the power of closures at your disposal, it makes more sense to take advantage of them to come up with a more elegant solution. Listing 13-23 shows how to implement caching of entire logical blocks using closures.

Listing 13-23. Caching the Return Value of Blocks of Code Using Closures

1 import net.sf.ehcache.Ehcache
2 import net.sf.ehcache.Element
3
4 class CacheService {
5    ...
6   def cacheOrReturn(Serializable cacheKey, Closure callable) {
7        def entry = globalCache?.get(cacheKey)?.getValue()
8        if(!entry) {
9             entry = callable.call()
10            globalCache.put new Element(cacheKey, entry)
11       }
12       return entry
13    }
14 }

To understand what the code is doing in Listing 13-23, let's step through it line by line. First, on line 7 an entry is obtained from the globalCache bean, which is an instance of the net.sf.ehcache.Ehcache class:

7 def entry = globalCache?.get(cacheKey)?.getValue()

Notice how you can use Groovy's safe-dereference operator ?. to make sure that a NullPointerException is never thrown when accessing the value, even if the globalCache property is null! The get method of the globalCache instance returns a net.sf.ehcache. Element instance, which has a getValue() method you can call to obtain the cached value. Next on lines 8 and 9 the code checks that the returned value is null, and if it is, the passed closure is invoked, which returns the result that needs to be cached:

8      if(!entry) {
9          def entry = callable.call()

The return value of the call to the closure is used to place a new cache entry into the cache on line 10:

10 globalCache.put new Element(cacheKey, entry)

Finally, on line 12 the cache entry is returned regardless of whether it is the cached version:

12 return entry

With that done, let's see how to implement the CacheTagLib that can take advantage of the CacheService in Listing 13-24.

Listing 13-24. Adding Content-Level Caching

class CacheTagLib {
    static namespace = "cache"

    CacheService cacheService
    def text = { attrs, body ->
        def cacheKey = attrs.key
        out << cacheService.cacheOrReturn(cacheKey) {
            body()
        }
    }
}

Once again, Listing 13-24 shows how to use dependency injection to get hold of a reference to the CacheService in the CacheTagLib. The cacheOrReturn method is then used to cache the body of the tag using the key attribute passed into the text tag. Notice how the CacheTagLib has been placed inside a namespace, a concept you first learned about in Chapter 5.

Users of the simple-cache plugin can now take advantage of content-level caching simply by surrounding the body of markup code they want to cache with the <cache:text> tag that the CacheTagLib provides. Listing 13-25 shows an example of its usage.

Listing 13-25. Using the Tag Provided by the simple-cache Plugin

<cache:text key="myKey">
   This is an expensive body of text!
</cache:text>

Dynamic Spring Beans Using Conventions

In the previous section, you implemented the simple-cache plugin using an Ehcache bean registered in the Spring ApplicationContext. What this example didn't demonstrate, though, is the ability to dynamically create beans on the fly using the conventions in the project.

In the "Custom Artefact Types" section, you explored how to create a plugin that identified Quartz jobs. In a typical Spring application, you would need to use XML to configure each individual job using the org.springframework.scheduling.quartz.JobDetailBean class. With a Grails plugin that knows about conventions, you can do it dynamically at runtime! Listing 13-26 shows this in action in a QuartzGrailsPlugin plugin descriptor.

Listing 13-26. Dynamically Creating Beans at Runtime

1 import org.springframework.scheduling.quartz.*
2
3 class QuartzGrailsPlugin {
4    ...
5    def doWithSpring ={
6        application.jobClasses.each { GrailsClass job ->
7            "${job.propertyName}"(JobDetailBean) {
8                name = job.name
9                jobClass = job.getClazz()
10            }
11       }
12    ...
13  }
14 }

To better understand the code in Listing 13-26, let's step through it. First, on line 6 the each method is used to iterate over all the artefacts of type Job:

6 application.jobClasses.each { GrailsClass job ->

Then on line 7, a new bean is dynamically created using Groovy's ability to invoke methods using a String (or a GString) as the method name:

7 "${job.propertyName}"(JobDetailBean) {

In this case, given the SimpleJob from Listing 13-18, you would end up with a bean called simpleJob in the Spring ApplicationContext that is an instance of the Quartz JobDetail class. The JobDetailBean class is a Spring-provided helper class for creating Quartz JobDetail instances as Spring beans. Finally, on lines 8 and 9, the name of the job and the class of the job are set using properties of the GrailsClass interface:

8           name = job.name
9           jobClass = job.getClazz()

To finish up the Quartz plugin, you could set up beans within doWithSpring for the Scheduler, using Spring's SchedulerFactoryBean, the triggers, and so on. However, since this serves mainly as a demonstration of what is possible, we recommend you take a look at the excellent existing Quartz plugin for Grails, which is installable with the following command:

$ grails install-plugin quartz

Using Metaprogramming to Enhance Behavior

In the previous section, you saw how plugins can participate in the configuration of the Spring ApplicationContext. Now let's look at another area that plugins typically contribute to: the application behavior. Groovy is a fully dynamic language that allows you to completely modify the behavior of a class at runtime through its metaprogramming APIs.


Tip If you're looking for a book with significant coverage of the metaprogramming capabilities offered by Groovy, take a look at Programming Groovy by Venkat Subramaniam (Pragmatic Programmers, 2008).


Like other dynamic languages such as Smalltalk, Ruby, and Lisp, Groovy features a Meta Object Protocol (MOP). The key thing to remember is that it is the MOP that decides the behavior of Groovy code at runtime, so code that looks as though it may do one thing at compile time could be made to do something completely different. For each java.lang.Class that Groovy knows about, there is an associated MetaClass. The MetaClass is what dictates how a particular method, constructor, or property behaves at runtime.

Groovy's MetaClass allows you to add methods, properties, constructors, and static methods to any class. For example, consider the code in Listing 13-27.

Listing 13-27. Adding New Methods to a Class

class Dog {}
Dog.metaClass.bark = { "woof!" }
assert "woof!" == new Dog().bark()

Here you have a simple class called Dog. Instances of the Dog class cannot, as it stands, bark. However, by using the metaClass, you can create a bark method with this expression:

Dog.metaClass.bark = { "woof!" }

Clearly, this example has only brushed the surface of what is possible. If you refer to Appendix A, you'll find more detailed coverage of the metaprogramming APIs.

Let's look at an example within the context of a Grails plugin by trying to add the cacheOrReturn method to all controllers to eliminate the need to inject the service via Spring first. Listing 13-28 demonstrates how, by simply delegating to the CacheService, you can add a cacheOrReturn method to all controllers too.


Tip If you prefer not to create a plugin but would still like to do metaprogramming in your Grails application, we recommend you do so within a Bootstrap class, a topic covered in Chapter 12.


Listing 13-28. Adding Methods to All Controllers

class SimpleCacheGrailsPlugin {
   ...
   def doWithDynamicMethods = { applicationContext ->
       def cacheService = applicationContext.getBean("cacheService")
       application
              .controllerClasses
              *.metaClass
              *.cacheOrReturn = { Serializable cacheKey, Closure callable ->
              cacheService.cacheOrReturn(cacheKey, callable)
       }
   }
}

Another important aspect to notice about the code in Listing 13-28 is the use of Groovy's spread dot operator *. to obtain all the metaClass instances from all the controllerClasses and also the use of a spread assignment to create a cacheOrReturn method for each MetaClass. That's far easier than adding a for or each loop!

Plugin Events and Application Reloading

As well as the plugin hooks discussed in the "Plugin Hooks" section, plugins can also participate in a number of events, including application reload events. Grails aims to minimize the number of application restarts required during development time. However, since reloading is typically different for each artefact type, the responsibility to reload is delegated to plugins.

A plugin can essentially listen for three core events: onChange, onConfigChange, and onShutdown. Let's take a look at onChange first because it is the most common event dealt with by plugins. Each individual plugin can monitor a set of resources. These are defined by a property called watchedResources. For example, as part of Grails core, there is a plugin that provides support for internationalization (covered in Chapter 7) through the use of message bundles that are found in the grails-app/i18n directory. The i18n plugin defines its watchedResources property as follows:

def watchedResources = "file:./grails-app/i18n/*.properties"

What this says is that the i18n plugin will monitor all files within the grails-app/i18n directory ending with the file extension .properties.


Tip If you're wondering about the file-matching patterns the watchedResources property uses, take a look at Spring's org.springframework.core.io.support.PathMatchingResourcePatternResolver class as well as the Spring Core IO package in general, which Grails uses under the covers.


Whenever one of the properties files in the grails-app/i18n directory changes, Grails will automatically trigger the onChange event of the plugin or plugins, monitoring the file passing in a change event object. The event object is essentially just a map containing the following entries:

  • source: The source of the event, which is either a Spring org.springframework.core. io.Resource instance representing the file on disk or the recompiled and changed java.lang.Class instance if the watchResources property refers to Groovy classes
  • application: A reference to the GrailsApplication instance
  • manager: A reference to the GrailsPluginManager instance
  • ctx: A reference to the Spring ApplicationContext instance

Typically the most important entry in the event map is the source, which contains a reference to the source of the change. In the case of the i18n plugin, the source entry would reference a Spring org.springframework.core.io.Resource instance since the properties files monitored by the i18n plugin are not Groovy classes. However, if you develop a plugin where you choose to monitor Groovy classes instead, Grails will automatically recompile the changed class and place the altered class within the source entry in the event map.

As an example, consider the Quartz plugin discussed in previous sections. The watchedResources definition for the Quartz plugin would look something like this:

def watchedResources = "file:./grails-app/jobs/**/*Job.groovy"

Whenever one of the Groovy files changes, Grails will recompile the class and pass you a reference to the java.lang.Class instance representing the job. However, that is all Grails will do. It is then up to you to make whatever changes you deem necessary to the running application to ensure it is now in the correct state. For example, in the "Dynamic Spring Beans Using Conventions" section, we showed you how to dynamically register new JobDetail beans for each job class. To implement reloading correctly for the Quartz plugin, you would need to ensure that those beans are replaced with the new class. Listing 13-29 shows a hypothetical implementation that takes the newly recompiled class and registers new beans with the ApplicationContext.

Listing 13-29. Implementing onChange for the Quartz Plugin

1 class QuartzGrailsPlugin {
2    def watchedResources = "file:./grails-app/jobs/**/*Job.groovy"
3    ...
4
5    def onChange = { event ->
6        Class changedJob = event.source
7        GrailsClass newJobClass = application.addArtefact(changedJob)
8        def newBeans = beans {
9              "${newJobClass.propertyName}"(JobDetailBean) {
10                  name = newJobClass.name
11                  jobClass = newJobClass.getClazz()
12             }
13        }
14        newBeans.registerBeans(applicationContext)
15    }
16 }

Although the code is pretty short, there are quite a few new concepts to understand, so let's walk through those starting on line 6 where a reference to the event's source is obtained:

6 Class changedJob = event.source

With the source in hand, the next thing the onChange event does is register the new Class with the GrailsApplication instance by calling the addArtefact method:

7 GrailsClass newJobClass = application.addArtefact(changedJob)

The code on line 8 is pretty interesting, because here the implicit beans method is used, which takes a block of code that uses the BeanBuilder syntax we discussed in the "Providing Spring Beans" section. The beans method returns a BeanBuilder instance containing the bean definitions (but not the instantiated beans themselves):

8 def newBeans = beans {

The code on lines 8 to 13 are essentially the same as you saw in Listing 13-26; all the code is doing is creating a new JobDetailBean bean definition from the new class. Line 14 is far more interesting because it shows how you can use the registerBeans method of the BeanBuilder class to register all the bean definitions defined within the BeanBuilder instance with the provided ApplicationContext:

14 newBeans.registerBeans(applicationContext)

Of course, not all plugins will need to register new beans based on an onChange event. This is a requirement only if you registered beans in the doWithSpring closure that require reloading behavior. It may be possible to work with the existing beans to implement effective reloading for a plugin. For example, the i18n plugin we discussed earlier simply clears the MessageSource cache, forcing it to be rebuilt:

def messageSource = applicationContext.getBean("messageSource")
if (messageSource instanceof ReloadableResourceBundleMessageSource) {
    messageSource.clearCache()
}

Other than the onChange event, the two other events available are onConfigChange and onShutdown. The onConfigChange event is fired if Grails' global configuration file found at grails-app/conf/Config.groovy is changed by the user. In the case of the onConfigChange event handler, the source of the change event is the altered ConfigObject. Often, plugins rely on settings found within Config.groovy for configuration. Remember, Grails uses Convention over Configuration, which means that conventions are used to ease development, but configuration is still possible if required. Later in this chapter we'll show an example that uses the Grails ConfigObject, which is obtainable using the getConfig() method of the GrailsApplication class.

Finally, the onShutdown event is fired when the shutdown() method of the GrailsPluginManager is called. This happens, for example, when a Grails application is undeployed from a container and the Grails servlet's destroy() method is invoked.

Modifying the Generated WAR Descriptor

As discussed in Chapter 12, the web.xml file Grails uses to integrate with servlet containers is generated programmatically. You saw in Chapter 12 that it is possible to modify the template used to generate web.xml by using the install-templates command. However, it is also possible for plugins to modify web.xml programmatically using the doWithWebDescriptor hook.

Essentially, when the web.xml file is generated, it gets parsed into memory by Groovy's XmlSlurper parser. This parser creates an in-memory representation of the XML that you can modify. The doWithWebDescriptor hook is passed a reference to the XML as the first argument to the doWithWebDescriptor closure. XmlSlurper allows you to use a builder-like syntax to make modifications to the XML.

As an example, one of the core Grails plugins is the URL mappings plugin, which provides the functionality covered in Chapter 6. The way the plugin works is to provide a Servlet filter that rewrites requests onto the main Grails servlet. To add this Servlet filter into the mix, the doWithWebDescriptor implementation of the URL mappings plugin looks something like the code in Listing 13-30.

Listing 13-30. Example doWithWebDescriptor That Adds a New Servlet Filter

1 def doWithWebDescriptor = { webXml ->
2    def filters = webXml.filter
3    def lastFilter = filters[filters.size()-1]
4    lastFilter + {
5        filter {
6            'filter-name'('urlMapping')
7            'filter-class'(UrlMappingsFilter.getName())
8        }
9    }
10   ...
11 }

To understand what the code in Listing 13-30 is doing, let's take a look at it line by line. First, on line 2, a GPath expression is used to get a list of all the existing <filter> elements contained within the web.xml file:

2 def filters = webXml.filter

Then, on line 3, a reference to the last <filter> element in the list is obtained:

3 def lastFilter = filters[filters.size()-1]

As you can see from the previous two examples, using Groovy's XML APIs is nothing like using a Java XML parser. The XML object parsed by XmlSlurper almost feels like a first-class object, with very little evidence that the underlying data structure is in fact XML. Finally, on lines 4 through 9, the overridden + operator is used to add a new <filter> element directly after the last <filter> element:

4    lastFilter + {
5        filter {
6            'filter-name'('urlMapping')
7            'filter-class'(UrlMappingsFilter.getName())
8        }
9    }

Notice how in Groovy you can use strings for method names; for instance, you can choose an idiomatic XML element name like <filter-name> as the name of a method. The previous code will append the following equivalent XML snippet to the web.xml document:

<filter>
    <filter-name>urlMapping</filter-name>
    <filter-class>org.codehaus.groovy.grails.web.mapping.filter. figs/U002.jpg
       UrlMappingsFilter</filter-class>
</filter>

As you can see, Grails makes it pretty easy to participate in the generation of the web.xml file. Although not a common thing to do in a plugin, it is sometimes useful when you want to integrate legacy servlets, filters, and so on. As mentioned previously, you could have used the grails install-templates command and modified the web.xml template directly, but this technique allows you to create plugins that automatically do this configuration for you. Reducing configuration, as well as embracing simplicity, is very much the Grails way, and doWithWebDescriptor is just another example of that.

Packaging and Distributing a Grails Plugin

Once you are confident that your plugin is ready for distribution, you can package it using the grails package-plugin command. In the command window, simply type grails package-plugin from the root of your plugin project, as shown in Listing 13-31.

Listing 13-31. Packaging a Plugin


$ grails package-plugin
...
[zip] Building zip: /Developer/grails/simple-cache/grails-simple-cache-0.1.zip

As you can see from the output in Listing 13-31, the package-plugin command generates a zip file using the name and version number of your plugin. In this case, you're packaging up the simple-cache plugin you developed earlier. Figure 13-2 shows an example of the resulting zip file.

image

Figure 13-2. The simple-cache plugin's packaged zip file

Using the steps explained earlier in this chapter in the "Plugin Installation" section, you can now install the simple-cache plugin into other applications and make use of the tag library and services it provides.

If you want to distribute your plugin within the Grails central repository, you first need to obtain a plugin developer account for the Grails central Subversion (SVN) repository. You can find the steps to do so on the Grails web site at http://grails.org/Creating+Plugins.

Once you have obtained an account, releasing your plugin is as simple as typing the following command:

$ grails release-plugin

The release-plugin command will prompt you for the SVN username and password that you obtained when you set up a developer account. Grails does all the heavy lifting for you in making sure that the appropriate resources have been published in the repository and been tagged appropriately. The release-plugin command will also generate an updated plugin list so that your plugin appears whenever a Grails user types the list-plugins command.

Local Plugin Repositories

If you want to take advantage of Grails' plugin distribution and discovery mechanism on your own local network, then you can set up a local plugin repository. Grails' plugin repositories are currently backed by the SVN version control system, so all you need to do is set up an SVN repository on your local network, which you can do using the svnadmin command provided by SVN:

$ svnadmin create /path/to/repo

Once your SVN repository is created, you can configure additional repositories inside the grails-app/conf/BuildConfig.groovy file for each application or globally by creating a file in your USER_HOME directory at the location USER_HOME/.grails/settings.groovy. Either way, you can then provide additional named URLs of SVN repositories used for discovery and distribution. Listing 13-32 presents an example of configuring an additional plugin repository.

Listing 13-32. Configuring Additional Plugin Repositories

grails.plugin.repos.discovery.myRepository="http://foo.bar.com"
grails.plugin.repos.distrubtion.myRepository="https://foo.bar.com"

Notice in Listing 13-28 how Grails groups repositories under discovery and distribution. The URLs under discovery are used by the list-plugins, install-plugin, and plug-info commands discussed in the section on "Plugin Installation" to produce the plugin list that is presented to the user. The URLs under distribution are used by the release-plugin command, as discussed in the previous section.

By default, the release-plugin command will always try to publish to the Grails central repository. To tell the release-plugin command to publish to one of the repositories configured as in Listing 13-32, you need to add the name of the repository as an argument to the release-plugin command. For example:

$ grails release-plugin -repository=myRepository

And with that, we've reached the end of this tour of the plugin system. As you can imagine, you can take advantage of the plugin system in many different ways. In this section, we've touched on some ideas for plugins such as the simple-cache plugin and the Quartz plugin, but we think the plugin system is such a critical part of the Grails ecosystem that the lessons learned in this chapter should be put to further use. In the next section, you'll be applying what you've learned so far to create two new plugins for the gTunes application. Along the way, you'll discover how Grails' plugins can be used as both a way to extend the functionality of an existing application and as a way to effectively modularize your codebase.

Plugins in Action

So, you've learned what plugins are and the basics of creating plugins. It is now time to put that knowledge to work by developing a couple of plugins for the gTunes application. The first one you're going to create is a plugin that makes the album art service and tag library you developed in Chapter 8 into a reusable plugin. This is a perfect example of developing a plugin to add functionality and enhance behavior.

Plugins to Add Behavior

To start with, run the create-plugin command to create the basis of an album-art plugin:

$ grails create-plugin album-art

The next step is to move the AlbumArtService.groovy file and the AlbumArtTagLib.groovy file into the newly created plugin project. Once this is done, your plugin should be structured like Figure 13-3.

image

Figure 13-3. The structure of the album-art plugin

Of course, the AlbumArtService relies heavily on the Amazon web services library, so you should move those from the application into the plugin too. Figure 13-4 shows the lib directory with the necessary JAR files in place.

Also, don't forget to move the two tests that provide coverage for the AlbumArtService and AlbumArtTagLib from the application into the plugin. As mentioned previously, the great thing about plugins is that they can be developed and tested separately, which makes them useful for larger projects with multiple developers. With the AlbumArtServiceTests and AlbumArtTagLibTests test cases included in the album-art plugin, you can now immediately test whether your plugin is working by running the test-app command:

$ grails test-app

image

Figure 13-4. The album-art plugin's dependencies

With the tests passing, you can add the plugin metadata to the plugin descriptor that describes what this plugin is all about. Listing 13-33 shows the updated plugin descriptor with the metadata provided.

Listing 13-33. Providing Metadata to the album-art Plugin

class AlbumArtGrailsPlugin {

    def version = 0.1

    def author = "Graeme Rocher"
    def authorEmail = "[email protected]"
    def title = "Album art look-up plugin"
    def description = 'A plug-in that provides facilities to look-up album art'
    ...
}

One thing to consider is that when you developed the AlbumArtService in Chapter 8, it was designed to work in conjunction with an albumArtCache that used Ehcache provided by the application's grails-app/conf/spring/resources.groovy file. One solution to this would be to update the doWithSpring of the AlbumArtGrailsPlugin descriptor, as shown in Listing 13-34.

Listing 13-34. Providing the albumArtCache with doWithSpring

class AlbumArtGrailsPlugin {

    def version = 0.1
    ...
    def doWithSpring = {
        albumArtCache(org.springframework.cache.ehcache.EhCacheFactoryBean) {
            timeToLive = 300
        }
    }
}

However, since you previously developed a simple-cache plugin earlier in the chapter, it makes a lot more sense to take advantage of it. To do so, let's modify the dependsOn property on the album-art plugin descriptor, as shown in Listing 13-35.

Listing 13-35. Using dependsOn to Depend on the simple-cache Plugin

class AlbumArtGrailsPlugin {

    def dependsOn = [simpleCache:'0.1 > *']
    ...
}

Tip When specifying dependencies, you need to use bean conventions instead of the hyphen-separated, lowercase name simple-cache. The reason for this Grails design decision is that a hyphen isn't valid in a variable name or map key in Groovy unless you put quotes around it.


To enable the ability to continue to test the album-art plugin in isolation, you can install the simple-cache plugin into the album-art plugin using the install-plugin command from the root of the album-art plugin directory:

$ grails install-plugin /path/to/simple-cache/grails-simple-cache-0.1.zip

When you package the album-art plugin, Grails will not include the simple-cache plugin within the album-art zip. It is your responsibility to ensure that when you install the album-art plugin into the target application, you install the simple-cache plugin first. If you don't, you will get an error because Grails will be unable to resolve the album-art plugins' dependency on the simple-cache plugin, unless the simple-cache plugin is available in one of the configured repositories.

Moving on, you now need to update the album-art plugin to use the CacheService provided by the simple-cache plugin. Listing 13-36 shows the changes made to the AlbumArtService highlighted in bold.

Listing 13-36. Updating the AlbumArtService to Use the simple-cache Plugin

class AlbumArtService {
    ...
    def cacheService

    String getAlbumArt(String artist, String album) {
       ...
                def key = new AlbumArtKey(album:album, artist:artist)
                return cacheService.cacheOrReturn(key) {
                    try {
                        def request = new ItemSearchRequest()
                        ...

                        def response = client.itemSearch(request)

                        // get the URL to the amazon image (if one was returned)
                        return response.items[0].item[0].largeImage.URL
                    }
                    catch(Exception e) {
                        log.error "Problem calling Amazon: ${e message}", e
                        return DEFAULT_ALBUM_ART_IMAGE
                    }
               }
       ...
    }
}

The changes in Listing 13-36 will cause the tests for the AlbumArtService to fail with a NullPointerException because the cacheService is null within the context of the test. Instead of using a real implementation in the unit test, you can use duck typing to specify a mock implementation using Groovy's Map literal syntax, as shown in Listing 13-37.

Listing 13-37. Mocking the cacheService

albumArtService.cacheService = [cacheOrReturn:{key, callable-> callable() }]

Groovy allows maps, where the value of a given key is a closure, to act as if they are callable methods. In the example in Listing 13-37, by providing a cacheOrReturn key, you are able to mock the methods of the CacheService.

To spice things up even further, you're going to do a bit of metaprogramming, first by adding a getAlbumArt method to all controllers and second by allowing instances of the Album class from the gTunes application to retrieve their art simply by calling a getArt() method. The first case, in Listing 13-38, shows the necessary code, which just gets the AlbumArtService instance and adds a method to all controllers that delegates to the AlbumArtService.

Listing 13-38. Adding a getAlbumArt Method to All Controllers

class AlbumArtGrailsPlugin {
    ...
    def doWithDynamicMethods = { ctx ->
        def albumArtService = ctx.getBean("albumArtService")

        application.controllerClasses
                         *.metaClass
                         *.getAlbumArt = { String artist, String album ->
            return albumArtService.getAlbumArt(artist, album)
        }
    }
}

Adding a getArt() method to the Album class is a little trickier, because the plugin doesn't know anything about the Album class. So to implement this enhancement, you'll search the GrailsApplication instance for a domain class called Album and, if it exists, add the getArt() method to it. Listing 13-39 shows the modifications to the doWithDynamicMethods plugin hook.

Listing 13-39. Adding a getAlbumArt Method to All Controllers

class AlbumArtGrailsPlugin {
    ...
    def doWithDynamicMethods = { ctx ->
        ...
        def albumClass = application.domainClasses.find { it.shortName == 'Album' }
        if(albumClass) {
            albumClass.metaClass.getArt ={->
                     albumArtService.getAlbumArt( delegate.artist?.name,
                                                  delegate.title)
            }
        }
    }
}

Notice how within the body of the new getArt method you can use the closure delegate to obtain the artist and title. The delegate property of a closure, when used in this context, is equivalent to referring to this in a regular method. With the code in Listing 13-39 in place, you can now obtain the URL to an Album instance's album art with the code shown in Listing 13-40.

Listing 13-40. Using the getArt() Method to Obtain Album Art

def album = Album.get(10)
println "The art for this album is at ${album.art}"

Note that, in Groovy, methods that follow bean conventions are accessible via the property access notation, so the expression album.art is equivalent to album.getArt(). And with that, you have completed the album-art plugin that can now be installed into any application that has a requirement to look up album art. The gTunes application is one such application. However, before you can install the album-art plugin, you need to install the simple-cache plugin that the album-art plugin is dependent on into the gTunes application:

$ grails install-plugin ../simple-cache/grails-simple-cache-0.1.zip

With that done, install the album-art plugin next:

$ grails install-plugin ../simple-cache/grails-album-art-0.1.zip

Now you can start up the gTunes application, and it will behave exactly as before, except it is utilizing the album-art plugin's functionality instead! One thing to note about the album-art plugin is that although it provides new functionality in the form of services, tag libraries, and new methods, it does not comprise an entire self-contained application. We'll be looking at how you can achieve this in the next section.

Plugins for Application Modularity

As well as making it possible to extend the available APIs within a Grails application, plugins can also provide entire modules of application functionality. Many newcomers dismiss plugins as purely for plugin developers who are willing to jump into the core Grails APIs, but in fact, plugins are an extremely effective way to modularize your application. In this section, we'll explain how you can create an entire application as a plugin that can be installed into the gTunes application.

To keep things simple, you'll tackle a very commonly demonstrated application in screen-casts and presentations around Grails: the blog. Yes, as with any self-respecting modern Web 2.0 application, the gTunes application needs a blog where the proprietors of the gTunes store can make big announcements about new music, events, and so on. Luckily, a simple blog takes about five minutes to implement in Grails, so it shouldn't be too complicated.

The first step is to run the create-plugin command to create the blog plugin:

$ grails create-plugin blog

This will create the blog plugin and associated BlogGrailsPlugin descriptor. You can populate the descriptor with some plugin metadata; Listing 13-41 shows a sample blog plugin descriptor.

Listing 13-41. Adding Metadata to the blog Plugin

class BlogGrailsPlugin {
    def version = 0.1
    def author = "Graeme Rocher"
    def authorEmail = "[email protected]"
    def title = "A blogging plugin"
    def description = 'A plugin that provides a blog facility'
}

Now it's time to create a domain class that models a blog post:

$ grails create-domain-class com.g2one.blog.Post

After these two commands are complete, you should have a directory structure similar to that pictured in Figure 13-5.

image

Figure 13-5. The Post domain class

Thinking about the Post domain class for a moment, it's going to have the obvious things like a title and a body, as well as a date posted. Putting this into practice, Listing 13-42 shows the Post domain class containing the necessary properties.

Listing 13-42. The Post Domain Class

package com.g2one.blog

class Post {
    String title
    String body
    Date dateCreated
    Date lastUpdated

    static constraints = {
        title blank:false
        body type:"text", blank:false
    }
}

Note that the Post domain class is using the property names dateCreated and lastUpdated to take advantage of Grails' auto time stamping capabilities that were first discussed in Chapter 10. With an appropriate domain class in place, to help you get started, you can use scaffolding to quickly generate a controller and views for the Post domain class:

$ grails generate-all com.g2one.blog.Post

For this first revision of the blog plugin, you're going to support the creation of new entries only; hence, you can remove the generated edit, update, and delete actions. In addition, you need to show only the first five posts; therefore, you can use the max parameter to the static list method of the Post class to specify that. Listing 13-43 shows the full code for the PostController.

Listing 13-43. The PostController for the blog Plugin

package com.g2one.blog

class PostController {
     def index = { redirect(action:list,params:params) }
     def allowedMethods = [save:'POST']

     def list = {
         [ postList: Post.list( max:5) ]
     }

     def create = {
         [post: new Post(params) ]
     }

     def save = {

         def post = new Post(params)
         if(!post.hasErrors() && post.save()) {
             flash.message = "Post ${post.id} created"
             redirect(action:list)
         }
         else {
              render(view:'create',model:[post:post])
         }
    }
}

Now let's move onto the views. In the case of the blog plugin, the list.gsp view is the most important because it will be responsible for showing each blog entry. However, Grails' default scaffolding displays the list view as a table, which is not very useful in this case. You can correct that by modifying the list.gsp view to render a _post.gsp template instead. Listing 13-44 shows the updated list.gsp code.

Listing 13-44. The blog Plugin's list.gsp View

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <meta name="layout" content="${params.layout ?: 'main'}" />
        <title>Post List</title>
    </head>
    <body>

         <div class="nav">
                <span class="menuButton">
                       <g:link class="create" action="create">New Post</g:link>
                </span>
         </div>
         <div class="blog">
             <h1>${grailsApplication.config.blog.title ?: 'No Title'}</h1>

             <g:render plugin="blog"
                             template="post"
                             var="post"
                             collection="${postList?.reverse()}" />
        </div>
    </body>
</html>

There are a few key things to mention about the list.gsp view in Listing 13-44. First, note that when using the <g:render> tag to render a template in a plugin view, you must specify the plugin that this template belongs to; otherwise, Grails will attempt to resolve the template within the application it is installed into. Second, take note of the usage of the grailsApplication variable to specify the blog title:

<h1>${grailsApplication.config.blog.title ?: 'No Title'}</h1>

Here the implicit grailsApplication object is used to read a configuration setting from the grails-app/conf/Config.groovy file. If the setting called blog.title is specified in Config.groovy, then the view will use that. Hence, users of this plugin are able to configure the blog to their needs. An alternative approach to doing this would be to use the <g:message> tag, in which case the plugin user has to specify the message in the grails-app/i18n/ messages.properties file. The choice is up to you.

Finally, take note of the HTML <meta> tag that dictates what layout the list.gsp uses:

<meta name="layout" content="${params.layout ?: 'main'}" />

What this does is if there is a layout parameter within the params object, it will use that for the layout; otherwise, use the main layout. The main layout will, of course, resolve to grails-app/views/layouts/main.gsp, but why the decision to allow customization via a parameter? The idea here is that the user of the plugin can very easily customize the layout of the blog through URL mappings. For example, consider the URL mapping in Listing 13-45.

Listing 13-45. Using a URL Mapping to Customize the blog Plugin's Layout

"/blog"(controller:"post", action:"list") {
    layout = "funky"
}

If you add the URL mapping in Listing 13-45 to your grails-app/conf/UrlMappings.groovy file, users can go to the /blog URL and have the list action of the PostController execute, which in turn renders the list.gsp view. However, notice how a property called layout is set inside the body of the closure passed to the URL mapping definition. As you learned in Chapter 6, it is possible to pass parameters in this way. The result is that for the /blog mapping, a layout called grails-app/views/layouts/funky.gsp will be used instead! This is a pretty powerful pattern because it allows you to apply a different layout simply by applying a new URL mapping to the same controller and action.

As for the _post.gsp template used in the <g:render> method of Listing 13-44, it is pretty simple and just formats each Post instance appropriately. You can see the code for the _post.gsp template in Listing 13-46.

Listing 13-46. The _post.gsp Template

<div id="post${post.id}" class="blogPost">
    <h2>${post.title}</h2>
    <div class="body">
        ${post.body}
    </div>

    <div class="desc">
        Posted on <g:formatDate date="${post.dateCreated}"
                                format="dd MMMMMM yy" />
    </div>

</div>

And with that, you have pretty much completed the list.gsp view. Figure 13-6 shows what the list.gsp view looks like when you run the blog plugin and head off to the list action of the PostController.

image

Figure 13-6. The list view of the blog plugin

Since the view renders each Post directly in the list.gsp view, the show.gsp view has been made redundant and can be deleted. Also, for the first revision, you're interesting in creating new posts only, so edit.gsp can be deleted too—you can always add editing later!

Moving on to the create.gsp view, it too could use a little cleaning up. Also, it would be nice to provide a rich text–editing capability for authoring the post. One of the plugins available for Grails is the fckeditor plugin, which adds support for FCKeditor (http://www.fckeditor.net/), a rich text–editing component. To install the fckeditor plugin into the blog plugin, run the following command:

$ grails install-plugin fckeditor

In addition to this, you need to update the BlogGrailsPlugin descriptor and add a dependsOn setting to ensure that when others install the blog plugin, FCKeditor is resolved too. Listing 13-47 shows the dependsOn set appropriately.

Listing 13-47. Making the blog Plugin Depend on the fckeditor Plugin

class BlogGrailsPlugin {
    def dependsOn = [fckeditor:'0.8 > *']
    ...
}

With that done, let's enable FCKeditor in create-gsp by using the <fck:editor> tag provided by the fckeditor plugin. Listing 13-48 shows the updated create.gsp file with the usage of the <fck:editor> tag highlighted in bold. You will notice the logical name printed when you ran the blog plugin with grails run-app. Grails prints out a message such as this:


Loading with installed plug-ins: ["fckeditor", "blog"]

Listing 13-48. Using the fckeditor to Enable Rich Text Editing

<html>
    ...
    <body>
          <h1>Create Post</h1>
          <g:if test="${flash.message}">
          <div class="message">${flash.message}</div>
          </g:if>
          <g:hasErrors bean="${post}">
          <div class="errors">
              <g:renderErrors bean="${post}" as="list" />
          </div>
          </g:hasErrors>
          <g:form action="save" method="post" >
              <div class="dialog">
              <div id="titleField">
                        <label for="title">Title:</label>
                        <g:textField name="title"
                                   value="${fieldValue(bean:post,field:'title')}"/>
              </div>
              <div id="bodyField">
                  <fck:editor name="body"
                              width="500"
                              height="300"
                              toolbar="Basic">
                            ${fieldValue(bean:post,field:'body')}
                  </fck:editor>
              </div>
              </div>
              <div class="buttons">
                  <span class="button">
                        <input class="save" type="submit" value="Post" />
                  </span>
              </div>
          </g:form>
    </body>
</html>

Using the toolbar attribute of the <fck:editor> tag, you can specify that you want only a simple toolbar with basic formatting options; otherwise, you'll get a toolbar with almost as many options as a word processor like Microsoft Word. Figure 13-7 shows the create.gsp view with the <fck:editor> tag doing the job of rendering a rich text–editing component.

image

Figure 13-7. Creating a post with FCKeditor

Of course, both the list.gsp and create.gsp pages currently look rather uninspiring, but it is up to the application you install the blog plugin into to provide useful style information via CSS. Speaking of installing the blog plugin into an application, it is time to do exactly that! First package up the blog plugin by running the package-plugin command:

$ grails package-plugin

Then navigate to the gTunes application, and use install-plugin to install the blog plugin:

$ grails install-plugin ../blog/grails-blog-0.1.zip

Note how, in this case, since the FCKeditor plugin exists in the Grails central repository, the install-plugin command will automatically resolve the dependency. Now it would be useful to configure the blog's title using the grails-app/conf/Config.groovy file. Remember, the blog.title setting allows you to customize the blog title; simply adding the following setting to Config.groovy will do the trick:

// configuration for the blog
blog.title="The gTunes Weblog"

Run the gTunes application using the run-app command, and then navigate to the URL http://localhost:8080/gTunes/post/list. Like magic, you have the blog plugin running inside the gTunes application exactly as it was before—except that it is now taking advantage of the gTunes application's main layout. Clicking the "New Post" button will take you to the create.gsp view you developed earlier. Figure 13-8 shows the FCKeditor component running within the gTunes application.

image

Figure 13-8. Creating blog posts in the gTunes application

If you type some content, including a title and body, and then hit the "Post" button, you're able to create new posts on the gTunes application blog, as shown in Figure 13-9.

image

Figure 13-9. A blog post in the gTunes application

Clearly, this is a very basic blog plugin at the moment with no support for RSS, comments, calendars, archives, and all that jazz. However, as a demonstration of the concept of using plu-gins to separate your application in reusable modules, it's a perfect example. A separate team of developers could happily work on the blog plugin and gradually integrate its functionality into the primary application over time. You could even create an automated build, as you learned in Chapter 12, to build and test all your plugins and install them into your main application for integrating testing. So, plugins are definitely worth a look, even if you don't intend to become an expert on Grails internals.

Summary

In this chapter, we hope you have learned the power of the Grails plugin system not just for plugins that provide API enhancements but equally for use cases that provide fully functional application modules like you saw in the previous section. Plugin development is a very broad topic, and this chapter only brushed the surface of what is possible; however, this chapter has given you enough knowledge to investigate developing your own plugins.

From the basics of creating and populating plugin metadata to the intricacies of developing the plugin itself and finally to the packaging and distribution of your plugins, this chapter has covered a lot of ground. As you have seen, Grails provides a broad set of functionality out of the box that can be extended without limits through its plugin system.

One thing you will have noticed during the development of the blog plugin in the previous section is that at the moment it allows pretty much anyone to post. Clearly, this is not desirable in the long term, so in the next chapter, we'll cover how you can refactor the simple security implementation in the gTunes application into one of the more fully featured security plugins that are available. Role-based security, here we come!

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

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