Fork me on GitHub
Showing posts with label spring. Show all posts
Showing posts with label spring. Show all posts

2 Aug 2011

Avoiding accidental i18n in Grails

We’re developing an app that’s exclusively for a UK audience so i18n really isn’t an issue for us. However recently we got bitten by some i18n creeping in where we didn’t want it. Specifically, when using Grails’ g:dateFormat tag the default behaviour is to format the date according to the Locale specified in the user’s Accept-Language header. Even though we are explicitly specifying a format pattern for the date Java is aware of localized day names for some languages so the output can vary. The result is that on a page full of English text there suddenly appears a Spanish or Swedish day name. What makes things worse is that as we use server-side content caching and a CDN if a user with a non-English Accept-Language header is the first to see a particular page or bit of dynamically retrieved content then the cache is primed and until it expires everyone will see the non-English day name text.
The solution in a Grails app is as simple as replacing Spring’s standard localeResolver bean with an instance of FixedLocaleResolver. Just add the following to grails-app/conf/spring/resources.groovy:
localResolver(org.springframework.web.servlet.i18n.FixedLocaleResolver, Locale.UK)
This changes the way Spring works out the request locale and any locale-aware tags should just fall into place.

28 May 2010

Grails Spring Events Plugin

Following on from my last post I've developed a Grails plugin that packages the asynchronous events behaviour up and adds some extra useful functionality.

In addition to the asynchronous event processing the plugin gives you:
  • A publishEvent method attached to all domain classes, controllers and services.
  • A Hibernate session bound to the listener thread for the duration of the notification so that listeners can access lazy-loaded properties, etc.
  • The ability to have a "retry policy" for certain types of failed notifications on individual listeners. This is particularly useful for listeners that do things like invoking external web-services that may be periodically unavailable.

To install the plugin just use:
grails install-plugin spring-events

The code and some more detailed documentation is on GitHub. I'll be migrating the docs to the plugin's page on grails.org soon.

3 May 2010

Asynchronous application events in Grails

On the project for my current client we've been using JMS in a rather naïve way for some time now. We've also experienced a certain amount of pain getting JMS and ActiveMQ configured correctly. However, all we're really using JMS for is asynchronous event broadcasting. Essentially we have a handful actions such as flushing hardware caches and notifying external systems that take place when a document changes. We don't want these things blocking the request thread when users save data.

After wrestling with JMS one too many times we decided to take a look at Spring's event framework instead. It turns out it's extremely easy to use for these kinds of asynchronous notifications in a Grails application.

Essentially any artefact can publish an event to the Spring application context. A simple publishing service can be implemented like this:
import org.springframework.context.*

class EventService implements ApplicationContextAware {

    boolean transactional = false

    ApplicationContext applicationContext

    void publish(ApplicationEvent event) {
        println "Raising event '$event' in thread ${Thread.currentThread().id}"
        applicationContext.publishEvent(event)
    }
}
So a Grails domain class can then do something like this:
def eventService

void afterInsert() {
    eventService.publish(new DocumentEvent(this, "created"))
}

void afterUpdate() {
    eventService.publish(new DocumentEvent(this, "updated"))
}

void afterDelete() {
    eventService.publish(new DocumentEvent(this, "deleted"))
}
Grails services make ideal ApplicationListener implementations. As services are singleton Spring beans they are automatically discovered by Spring's event system without any configuration required. For example:
import org.springframework.context.*

class EventLoggingService implements ApplicationListener<DocumentEvent> {

    boolean transactional = false

    void onApplicationEvent(DocumentEvent event) {
        println "Recieved event '$event' in thread ${Thread.currentThread().id}"
    }
}
Of course, multiple listeners can respond to the same events.

If you run the code you will notice that by default Spring's event system processes events synchronously. The EventService and ApplicationListener will print out the same Thread id. This is not ideal if any of the listener implementations might take any time. Luckily it's easy to override the ApplicationEventMulticaster bean in resources.groovy so that it uses a thread pool:
import java.util.concurrent.*
import org.springframework.context.event.*

beans = {
    applicationEventMulticaster(SimpleApplicationEventMulticaster) {
        taskExecutor = Executors.newCachedThreadPool()
    }
}
Running the code again will show the event being published in one thread and consumed in another. If you have multiple listeners each one will be executed in its own thread.

Oddly, I would have thought it was possible to override the taskExecutor property of the default ApplicationEventMulticaster in Config.groovy using Grails' property override configuration, but I found the following didn't work:
beans {
    applicationEventMulticaster {
        taskExecutor = Executors.newCachedThreadPool()
    }
}

25 Feb 2010

Using a custom data binder with Grails domain objects

Yesterday I read a post by Stefan Armbruster on how to register a custom data binder to lookup Grails domain objects by any arbitrary property. I wanted to go a little further so that I could not only bind existing domain instances but create new ones as well.

For example, let's say I have Artist and Album domain classes where Artist hasMany Albums and Album belongsTo Artist. Artist has a name property that is unique. On my create album page I want to be able to type in the artist name and have the save action in the controller automatically lookup an existing Artist instance or create a new one if it doesn't exist. Doing this I don't want to have to add or change anything in the save action itself - I could theoretically use dynamic scaffolding.

Adapting Stefan's PropertyEditor implementation I created this:
The crucial change is the if (!value) block which creates the new instance and populates the relevant property.

To make everything work I just need to:
  1. Add the PropertyEditorRegistrar and place it in resources.groovy as per Stefan's post.
  2. Have a text input or autocompleter with the name "artist" in my create album form.
  3. Add artist cascade: "save-update" to the mapping block in Album so that when the Album is saved the new Album will get saved as well.

26 Nov 2009

Springcache Plugin Status

A couple of people have asked me about the Springcache plugin and Grails 1.2. Currently the plugin is not compatible with 1.2-M3 and onwards. The reason for this is that the spring-modules-cache library that the plugin depends on is not compatible with Spring 3.0 which is used by the newest versions of Grails. Not only that but the spring-modules project has been discontinued altogether. There is a fork of it on Github but that doesn't look terribly active either. This means that at some point I'm going to have to sit down and write some code to bring the annotation driven caching inside the plugin itself and drop the spring-modules dependency. However, just to compound things the Spring documentation on the relevant area hasn't been updated yet and still references the classes that have been removed from the API which is what's causing the incompatibility in the first place!

So, it's on my to-do list but don't hold your breath!

10 Mar 2009

Internationalizing Domain Classes and Enums

It's common to provide a toString implementation on a domain object that may well end up being used in the view. However, this doesn't allow for internationalization in the view. A good solution that I've used a number of times is to have classes implement Spring's MessageSourceResolvable interface. Consider this domain class that represents an image file:

grails-app/domain/Image.groovy

class Image {
    String name
    String path
    User uploadedBy
    org.joda.time.DateTime dateCreated

    static transients = ['file']
    File getFile() {
        new File(ConfigurationHolder.config.image.base.dir, path)
    }

    String toString() {
        "$name uploaded by $uploadedBy.username on $dateCreated - ${file.size()} bytes"
    }
}

The toString implementation is all well and good if we're targeting an English-speaking audience but with some simple changes we can make it i18n compliant:

grails-app/domain/Image.groovy

class Image implements org.springframework.context.MessageSourceResolvable {

    // properties as above

    static transients = ["codes", "arguments", "defaultMessage"]

    Object[] getArguments() {
        [name, uploadedBy.username, dateCreated.toDate(), file.size()] as Object[]
    }

    String[] getCodes() {
        ['image.info'] as String[]
    }

    String getDefaultMessage() {
        "$name uploaded by $uploadedBy.username on $dateCreated - ${file.size()} bytes"
    }
}

In our message properties file we can add:

grails-app/i18n/messages.properties

image.info={0} uploaded by {1} on {2,date} - ${3,number,integer} bytes

In the view we can display our object like this:

<g:message error="${imageInstance}"/>

Yes, that is the error attribute we're passing to the message tag! Grails intends the attribute to be used for outputting validation errors but the underlying mechanism is the same - Spring's ObjectError implements MessageSourceResolvable and that's how Grails' message tag resolves the displayed error message. Rather than passing separate code, args and default attributes to the tag we can pass the single MessageSourceResolvable instance and its implementation will take care of supplying those values.

Note: I added a message attribute to the message tag to avoid the confusion caused by using error. This is in Grails from version 1.2-M1 onwards.

We can now add translations of our object description. For example:

grails-app/i18n/messages_af.properties

image.info={0} opgelaai deur {1} op {2,date} - ${3,number,integer} grepe

It's worth noting that the format of the dateCreated property will be automatically determined by the request locale so the value will be formatted correctly for the user.

I've found this technique can also be very useful on enum classes. For example:

src/groovy/com/mycompany/Season.groovy

package com.mycompany

enum Season implements org.springframework.context.MessageSourceResolvable {
    SPRING, SUMMER, AUTUMN, WINTER

    Object[] getArguments() { [] as Object[] }

    String[] getCodes() {
        ["${getClass().name}.${name()}"] as String[]
    }

    String getDefaultMessage() { name() }
}

grails-app/i18n/messages.properties

com.mycompany.Season.SPRING=Spring
com.mycompany.Season.SUMMER=Summer
com.mycompany.Season.AUTUMN=Autumn
com.mycompany.Season.WINTER=Winter

grails-app/i18n/messages_en_US.properties

com.mycompany.Season.AUTUMN=Fall