0.2 (26/03/2009)

0.1 (16/03/2009)

Initial release

Description

GraniteDS Flex Plugin

This plugin provides integration between Grails and Adobe Flex using Granite Data Services

GraniteDS

GraniteDS is an open source project that provides integration between Adobe Flex and Java frameworks. It has had full support Hibernate and Spring for a long time, so implementing the integration with Grails is quite natural.
The plugin is currently a beta version and has been tested with Grails 1.2.2+ and Grails 1.3.1+.

Features

Automatic configuration: the various parts required in web.xml and other Flex/GraniteDS configuration files are automatically setup by the plugin. GraniteDS 2.2.0.RC1 Java and Flex libraries are included.

Automatic generation of the Flex domain classes: the embedded Gas3 generator automatically generates as3 classes from the Groovy model. Gas3 is registed as a compilation event listener, so there is no manual operation needed in most cases. Even if it is recommended to use JPA annotations for the Groovy entities, pure GORM entities are now supported since 0.4.

On-the-fly Flex compilation: the Flex OEM compiler is automatically triggered in background when changing any mxml/as/css in the grails-app/views/flex folder (this folder is now configurable). The swf will be built in grails-app/views/swf. The main mxml file must have the same name than the Grails application.

Includes the GraniteDS Tide client framework: it gives full support for transparent lazy loading of collections, paged data and other data related features.

Grails services and controllers can be exposed as Flex remoting destinations with a simple annotation: no particular configuration is needed to expose a Grails component to Flex, just use the org.granite.tide.annotations.TideEnabled. Newly deployed or redeployed services are automatically exposed without any manual operation.

Grails controllers results can be used with Flex data binding: the model variables returned by controller components are available as Tide context variables and can easily be bound to the Flex UI.

Includes the html-wrapper Flex task to generate an html page embedding the swf application.

Flex application generator to get started quickly with a simple CRUD application.

Integration with Spring security: remote calls can be secured and credentials are propagated to Grails. The Identity Flex component can be used to show/hide parts of the UI depending on the user access rights. Both the acegi and spring-security-core plugins are now supported since 0.8.0.

Support for Gravity server push: server push with Jetty continuations or Tomcat Comet support can be enabled and data can be exchanged between Flex clients or from Grails to Flex.

Installation

Use this from the project folder:

grails install-plugin gdsflex

Getting started tutorial

In this short tutorial, we are going to create a simple Flex application that list people in a DataGrid.

First create a Grails application and install the plugin :

grails create-app example

cd example

grails install-plugin gdsflex

Now create a Person domain class, a Grails controller (using scaffolding so we can enter test data) and a Grails service that we will access from Flex.

grails create-domain-class Person

grails create-controller Person

grails create-service Person

Edit grails-app/domain/example/Person.groovy :

package example

class Person {

String uid

String firstName

String lastName
}

Edit grails-app/controllers/example/PersonController.groovy :

package example

class PersonController {

def scaffold = Person
}

Edit grails-app/services/example/PersonService.groovy :

package example

import org.granite.tide.annotations.TideEnabled

@TideEnabled
class PeopleService {

static transactional = true

def list() {
return Person.list()
}
}

Our Grails application is ready. You can start it with grails run-app and check that it works at http://localhost:8080/example/.
Now let's add the Flex part :

Now you have to run the AS3 generator to create the AS3 domain classes, and compile the Flex application :

grails gas3

grails mxmlc

Start the application with

grails run-app

and go to http://localhost:8080/example/person to create some entries in the html UI.
Then go to http://localhost:8080/example/example.swf and check that you can list the entries you just created.

Now we are going to see each part in more details.

Flex compilation

The GDS plugin requires that you have a Flex SDK installed somewhere. By default it will use the one defined by the environment variable FLEX_HOME, this setting can be overriden in grails-app/conf/BuildConfig.groovy with the flex.sdk property (flex.sdk = "/home/work/flex_sdk_4_0"). The plugin uses the Flex OEM compiler to automatically compile .mxml and .as files present in the grails-app/views/flex folder. The swf will be built in grails-app/views/swf and gdsflex contains a simple servlet that maps all incoming requests for *.swf to this folder. Some settings can be defined in a grails-app/conf/GraniteDSConfig.groovy file :

The Flex compilation automatically includes all swc libraries from folders flex_libs and web-app/WEB-INF/flex/libs.

Once compiled, requesting http://localhost:8080/myApp/myApp.swf will look for an existing swf in grails-app/views/swf/myApp.swf.
ActionScript classes for the domain model are generated in grails-app/views/flex (or the configured source folder) from the Groovy domain classes.

Using Flex Builder

When using Flex Builder, you probably don't want to have the plugin do its own Flex compilation so you have to set autoCompileFlex = false (see previous paragraph).
Then you should build the swf in grails-app/views/swf so it can be picked up by the plugin swf servlet.

You have to link your Flex application with the libraries granite.swc and granite-essentials.swc that you can copy to your project (they are available in the plugin sources, for example ~/.grails/1.3.5/projects/example/plugins/gdsflex-0.8.4/src/flex/libs).
You also have to use the compiler option -services to reference the remoting configuration file in web-app/WEB-INF/flex/services-config.xml and the option -context-root /example to define the web app context path.
Lastly you need to use the -keep-as3-metadata option to keep the necessary Flex annotations in the compiled swf.

Exposing Grails services

Exposing a Grails service as a Flex destination just requires adding the org.granite.tide.annotations.TideEnabled annotation to the service:

class Person {

String uid

String firstName

String lastName
}

import org.granite.tide.annotations.TideEnabled

@TideEnabled
class PeopleService {

static transactional = true

def list() {
return Person.list()
}
}

Note that the entity can be a GORM entity or a JPA entity. In both cases, it is highly recommended (but not mandatory) to have a persistent uid property that will be used as a common identifier between Flex, Hibernate and the database layers.

the preinitialize handler is used to register the application with the Tide framework

the Tide context allows to get client proxies to server components

tideContext.peopleService gets a client proxy to the PeopleService Grails service

the dummyPerson is necessary here to force the Flex compiler to include the Person class in the swf. If not present, you will get a serialization error. That variable will not be needed as soon as you use the Person class somewhere else.

the 'people' property needs to be public (or at least provide a setter), because Tide needs to merge the collection instance and ensure it remains the same instance throughout remote calls. If the property is private, there will be a degraded mode where the collection instance is not kept identical between calls.

The Tide framework can also inject references to client proxies in Flex components:

Note the use of Inject (+) instead of In (+) to indicate a typesafe injection. In (+) matches the service name, Inject (+) matches the service type, so the property can be named as you want and there are less risks to mispell the name as the Flex compiler will be able to detect the error. It's also useful when using Flex builder as you get support for autocompletion of service methods in the Flex application.

One important thing when using the Tide framework is that the entities keep managed on the Flex client and can be considered as real JPA/Hibernate detached entities.
Entities received as arguments for a service can be directly saved or updated with the GORM extra persistence methods save, update, merge and delete. It is recommended though to use 'merge' instead of 'update' or 'save' when updating an existing object.

Exposing Grails controllers

Grails controllers can also be exposed with the TideEnabled annotation. The model variables received from controller method will be bound to the Tide context as context variables.

Since Grails 1.3, you have to specify the full qualified name of the controllers in the In (+) annotations when your controllers are not in the default package :

[In("myapp.peopleController")]
publicvar peopleController:Component;

Choosing between using services and controllers is a matter of preference and application design. Using services requires a little more work on the client side, using controllers simplifies the integration but implies a very strong coupling between the Flex view and the Grails controller.

Using transparent lazy loading

Tide maintains a client image of the server persistence context. It is able to trigger initialization of lazy loaded collections when they are bound to a UI component. While this feature should not be overused because of the extra network traffic it can cause, it is very handy when dealing with master-detail elements or tree structures.

Scaffolding templates for gdsflex enabled controllers

The plugin comes with templates for Grails scaffolding that include the necessary actions for use from Flex :
find, persist, merge, remove, upload and download.
The templates can be installed with

grails install-flex-templates

Integration of html-wrapper Flex task

The plugin includes the html-wrapper task to generate a html file embedding the swf file.
This is necessary for example to use the Flex browser integration and deep linking functionality.

grails html-wrapper

The html file is generated both in web-app/{appname}.html with all necessary files for the Flex wrapper (js, css...),
and in grails-app/views/flexindex.gsp. The generated gsp can for example be defined as the target for Grails url mappings.

Support for deep linking

Tide now includes a simple plugin to easily handle deep linking.
Tide components annotated with [Path] are marked as handlers for browser url changes and can take appropriate actions.

The application can be accessed at http://localhost:8080/{myapp}/flexindex.gsp.
The UI builder supports most Grails constraints on properties and the generated application is comparable in functionality to the html Grails scaffolding.

Currently supported relationships are :

oneToOne/manyToOne: Displayed as a ComboBox, requires a controller for the associated entity (to get the list of possible values).

bidirectional oneToMany: Displayed as an editable DataGrid, requires that the associated entity and owner implement particular constructors. For example Book hasMany => Chapter requires that Book has a constructor Book() that initializes the chapters collections with chapters = new ArrayCollection() and Chapter has a constructor Chapter(book:Book = null).

unidirectional oneToMany / manyToMany: Displayed as a DataGrid with a list of possible values that can be dragged in the grid, requires a controller for the associated entity

Lazy associations are supported for oneToMany and manyToMany relationships, but not for manyToOne or oneToOne that must be marked lazy:false.

The generated as3 constraints map contains everything that is present in the Grails ConstrainedProperty, but not all are currently used by the UI generator.

display:false => field not displayed

editable:false => field not editable (for now works only with oneToMany and manyToMany)

inCreate:false => field not present in create view

inEdit:false => field not present in edit view

inList:"[val1,val2,val3]" => use ComboBox with specified values for string fields

More information and documentation on the UI builder will be available later.

Integrating with Spring security

The integration can be done with either the acegi plugin or the spring-security-core plugin. These plugins are automatically detected and GraniteDS is automatically configured to propagate the security credentials from Flex RemoteObject to Spring security.

Data Push with Gravity

For Grails 1.4:
The NIO option should be committed to core, no need to install the plugin.

You also have to set the option gravityEnabled = true in the graniteConfig and otherwise follow the instructions in the GDS doco for Data Push, such as configuring the channel in services-config.xml. Also check out this basic Grails + GDS + Gravity tutorial.

Enabling NIO or APR at deployment time is dependant upon the application server you deploy to.

Deploying on Google App Engine

The gdsflex plugin is able to detect that the app-engine plugin (0.8.2 minimum) has been enabled for a project and defines an appropriate GraniteDS setup (support of DataNucleus JDO/JPA entities instead of Hibernate).
To correctly run the application, it is though necessary to update manually the web-app/WEB-INF/flex/services-config.xml and remove the {context.root} from the channel endpoint : uri="http://{server.name}:{server.port}/graniteamf/amf" because GAE deploys in the root context.

Using Grails and Google App Engine is generally very far from a pleasant experience (often due to GAE), but the following recommendations can limit problems :

Always define the domain classes in packages (the default package will not work)

Use the Key class for primary keys

gdsflex requires that the primary key is named 'id' (like GORM domain classes)

Use preferably JDO than JPA (except if you like extreme S/M experiments).