Login required
Download

Grails Vaadin Plugin

(7)
Author(s): Daniel Bell, Les Hazlewood
Current Release: 1.2.2
Grails Version: ?
Tags ajax rich client vaadin
grails install-plugin vaadin
A plugin for creating a Vaadin applications on Grails.

Overview

Install:

grails install-plugin vaadin

After installing the plugin:

  1. Create a class in the grails-app/vaadin directory that extends com.vaadin.Application .
  2. Specify this class in the Vaadin configuration file ( grails-app/conf/VaadinConfig.groovy ).
  3. Remove any Grails URL mappings for the URL where you want to access the Vaadin UI (in grails-app/conf/UrlMappings.groovy )
  4. Run the application:
grails run-app

Usage

As of the plugin's 1.2 release, all Vaadin-related code should reside in the grails-app/vaadin directory. This directory is a normal source directory, and should contain packages and Groovy classes as expected (e.g. grails-app/vaadin/com/mycompany/vaadin/MyVaadinApplication.groovy ).

Placing the source in this directory allows the Grails environment to treat all Vaadin code as a special Grails artefact, enabling additional features, such as

  • auto-reload after a source code change (just hit browser refresh)
  • Spring dependency support, and
  • integration with Grails internationalization (i18n).

Spring Dependencies

The traditional way in Grails to wire up Spring dependencies is to simply declare the dependency as a property in your source code, for example, an AuditService used to keep track of what happens in an application. Note the 'def auditService' property:

//Ok for normal Grails artefacts, but NOT IDEAL FOR VAADIN UI COMPONENTS:
class VaadinApplication extends com.vaadin.Application {

def auditService

init() { auditService.log new ApplicationStartedEvent()

//set up window and buttons, etc. … } }

However, this is NOT recommended in your Grails-based Vaadin applications, for two reasons:

Reason 1: In order to support dependency injection across all of your Vaadin components (beyond the initial Vaadin com.vaadin.Application instance), you would be required to wire all of your Vaadin UI components in Spring, i.e. grails-app/conf/spring/resources.groovy .

You wouldn't be able to call Window window = new Window("My Window") in your code, and instead would need to acquire all instances from Spring, e.g. Window window = applicationContext.getBean("myWindow"); (and don't forget to make all of those UI beans prototype scoped!).

For even relatively simple UIs, this could quickly become difficult to manage.

(Note: If you still want to do this to achieve a templating type of mechanism where the spring bean definitions act as a sort of UI template, this is ok - but you must ensure that all beans have 'singleton = false' or scope = prototype'. Vaadin UI components cannot be application singletons - each user must get their own copy. Developer beware).

Reason 2: Perhaps the even more important reason requires understanding of Vaadin UIs: A Vaadin application runtime instance, and all of the objects that it reaches (children windows, buttons, etc) are bound to the HttpSession at runtime. Each user has their own Application instance, their own state. So what does this mean for Spring dependencies?

Spring beans in the runtime ApplicationContext , such as all of your
*Service implementations (UserService, DocumentService, etc) are usually stateless - unlike the Vaadin components. All Vaadin components that used traditional dependency injection ('def userService', etc) would retain a strong reference to each Service or any other bean in the ApplicationContext .

Why is this a problem? Session objects (and their contents, such as the Vaadin application and all of its children) might need to be serialized and paged to disk; the servlet container is free to store sessions in memory and then offload them to disk if memory becomes full. If you have a larger Vaadin application and thousands of users, the servlet container might need to do this. The same serialization could also happen if you use clustered sessions, or an enterprise caching framework that serializes Session data across a cluster.

If your Vaadin components retain strong references to stateless Spring beans that are application singletons, each serialized Session would also serialize the Spring beans! This makes the serialized object graph larger than it needs to be. Also, when deserialized, these beans would not be under Spring's control and would very likely fail to work.

Using Dependencies

So, based on the above reasons why it may not be ideal to use standard dependency injection, the Vaadin plugin adds two dynamic methods to all classes in grails-app/vaadin to support dependency acquisition:

getBean(String beanName)
getBean(Class beanType)

You can access any Spring bean as necessary by calling this method in any class under grails-app/vaadin . Here is the above Application example refactored to use this mechanism:

//Cleanly serializable implementation:
class VaadinApplication extends com.vaadin.Application {

init() { getBean(AuditService).log new ApplicationStartedEvent()

//set up window and buttons, etc. … } }

The above example uses the getBean(Class clazz) method to acquire the dependency by type. We could have just as easily looked up the bean by name: getBean("defaultAuditService") . These methods will always ask the Spring ApplicationContext for the dependency. Spring caches these references of course, so the call is extremely fast, but still serialization-safe.

(Aside: the 'getBean' approach is one way to solve the serialization problem. Another way is to dynamically replace all property definitions, e.g. 'def userService', with a runtime proxy: the proxy can be serialized safely, but when deserialized it would know how to look up the 'real' target bean from the Spring ApplicationContext at runtime. The current version of the Vaadin plugin does not support this dynamic proxy approach - please discuss on the dev list if you'd like to help make this happen!).

Internationalization (i18n).

Internationalization (i18n) is one of the things you have to do yourself when writing standard Vaadin applications - messing with Resource Bundles, MessageFormats, etc. But you don't have to worry about that when using the Grails Vaadin plugin!

All classes under grails-app/vaadin automatically receive dynamically added methods to 'hook' in to Grails I18N support:

i18n(String key, Collection args=null, Locale locale=LocaleContextHolder.getLocale())
i18n(String key, String defaultMessage, Collection args=null, Locale locale=LocaleContextHolder.getLocale())

The first method will throw an exception if there is no matching key. The second one will show the default message if the key doesn't exist.

All message keys should be defined in the standard Grails i18n location in grails-app/i18n/messages*.properties .

As you can see, the args and locale arguments are optional. so you can call the method in many permutations. If you don't specify a Locale (pretty common), it defaults to the one Grails sets up automatically for a request as defined in the Grails chapter on internationalization .

Some examples:

Window window = new Window(i18n("default.home.label"));

//button with label arguments:, e.g. "You have clicked {0} times since 2:15 pm!": Button button = new Button(i18n("button.click", [numClicked, sinceDate]));

window.addComponent button

..

Upgrading from 1.0 to 1.1

If you're upgrading from the 1.0 Vaading plugin, your old Vaadin configuration will not be automatically upgraded. You will want to use the following as a starting point in your grails-app/conf/VaadinConfig.groovy file:

vaadin {

//Your Vaadin application class that extends com.vaadin.Application: applicationClass = "com.mycompany.MyVaadinApplication"

//the context relative path where you want to access your Vaadin UI. Default to the context root. contextRelativePath = "/"

productionMode = false

googleAppEngineMode = false }

environments { production { vaadin { productionMode = true } } }

Release Notes

  • 1.2 - Created new 'vaadin' artefact type - all Vaadin code should now reside in grails-app/vaadin . New features: Seamless integration with Grails Internationalization (i18n) as well as Spring bean lookup from any Vaadin component.
  • 1.1.2 - Upgraded to the latest stable Vaadin release (6.3.4), fixing these issues.
  • 1.1.1 - A minor point release that ensures compatibility with Grails 1.1 and higher. However, Groovy 1.7 language features (anonymous inner classes, etc) are only available in Grails 1.3+ environments (Groovy 1.7 is part of Grails 1.3+).
  • 1.1 - Latest stable Vaadin release (6.3.3). Ability to specify a path to your Vaadin UI other than the root context path (e.g. http://localhost:8080/myapp/vaadinUI). Environment-specific configuration support (production, development, etc. Custom environments too 'beta', 'uat', etc).