GraniteDS Flex Plugin
Dependency :
compile ":gdsflex:0.9.0"
Summary
Installation
Installation
Use this from the project folder:grails install-plugin gdsflex
- the AS3 generator :
grails gas3
- The Flex compilation
grails mxmlc
Plugin history
0.9.0 (28/10/2011)
- Upgraded GDS libs to 2.3.0
- Fixed mxmlc compilation broken in Grails 1.3.x
- Added support for Flex 4.5+
- See full release notes here
0.8.5 (21/01/2011)
- Upgraded GDS libs to 2.2.0 SP1
- Fixed issue with services-config.xml when using Flash builder with external Flex project
0.8.4 (15/11/2010)
- Fixed stupid blocking bug when no security plugin is installed (J. Fletcher)
- Added excludedClasses option to exclude classes from gas3 generation
- Hackich workaround to fix typesafe remoting to services after class reloading
- Updated GDS libs to pre-2.2.0.final
0.8.3 (08/10/2010)
- Improved interoperability between html and Flex authentication
- Support for controller authorization with @Secured / requestmap
- autoCompileFlex option not enforced in production mode (J. Fletcher)
0.8.2 (17/09/2010)
- Updated GDS libs to 2.2 RC2
- Fixed support for transactional services proxied with cglib
- Fixed generation of typesafe as3 proxies for services
- Repackaging of generator integration to prevent classloading issues
0.8.1 (02/08/2010)
- Updated GDS libs (with critical bug fix on Spring Security integration)
- Fixed issue with compiler not including libraries from web-app/WEB-INF/flex/libs and support optional flex_libs folder for Flex libraries.
- Added support for generation of typesafe client proxies for Grails services
0.8.0 (05/07/2010)
- Updated GDS lib to GDS 2.2 beta1
- Support for Grails 1.2+ and 1.3+
- Support for Flex 4+, complete rewrite of the Flex compilation part of the plugin and various new options
- Removed built-in Flex SDK to allow use of any Flex SDK version
- Support for spring-security-core plugin
0.7.2 (27/09/2009)
- Fixed plugin compilation on JDK 5
- Updated GDS libs
0.7.1 (20/08/2009)
- Fixed support for controllers with package names in Flex application generator
0.7 (11/08/2009)
- Improved as3 template for domain classes including Grails constraints and association information
- Added support for byte[] and Blob properties
- Added scaffolding templates for gdsflex-enabled controllers (installed with grails install-flex-templates), including support for file upload/download from Flex
- Added html-wrapper script to generate an html wrapper (web-app/app.html or grails-app/views/flexindex.gsp)
- Support for Flex deep linking with [Path] annotation in Tide components
- Generation of a Flex application from the domain classes with grails generate-flex-app using client-side dynamic UI builder
0.6.2 (16/07/2009)
- Fixed as3 template for domain classes
- Fixed support of gas3 extraClasses
0.6.1 (13/07/2009)
- Updated GDS libraries to simplify Grails externalization
- Fixed broken support for GAE
- Fixed serialization of inherited domain classes
- Improved fix for GORM events
- Generation of Grails constraints in the AS3 classes
0.6 (09/07/2009)
- Upgrade to GraniteDS 2.0 SP1 libraries
- Restored Flex autoCompile
- Initial support for app-engine plugin and deployment to Google App Engine
- Completely refactored handling of Grails domain classes
0.5 (22/06/2009)
- Upgrade to GraniteDS 2.0 GA libraries(see more details)
- add mxmlc command:for example,grails mxmlc main.mxml or grails mxmlc main or grails mxmlc(will find all application and modules)
- remove the auto-compilation for mxml/as etc.
0.4 (19/05/2009)
- Upgrade to GraniteDS 2.0 RC1 libraries
- Changed Flex compilation to compile in background in the grails-app/views/flex folder instead of using the web compiler
- Support for pure GORM domain classes without JPA annotations
0.3.2 (13/04/2009)
- fix the runtime error on windows
0.3.1 (12/04/2009)
- Add domain support for JPA jar(configured in GraniteDSConfig.groovy:as3Config.domainJar)
- Compile(incrementally) the mxml file from HTTP request to grails pacakage and monitor the modifying
- mxml files are now put into grails-app/views/mxml,the swf files are compiled into views/swf
0.3 (06/04/2009)
- Add support for Spring security authorizations (similar to ifAllGranted / ifAnyGranted tags)
- Add support for Gravity server push and multiuser data updates
- More info here
0.2 (26/03/2009)
- Bug fixes in GDS core in handling of Grails controllers
- Support for Spring validation errors
- Web compiler now detects changes on ActionScript files
0.1 (16/03/2009)
- Initial release
Description
GraniteDS Flex Plugin
This plugin provides integration between Grails and Adobe Flex using Granite Data ServicesGraniteDS
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 examplecd examplegrails install-plugin gdsflex
grails create-domain-class Persongrails create-controller Persongrails create-service Person
package exampleclass Person { String uid String firstName String lastName }
package exampleclass PersonController { def scaffold = Person
}package exampleimport org.granite.tide.annotations.TideEnabled@TideEnabled class PeopleService { static transactional = true def list() { return Person.list() } }
<?xml version="1.0" encoding="utf-8"?><mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" preinitialize="Spring.getInstance().initApplication()"> <mx:Script> import mx.collections.ArrayCollection; import org.granite.tide.spring.Spring; import org.granite.tide.spring.Context; import org.granite.tide.events.TideResultEvent; import example.Person; private var tideContext:Context = Spring.getInstance().getSpringContext(); private function listPeople():void { tideContext.personService.list(listResult); } private function listResult(event:TideResultEvent):void { people = event.result as ArrayCollection; } private var dummyPerson:Person; [Bindable] public var people:ArrayCollection = new ArrayCollection(); </mx:Script> <mx:Button label="List people" click="listPeople()"/> <mx:DataGrid dataProvider="{people}"> <mx:columns> <mx:DataGridColumn dataField="firstName"/> <mx:DataGridColumn dataField="lastName"/> </mx:columns> </mx:DataGrid></mx:Application>
grails gas3grails mxmlc
grails run-app
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 :as3Config {
// Enable automatic compilation (should be false when using Flex/Flash Builder)
autoCompileFlex = true
// Override source folder (relative to Grails application path)
srcDir = "./grails-app/flex"
// List of extra files to compile as separate swfs.
modules = [ "myModule.mxml", "myStylesheet.css" ]
// Path of an external jar containing domain classes to scan (for Gas3 generation)
domainJar = null
// Qualified names of extra Java/Groovy classes to generate to Flex classes
extraClasses = []
// Qualified names of Java classes to exclude from gas3 generation
excludeClasses = []
}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.In summary, your Flex compilation options should look like :-locale en_US -include-libraries flex_libs/granite-essentials.swc -services web-app/WEB-INF/flex/services-config.xml -context-root /example -keep-as3-metadata=Bindable,Managed,ChangeEvent,NonCommittingChangeEvent,Transient, Name,In,Out,Observer,ManagedEvent,Destroy,Id,Version
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()
}
}<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" preinitialize="Spring.getInstance().initApplication()"> <mx:Script> import mx.collections.ArrayCollection; import org.granite.tide.spring.Spring; import org.granite.tide.spring.Context; import org.granite.tide.events.TideResultEvent; private var tideContext:Context = Spring.getInstance().getSpringContext(); private function listPeople():void { tideContext.peopleService.list(listResult); } private function listResult(event:TideResultEvent):void { people = event.result as ArrayCollection; } private var dummyPerson:Person; [Bindable] public var people:ArrayCollection = new ArrayCollection(); </mx:Script> <mx:Button label="List people" click="listPeople()"/> <mx:DataGrid dataProvider="{people}"> <mx:columns> <mx:DataGridColumn dataField="firstName"/> <mx:DataGridColumn dataField="lastName"/> </mx:columns> </mx:DataGrid></mx:Application>
- 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.
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" preinitialize="Spring.getInstance().initApplication()"> <mx:Script> import mx.collections.ArrayCollection; import org.granite.tide.Component; import org.granite.tide.spring.Spring; import org.granite.tide.events.TideResultEvent; [In] public var peopleService:Component; private function listPeople():void { peopleService.list(listResult); } private function listResult(event:TideResultEvent):void { hello = event.result as ArrayCollection; } private var dummyPerson:Person; [Bindable] private var people:ArrayCollection = new ArrayCollection(); </mx:Script> <mx:Button label="List people" click="listPeople()"/> <mx:DataGrid dataProvider="{people}"> <mx:columns> <mx:DataGridColumn dataField="firstName"/> <mx:DataGridColumn dataField="lastName"/> </mx:columns> </mx:DataGrid></mx:Application>
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" preinitialize="Spring.getInstance().initApplication()"> <mx:Script> import mx.collections.ArrayCollection; import org.granite.tide.Component; import org.granite.tide.spring.Spring; import org.granite.tide.events.TideResultEvent; [Inject] public var myService:PeopleService; private function listPeople():void { myService.list(listResult); } private function listResult(event:TideResultEvent):void { hello = event.result as ArrayCollection; } private var dummyPerson:Person; [Bindable] private var people:ArrayCollection = new ArrayCollection(); </mx:Script> <mx:Button label="List people" click="listPeople()"/> <mx:DataGrid dataProvider="{people}"> <mx:columns> <mx:DataGridColumn dataField="firstName"/> <mx:DataGridColumn dataField="lastName"/> </mx:columns> </mx:DataGrid></mx:Application>
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.import org.granite.tide.annotations.TideEnabled@TideEnabled
class PeopleController { List people def list = {
people = Person.list()
}
}<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" preinitialize="Spring.getInstance().initApplication()"> <mx:Script> import mx.collections.ArrayCollection; import org.granite.tide.Component; import org.granite.tide.spring.Spring; [In] public var peopleController:Component; private var dummyPerson:Person; [Bindable] [In] public var people:ArrayCollection; </mx:Script> <mx:Button label="List people" click="peopleController.list()"/> <mx:DataGrid dataProvider="{people}"> <mx:columns> <mx:DataGridColumn dataField="firstName"/> <mx:DataGridColumn dataField="lastName"/> </mx:columns> </mx:DataGrid></mx:Application>
import org.granite.tide.annotations.TideEnabled@TideEnabled
class PeopleController { List people def list = {
people = Person.findByName(params.searchString)
}
}import org.granite.tide.annotations.TideEnabled@TideEnabled
class PeopleController { def list = {
[ people: Person.findByName(params.searchString) ]
}
}<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" preinitialize="Spring.getInstance().initApplication()"> <mx:Script> import mx.collections.ArrayCollection; import org.granite.tide.Component; import org.granite.tide.spring.Spring; [In] public var peopleController:Component; private var dummyPerson:Person; [Bindable] [In] public var people:ArrayCollection = new ArrayCollection(); </mx:Script> <mx:TextInput id="tiText"/> <mx:Button label="List people" click="peopleController.list({searchString: tiText.text})"/> <mx:DataGrid dataProvider="{people}"> <mx:columns> <mx:DataGridColumn dataField="firstName"/> <mx:DataGridColumn dataField="lastName"/> </mx:columns> </mx:DataGrid></mx:Application>
[In("myapp.peopleController")] public var peopleController:Component;
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.class Person { String uid String name Set<Contact> contacts
static hasMany = [contacts:Contact]
static mapping = {
contacts cascade:"all,delete-orphan"
}
}class Contact { String uid Person person String email
}<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
xmlns:gv="org.granite.tide.validators.*"
layout="vertical"
backgroundGradientColors="[#0e2e7d, #6479ab]"
preinitialize="Spring.getInstance().initApplication()"> <mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import org.granite.tide.spring.Spring;
import org.granite.tide.events.TideResultEvent;
import org.granite.tide.events.TideFaultEvent;
import Person;
import Contact; [Bindable] [In]
public var peopleController:Object; [Bindable] [In]
public var people:ArrayCollection; public var dummyPerson:Person;
public var dummyContact:Contact; private function getList():void {
peopleController.list();
}
]]>
</mx:Script> <mx:VBox width="100%">
<mx:HBox>
<mx:Button id="bList" label="Get list" click="getList()"/>
</mx:HBox> <mx:HBox width="100%">
<mx:DataGrid id="dgPeople"
dataProvider="{people}"
change="dgContacts.dataProvider
= dgPeople.selectedItem.contacts">
<mx:columns>
<mx:DataGridColumn dataField="name"/>
</mx:columns>
</mx:DataGrid> <mx:DataGrid id="dgContacts">
<mx:columns>
<mx:DataGridColumn dataField="email"/>
</mx:columns>
</mx:DataGrid>
</mx:HBox>
</mx:VBox></mx:Application>Using paged data
Tide provides the PagedQuery client component that handles client data paging and interaction with a Grails service/controller that executes queries.The service must implement a find method with the following signature:Map<String, Object> find( Map<String, Object> filter, // map containing criteria int first, // Index of first requested element int max, // Max number of elements String order, // order field boolean desc // true when descendent sorting )
- resultCount
- resultList
- firstResult (identical to first)
- maxResults
import org.granite.tide.annotations.TideEnabled@TideEnabled class PeopleService { def find(filter, first, max, order, desc) { if (max == 0) max = 36 List resultList = Person.list(max: max, offset: first, sort: order, order: (desc ? "desc" : "asc") ) int resultCount = Person.count() return [ resultList: resultList, resultCount: resultCount ] } }
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
xmlns:gv="org.granite.tide.validators.*"
layout="vertical"
backgroundGradientColors="[#0e2e7d, #6479ab]"
preinitialize="Spring.getInstance().initApplication()"> <mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import org.granite.tide.spring.Spring;
import org.granite.tide.spring.PagedQuery;
import org.granite.tide.events.TideResultEvent;
import org.granite.tide.events.TideFaultEvent;
import Person;
import Contact; Spring.getInstance().addComponentWithFactory(
"peopleService",
PagedQuery, { maxResults: 36 }); [Bindable] [In]
public var peopleService:PagedQuery; private var dummyPerson:Person; private function getList():void {
people.fullRefresh();
}
]]>
</mx:Script> <mx:VBox width="100%">
<mx:HBox>
<mx:Button id="bList"
label="Get list"
click="getList()" />
</mx:HBox> <mx:HBox width="100%">
<mx:DataGrid id="dgPeople"
dataProvider="{peopleService}">
<mx:columns>
<mx:DataGridColumn dataField="name"/>
</mx:columns>
</mx:DataGrid>
</mx:HBox>
</mx:VBox></mx:Application>- The client PagedQuery component can be used as a data provider for Flex UI components.
import org.granite.tide.annotations.TideEnabled@TideEnabled class PersonController { def list = { if (params.max == 0) params.max = 36 List resultList = Person.list(params) int resultCount = Person.count() return [ resultList: resultList, resultCount: resultCount ] } }
import org.granite.tide.annotations.TideEnabled@TideEnabled
class PersonController { def scaffold = Person
}<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*"
xmlns:gv="org.granite.tide.validators.*"
layout="vertical"
backgroundGradientColors="[#0e2e7d, #6479ab]"
preinitialize="Spring.getInstance().initApplication()"> <mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import org.granite.tide.spring.Spring;
import org.granite.tide.spring.PagedQuery;
import org.granite.tide.events.TideResultEvent;
import org.granite.tide.events.TideFaultEvent;
import Person;
import Contact; Spring.getInstance().addComponentWithFactory(
"people",
PagedQuery, {
useGrailsController: true,
remoteComponentName: "personController",
maxResults: 36
}
); [Bindable] [In]
public var people:PagedQuery; private var dummyPerson:Person; private function getList():void {
people.fullRefresh();
}
]]>
</mx:Script> <mx:VBox width="100%">
<mx:HBox>
<mx:Button id="bList"
label="Get list"
click="getList()" />
</mx:HBox> <mx:HBox width="100%">
<mx:DataGrid id="dgPeople"
dataProvider="{people}">
<mx:columns>
<mx:DataGridColumn dataField="name"/>
</mx:columns>
</mx:DataGrid>
</mx:HBox>
</mx:VBox></mx:Application>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 withgrails 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
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 plugin can be setup in the main application by :import org.granite.tide.deeplinking.TideUrlMapping;
…
Spring.getInstance().addPlugin(TideUrlMapping.getInstance());[Path("book")] public class BookUI { [Path("list")] public function listHandler():void { // Do something interesting, for example show the relevant list view } }
[Path("book")] public class BookUI { [Path("show/{id}")] public function showHandler(id:Number):void { // Do something interesting, for example show the requested book } }
Flex application generator
Gdsflex includes a simple Flex application generator driven by the domain classes. The UI is dynamically built using the included Tide UI builder library.The process to generate a Flex app is the following :// Create and edit the domain classes grails create-domain-class com.myapp.Domain1 grails create-domain-class com.myapp.Domain2 ...// Create and edit the corresponding controllers with scaffolding // (don't forget to add the @TideEnabled annotation) grails create-controller com.myapp.Domain1 grails create-controller com.myapp.Domain2 ...// Generate the as3 domain classes grails gas3// Generate the main Flex application grails generate-flex-app// Compile the Flex application and optionally generate an html wrapper grails mxmlc grails html-wrapper// Run the application grails run-app
- 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
- 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
- widget:"textArea" => use TextArea instead of TextInput for strings
- widget:"image" => use Image instead of download button for ByteArray / Blob
- format:"DD/MM/YYYY" => formatString in dateFormatter for Date fields
- format: "2" => precision in numberFormatter for Number fields
[Name("tideUIBuilder")] public class MyGlobalUIBuilder extends DefaultUIBuilder { protected override function buildEditFormItem(property:Object, create:Boolean):EntityProperty { if (property.name == "someProperty") { var entityProperty:EntityProperty = new EntityProperty(); entityProperty.property = property.name; entityProperty.bound = false; … return entityProperty; } else return super.buildEditFormItem(property, create); } }
[Name("com.myapp.author.tideUIBuilder")] public class AuthorUIBuilder extends DefaultUIBuilder { protected override function buildEditFormItem(property:Object, create:Boolean):EntityProperty { if (property.name == "someProperty") { var entityProperty:EntityProperty = new EntityProperty(); entityProperty.property = property.name; entityProperty.bound = false; … return entityProperty; } else return super.buildEditFormItem(property, create); } }
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.More details and documentation on the use of the GraniteDS Tide framework for Spring can be found on the GDS site at http://www.graniteds.org/confluence/display/DOC20/6.+Tide+Data+Framework.Data Push with Gravity
To get this working at development time, in Config.groovy set:grails.tomcat.nio = trueuninstall-plugin tomcat install-plugin tomcatnio
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).
package org.gdsgaegrails.entityimport javax.jdo.annotations.* import com.google.appengine.api.datastore.Key@PersistenceCapable(identityType=IdentityType.APPLICATION, detachable="true") class Task { static constraints = { } @PrimaryKey @Persistent(primaryKey="true", valueStrategy=IdGeneratorStrategy.IDENTITY) Key id @Persistent String uid @Persistent String description }
Options
If you create a file called GraniteDSConfig.groovy in your grails-app/conf folder you may put the following configuration in (defaults shown below):graniteConfig {
springSecurityAuthorizationEnabled = false
springSecurityIdentityClass = Identity.class gravityEnabled = false
gravityServletClassName = "org.granite.gravity.tomcat.GravityTomcatServlet" dataDispatchEnabled = false // see part 3 of
// http://graniteds.blogspot.com/2010/07/flex-grails-crud-application-with.html for example
}as3Config {
// a list of additional classes to generate with gas3 (e.g. non domain classes in the java or groovy folders).
extraClasses = []
// the path of a jar containing additional classes to generate with gas3
domainJar = null
// whether the plugin should compile your Flex project for you when the application runs.
autoCompileFlex = true
// where to output the generated as3 files.
srcDir = "grails-app/views/flex"
}