Blogs

SpringSource Blog

Countdown to Grails 2.0: Static resources

Peter Ledbrook

Web applications typically rely heavily on what we call static resources, such as Javascript, CSS and image files. In a Grails application, they are put into a project's web-app directory and then referenced from the HTML. For example,

<link rel="stylesheet" href="${resource(dir: 'css', file: 'main.css')}" type="text/css">

will create a link to the file web-app/css/main.css. All very straightforward. You might even think that the current support is more than sufficient for anyone's needs. What else would you want to do?

That's a good point. The answer depends on the complexity of your application, but let's start with the example CSS link above. Why do we have to type out the <link rel="..." href=...>? Just by looking at the extension, we know that the resource is a CSS file. We also know that CSS files should be linked into an HTML page using the above element. So we're basically doing a lot of typing for something that Grails should be able to handle itself.

"OK," I hear you say, "but we could add a tag for CSS links." Indeed we could, and Grails 2 will include a <g:external/> tag that intelligently picks the appropriate link type for the resource. But now consider production: wouldn't it be a good idea to bundle CSS and Javascript files together? What about compressing them? (As an aside, Yahoo provides a tool called ySlow that will give you hints on just these kinds of optimisation). So how do we implement such optimisations? You could probably do it on an ad-hoc basis, but doing so limits your options because you can't define relationships between resources. What we really want is some way of declaring resources in a central place and indicating which ones should be processed and bundled together.

If I have yet to persuade you that there could be a better way to manage static resources, then think about Grails plugins. Many of these provide their own static resources. In fact, some plugins provide the same resources as each other. For example older versions of the YUI, Bubbling and Grails UI plugins all include the YUI libraries. Do we really need multiple copies of entire Javascript libraries? Of course not. Part of the problem can be solved with inter-plugin dependencies, but that doesn't work well on its own because there is no way for a plugin to say that some of its resources depend on specific resources in another plugin. In the end, it's up to the developer to make sure that all the appropriate resources (including transitive dependencies) are included in each page. That may involve some trial and error to make sure all requires resources are linked into the page and in the correct order.

Fortunately, we can do better. Enter the Resources plugin.

Declarative resources

By installing just this one plugin, you can start declaring your static resources (Javascript, CSS, etc.) in reusable modules. You can then define dependencies between modules so that Grails knows exactly which resources a module requires, including those specified in transitive dependencies. As an added benefit, modules ensure that resources are always declared in the correct order in a page and that there are no duplicates. In doing so, modules take much of the pain out of static resource management.

Declaring modules

You can declare resource modules in both applications and plugins. There are a couple of ways of doing this, but the most common approach is to add one or more dedicated artifacts to the project. For an application this might be grails-app/conf/ApplicationResources.groovy. As another example, the YUI plugin has grails-app/conf/YuiPluginResources.groovy. The basic structure of these files looks like this:

modules = {
    core {
        resource url: 'js/core.js', disposition: 'head'
        resource url: 'js/ui.js'
        resource url: 'css/main.css'
        resource url: 'css/branding.css'
        resource url: 'css/print.css', attrs: [media: 'print']
    }

    utils {
        dependsOn 'jquery'
        resource url: 'js/utils.js'
    }

    forms {
        dependsOn 'core', 'utils'

        resource url: 'css/forms.css'
        resource url: 'js/forms.js'
    }
}

"core", "utils" and "forms" are the names of our application modules, and as you can probably guess, the "forms" module depends on both "core" and "utils". You can also see that the "utils" module depends on "jquery", which is a module provided by the jQuery plugin. This structure is best seen in diagrammatic form:

If we look a bit deeper into the above module definitions, we can see that individual resources are declared within a module using "resource" plus a URL. This URL is the location of the resource relative to the web-app directory in your project. If you wish, you can also add extra attributes for fine-grained control of resources, in particular via the "disposition" setting.

There are two standard dispositions: "head", meaning that a resource goes inside the <head> element, and "defer", which typically means the end of the body (although you have control over the exact placement as you'll see shortly). By default, CSS files have a disposition of "head" while Javascript files use "defer", but these defaults can be overridden on a per-resource basis.

We now have the basis for defining simple or complex networks of resource modules, with those networks crossing application and plugin boundaries. Even better from a user's perspective, it's up to the plugins to make sure that their modules include the appropriate resources and dependencies.

So, Grails may now be aware of what static resources your application and its plugins provide, but it still doesn't know which pages require which resources. For that, you have to make some modifications to your GSPs.

Including resources in a page

As you know, you previously had to declare all your CSS and Javascript links explicitly in your layouts and views. So how does this change with the Resources plugin? Instead of putting in links to individual resources, you declare which modules your page requires, which makes for much conciser <head> blocks. In addition, you specify where in the page the resource links should go. Here's an example of a very simple layout using Resources:

<html>
<head>
    ...
    <r:require modules="common, jquery"/>
    <r:layoutResources/>
</head>
<body>
    ...
    <r:layoutResources/>
</body>
</html>

As you can probably work out, the <r:require> tag tells Grails which static resources to include in the page while the <r:layoutResources> tags specify where the links should go. You should have two <r:layoutResources> tags: one in <head> and the other in <body>. Any resource with a disposition of "head" will go where the first one is, while those with a disposition of "defer" will be inserted at the second's location.

That really is it! Most of the work goes into ensuring that your module definitions are correct.

What I have covered so far works really well for CSS, Javascript and favicon files because the Resources plugin knows what links to generate for them and where to put those links. But what happens with inline images and scripts?

Ad-hoc resources

The Resources plugin calls inline images and scripts "ad-hoc resources". These typically aren't declared in modules and are simply processed as and when they are encountered in the page. A standard inline image link looks something like:

<img src="${resource(dir:'images',file:'grails_logo.png')}" ... />

So how do we make the Resources plugin aware of the image file? We don't! As of Grails 2.0, the <g:resource/> tag (used as a method above) automatically registers the resource with the plugin if it's installed. That means all the magic performed by mappers (which I'll talk about in a bit) will be applied to the given resource. And of course, <g:resource/> can be used for any type of resource, not just images.

Inline scripts are a little different because they aren't links to external files. Yet they can still benefit from the Resources plugin. By default all inline scripts declared with the <g:javascript/> tag will behave as they always have done. But if you replace <g:javascript/> with the <r:script/> tag, the inline script will be moved to the location of the second <r:layoutResources/> (their default disposition is "defer"). You can also declare an explicit disposition so that the script goes into <head>. Best of all, the inline scripts retain the order in which they are declared in the page.

There is one other source of ad-hoc resources to mention: CSS. Stylesheets are a common way of specifying background and other types of image, but CSS files aren't processed as GSP files. Is the Resources plugin aware of these links? Fortunately, yes. Even though the Resources plugin modifies the URL paths for all the resources it manages, that doesn't matter to you because the plugin also updates the links in the CSS files automatically. Everything will just work!

Magic mappers

On its own, the Resources plugin makes the management of static resources much simpler than before. But that's only part of the story. Since the plugin knows about all the resources and controls the link generation for them, it can perform extra processing to add interesting behaviour. This processing is done by an extensible pipeline of mappers:

Resources Mapper Pipeline Diagram

Don't take this diagram as an absolute and correct reference: as a user, the exact pipeline and how it works doesn't really matter. The key is that you can if you want add your own mapper implementations or simply install a plugin that provides some mappers of its own. The result is some very powerful features for near zero effort.

For example, let's say you have the Resources plugin installed, some modules defined, and your GSP views and layouts set up to use the appropriate tags. Simply by installing the Cached Resources and Zipped Resources plugins, you will immediately start satisfying some of the ySlow recommendations, such as having a long-dated Expires header and gzip compression for all your static resources (although you can disable gzip compression for specific file types such as images, which tend to be compressed already). It shouldn't be this easy, right? But it is.

Conclusion

The Resources plugin has been around for a little while and is already used in a few production sites. It's even in use on the Grails website. And no wonder: it combines much improved management of static resources with a mapper pipeline that allows for easy-to-use but powerful features. Experienced web developers will notice the difference immediately, and you can expect more and more plugins to come with Resources support in the future. It even has its own user guide.

Because of the advantages the plugin brings, Grails 2 makes it a default plugin for new Grails projects and also integrates it into some of the core tags, such as <g:resource>. But even if you can't use Grails 2 yet, you can still make use of it with older versions of Grails and benefit from great resource management with little effort. One of it's key design principles is that it can be installed in any Grails application without breaking anything.

Whichever version of Grails you use, installing this plugin will improve your life as a web developer. That's not something to be sniffed at.

Similar Posts

Share this Post
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • DZone
  • LinkedIn
  • Slashdot
  • Technorati
  • Twitter
 

28 responses


  1. OOooooo very nice!! I want the production release right now!!


  2. I tried this out with 1.3.7, ran into an issue with reloading resources in development mode. Probably a bug in Grails itself rather than plugin, but still had to give it up. Hopefully not an issue in 2.0 Also, JS libraries that have dynamic loaders built in will first hit a 302 redirect before loading source from plugin.

    Dmitry.


  3. One other thing. There is a way to declare different modules for different environments just by wrapping the modules block in the environments block. I think that's supper useful when you have different builds of the JS libraries (i.e. ExtJS comes in minified, debug, debug with comments, etc) and of course you can turn off compressor for Debug builds.

    Dmitry.


  4. Anyone else tired of Grails, presumably in an effort to stay "edgy", forcing everyone to learn something new every few months?


  5. Nice and long awaited functionality. Good work.
    One comment though. Although YSlow believes it is a good idea to defer loading af JavaScript (i.e. insert the SCRIPT tags just before the enclosing BODY tag), it really isn't. PageSpeed agrees with me, so I believe the default disposition for JavaScript resources should be HEAD.


  6. Nice article, thanks for the information.


  7. @Dimitry: did you raise an issue? I haven't come across that problem myself.


  8. Is same functionality planned for spring mvc in near future?


  9. Great news!

    Are you planning to support CDNs for static resources? Scripts like JQuery are usually retrieved from Google or MS's CDNs.

    See http://code.google.com/apis/libraries/devguide.html


  10. @Per: PageSpeed says it's non-optimal, but the optimal solution is fairly involved. I think deferred loading is beyond the scope of the plugin, but at least you do have the option of specifying whether JS is deferred or not. It's probably work raising a JIRA issue to make the default configurable (head or defer).

    @Elisten Not that I'm aware of, but I'll see if I can find out.

    @Gabriel Some users are looking into. You can specify the location of a particular resource using an absolute URL if you want, but I don't think you will get compression and caching via the plugin. Still, that may be all you need, in which case the plugin does support CDNs :) Also, you can configure different modules depending on the environment, so CDN URLs may only be configured for production for example.


  11. Nice. Peter, what other core tags, besides g:resource, does the Grail 2 integrate into?


  12. @Sean: , and . The latter two are new to Grails 2 and documented here:

    http://grails.org/doc/1.4.0.M1/ref/Tags/img.html
    http://grails.org/doc/1.4.0.M1/ref/Tags/external.html


  13. that sounds….fantastic.
    question is: when will grails 2.0 arrive? I have a small project that i would be willing to test grails 2.0 with. In production even :)


  14. @giony: We're aiming for mid-September. First Grails 2.0 milestone hopefully out this week.


  15. "without breaking anything" ?? it broke all my links. I have custom mappings for urls that go something like context/really-cool-widget.html, where the "html" file is really a SEF url and not a file. the resource tag now wants to put in context/static/really-cool-widget.html, and I can't seem to config that static out.

    If you set the grails.resources.uri.prefix= property to null, it still uses static. if you set it to a blank string you get a double slash, e.g. context//really-cool-widget.html.

    my only workaround at the moment is to use the resource tag and set my context to root.


  16. @Robert: you should be able to use the grails.resources.adhoc.patterns setting. For example,

    grails.resources.adhoc.patterns = ["*.html"]

    But perhaps the tag is the wrong one to use anyway, if it's not really a link to a real resource?


  17. it seemed to be the only tag available that created a full link.

    I started down the path of changing all my code so it doesn't use that tag at all. I tried setting the grails.resources.processing.enabled = false in config and it still seems to intercept urls. I have links to images from css and they don't work now, same 'static' url issue. To take your suggestion one further I also tried:

    grails.resources.adhoc.patterns = ["*.html","/images/*", "*.css"]

    and going to context/images/bg-tile.png redirects me to context/static/images/bg-tile.png

    is there some way to completely disable this?


  18. Have an issue where my css resource keeps 404ing. I turned up the logging and found an entry that said:

    getOrCreateAdHocResource for [myResource], latch is null
    getOrCreateAdHocResource for [myResource], creating resource as not found

    Javascript resources seemed to work fine.

    Any Ideas?

    Grails-1.3.7, JDK-1.5 (nothing i can do about that), resources-1.1.1


  19. @Todd: that could be a bug. There are quite a few fixes going into 1.1.2. You could also try downgrading to 1.0.2 for the moment. Note that if those 404s happen immediately after changing a resource file (e.g. a CSS) then you may just have to wait a little while the resources are processed.

    Robert: Sorry for the long delay. I'll get Marc to take a look at your use case, but note that the adhoc patterns you specified include everything under /images, hence why you get a redirect for your image. If you don't want images to be intercepted by Resources, keep "/images/*" out of the adhoc patterns list.


  20. Nice functionality.

    I am wondering though, how can I have ajax-loade javascript evaluated.

    i.e. if you have a template with html/javascpit and render it via ajax the javascpit in the will not be executed.

    The only workaround I found is including a r:layoutResources if the current request is an ajax request like this.

    Am I missing something?


  21. Another option is to use a plain <script> tag, which doesn't hook into Resources. Probably the safest option for embedded Javascript.


  22. Using a plain in templates won't render the javascript correctly in the initial page load because the required libraries are loaded at the end of the page (same for ).

    I follow an other approach described in http://stackoverflow.com/questions/8736277/grails-resources-plugin-and-ajax-loaded-javascript, but I don't know if there is a better solution.


  23. If your embedded JavaScript requires libraries, those should be declared in disposition 'head' rather than 'defer'. However, I added an answer to the StackOverflow question that should help.


  24. I will lose one ySlow goal if I use 'head' disposition for js :)

    Thanks for the answer Peter.


  25. How does one go about setting up SWF files as a module? I'm trying to package a JS lib which depends on SWF files, which should be static resources; I have them stored in /media/ … how do I get these served?


  26. Has anyone had a problem with absolutely no CSS rendering in IE, Page just renders with multiple errors.

    Developer Tools shows
    But nothing under CSS

    Any thoughts?


  27. @Wayne r:resource will work with SWF files, or g:resource in Grails 2.0 (if you want automatic caching and zipping).

    Because SWF are (almost?) always used inline, there is no reason to declare them in modules.

    @dStinger I've never seen this problem. What do the Developer Tools show? I think some of your text has failed to appear, perhaps because you used markup in the comment.


  28. Using a plain in templates won't render the javascript correctly in the initial page load because the required libraries are loaded at the end of the page (same for ).

4 trackbacks

Leave a Reply