
Last week Marc Palmer announced his Grails Documentation Widget. To bad it was an OS X Dashboard Widget so I couldn't use it on my Windows based machine. Marc already suggested to buy a decent OS... Maybe my boss is reading this someday and buys me an Apple ;-)
When the widget was released I was playing a little bit with Adobe AIR and I decided to port the widget to - or better, create something similar in - Adobe AIR.
It gives you the power to do an incremental search on dynamic methods, tags, scripts etc.
Have a look at http://www.grails.org/Peek+in+Grails for more info and download instructions.
Monday, August 4, 2008
Peek in Grails
Posted by
Marcel Overdijk
at
11:56 AM
9
comments
Wednesday, July 30, 2008
Flex Spaghetti

We were playing internally with a Flex project and after our first Scrum sprint we ended up with a real nice looking frontend, ... but also with Flex Spaghetti! All kind of events were bubbling up and down through the application.
After searching the internet I noticed a lot of people are having the same problems. Using a framework or a blueprint architecture should help us. In Java we have a lot of frameworks to choose from, each with it's advantages and disadvantages. On the Flex side the number of mature frameworks is limited. For me this is a sign that (professional) Flex development is in it's early stages. However there are some frameworks. Luke Bayes and Ali Mills made a nice comparison of the available frameworks. Cairngorm and PureMVC scored best in this comparison and so I tried them both. I prefer PureMVC as it's much cleaner using the Mediator pattern. This allows to send event backs to the view eaily and clean as well.
In my free time I also started to experiment a little bit with AIR and it's embedded SQLite database engine. This is useful for storing local data in offline mode but also for creating small stand-alone applications like a personal Movie database. When talking to a database I'm used (and spoiled ;-) to Grails ORM (GORM) or Spring's JDBCTemplate and HibernateTemplate. There is nothing like this available in Flex/ActionScript. To write GORM in ActionScript is not an option, but to port Spring's JdbcTemplate to a SqliteTemplate in ActionScript was doable on a nice summer evenning. I'm currently in talks with Cliff Hall of the PureMVC project to add this as an utility to the PureMVC project. If you are interested in in the meantime, just leave your email address and I will send you a copy.
Wednesday, July 9, 2008
Grails for creating quick mock applications

Currently I'm working on an Adobe Flex front-end which will communicate to Microsoft Team Foundation Server using some SOAP webservices.
As we are currently just concentrating on the Flex front-end, there are no TFS webservices available yet. This makes testing the Flex front-end somehow difficult. To make things easy we wanted to create some mock TFS webservices. This is when Grails comes is!
Within the hour we had the mock application running. Creating some basic domain classes, installing the xfire plugin and creating some services exposed as xfire webservices. And for verifying the data is stored correctly in the database we just used the generate-all command to create some basic CRUD views.
I know the Grails mock application will not see the production light, but creating quick mock applications for temporary usage is another thing in which Grails can be used very effectively.
Posted by
Marcel Overdijk
at
11:12 PM
2
comments
Wednesday, January 30, 2008
Code by convention with Flex and Spring
Some years ago XML configuration was considered as a great way to configure applications. Take for example Struts' struts-config.xml, in the beginning it was like heaven how you could define all your action mappings etc. But as applications grow it gets harder and harder to maintain this bunch of XML.
The point is applications and application frameworks evolve. In modern application frameworks code by convention is becoming an important feature which reduces repetition and increases uniformity and productivity.
Take for example Grails and Ruby on Rails which are build from the ground up with code by convention in mind. For example in Grails a *Controller.groovy class is a automatically a controller and a *Service.groovy class is automatically a service. You do not need any XML configuration.
Back in my holidays in December I tried Adobe's BlazeDS. From Adobe Labs: BlazeDS is the server-based Java remoting and web messaging technology that enables developers to easily connect to back-end distributed data and push data in real-time to Adobe Flex and Adobe AIR applications for more responsive rich Internet application (RIA) experiences.
What this means is that Flex clients can communicate with Java objects deployed on the server. BlazeDS contains a Java Adapter which forms the infrastructure to make this possible. With Jeff Vroom's Spring Integration you can even use Spring beans to communicate with.
The only drawback with the standard Java Adapter (and so also the Spring Integation) is that each Java object you want to call on the server, you need to configure in remoting-config.xml. Guess what? I don't like that! So what I did is I created a JDK5 annotation to autowire Spring beans directly into BlazeDS. It means when you use Spring and JDK5 you can annotate your service classes with a @RemotingDestination annotation, and they get automatically registered in BlazeDS. Some sort of code by convention I would say!
How it works:
- Download the @RemotingDestination annotation for Spring supporting files at http://www.adobe.com/cfusion/exchange/index.cfm?event=extensionDetail&loc=en_us&extid=1398018. It also includes Jeff Vroom's Spring Factory.
- Expand remotingdestination-annotation.zip
- Copy dist\remotingdestination-annotation.jar in the WEB-INF\lib directory of your web application. Instead you could also include the Java sources (located in src) in your project.
- Download the Spring framework at http://www.springframework.org/download
- Locate spring.jar in the dist directory and copy it in the WEB-INF\lib directory of your web application
- In your Flex services-config.xml configure the SpringAutowiringBootstrapService:
The SpringAutowiringBootstrapService checks all @RemotingDestination annotated classes and registers them within BlazeDS at runtime. It also dynamically creates the Spring Factory.
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service-include file-path="remoting-config.xml" />
<service-include file-path="proxy-config.xml" />
<service-include file-path="messaging-config.xml" />
<service id="spring-autowiring-bootstrap-service" class="flex.contrib.services.SpringAutowiringBootstrapService"/>
<default-channels>
<channel ref="my-amf"/>
</default-channels>
</services>
....
Note that the default channel is required when using runtime registration of remoting destinations.
- Finally annotate your service classes with the @RemotingDestination annotation:
package flex.contrib.samples.mortgage;
import org.springframework.beans.factory.annotation.Autowired;
import flex.contrib.stereotypes.RemotingDestination;
@RemotingDestination(destination="mortgageService")
public class Mortgage {
@Autowired
RateFinder rateFinder;
public void setRateFinder(RateFinder rateFinder) {
this.rateFinder = rateFinder;
}
public double calculate(double amount) {
int term = 360; // 30 years
double rate = rateFinder.findRate();
return (amount*(rate/12)) / (1 - 1 /Math.pow((1 + rate/12), term));
}
}
By default the Spring bean name is used to register the remoting destination. The name can be overridden using the destination attribute of the annotation. The example class uses Spring annotation-based configuration using the @Autowired annotation. In this case the Spring application context would look like:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config/>
<context:component-scan base-package="flex.contrib.samples"/>
</beans>
That's it! The Mortgage service will be automatically registered without any XML configuration. The @RemotingDestination annotation allows you to easily expose services within BlazeDS. Together with Spring's annotation-based configuration this provides a productive code by convention experience for writing Flex applications with Spring on the server-side.
For more info about the @RemotingDestination annotation, SpringAutowiringBootstrapService see the Javadocs in docs\api.
Posted by
Marcel Overdijk
at
11:30 AM
25
comments
Tuesday, January 29, 2008
BlazeDS Test Drive sample in Grails

In my previous blog entry I introduced the Grails Flex Plugin. I got some reactions on this post and one of them was from Alexander Negoda (aka greendog), who had some problems getting BlazeDS Test Drive Sample 5: Updating data to work on Grails.
A good reason for me to implement the sample myself, and write a small tutorial!
- I assume you have Grails already installed and know the basics. This plugin requires requires the latest and greatest Grails 1.0-final development build. It is not compatible with Grails 1.0-RC4.
- Create a new Grails application by executing
grails create-app productfrom the Grails command line. - From within the
productapplication folder, install the Flex plugin by executing:grails install-plugin flex
All Flex libraries and configuration files will be copied into your application'sweb-appfolder. - Create the
Productdomain class by executing:grails create-domain-class product - Open
Product.groovyand add properties so your domain class looks like:
class Product {
String name
String description
String image
String category
Double price
Integer qtyInStock
} - Create the
Productservice by executing:grails create-service product - Open
ProductService.groovyand change the class so it looks like:
class ProductService {
static expose = ['flex-remoting']
def getProducts() {
return Product.list();
}
def update(Product product) {
def p = Product.get(product.id)
if (p) {
p.properties = product.properties
p.save()
}
}
}
Notice that we expose the service as aflex-remotingservice. - Now we are finished with the domain and service layer and we can focus on the Flex front-end. Within the application's
web-appfolder create a file calledmain.mxmland add the following content:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" layout="horizontal"
creationComplete="srv.getProducts()" viewSourceURL="srcview/index.html">
<mx:RemoteObject id="srv" destination="productService"/>
<mx:Panel title="Catalog" width="100%" height="100%">
<mx:DataGrid id="list" dataProvider="{srv.getProducts.lastResult}" width="100%" height="100%"/>
</mx:Panel>
<ProductForm product="{Product(list.selectedItem)}"/>
</mx:Application> - Within the application's
web-appfolder create a file calledProductForm.mxmland add the following content:
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*"
title="Details" width="100%" height="100%">
<Product id="product"
name="{productName.text}"
category="{category.text}"
price="{Number(price.text)}"
image="{image.text}"
description="{description.text}"/>
<mx:RemoteObject id="srv" destination="productService"/>
<mx:Form width="100%">
<mx:FormItem label="Name">
<mx:TextInput id="productName" text="{product.name}"/>
</mx:FormItem>
<mx:FormItem label="Category">
<mx:TextInput id="category" text="{product.category}"/>
</mx:FormItem>
<mx:FormItem label="Image">
<mx:TextInput id="image" text="{product.image}"/>
</mx:FormItem>
<mx:FormItem label="Price">
<mx:TextInput id="price" text="{product.price}"/>
</mx:FormItem>
<mx:FormItem label="Description" width="100%">
<mx:TextArea id="description" text="{product.description}" width="100%" height="100"/>
</mx:FormItem>
</mx:Form>
<mx:ControlBar>
<mx:Button label="Update" click="srv.update(product);"/>
</mx:ControlBar>
</mx:Panel> - Within the application's
web-appfolder create a file calledProduct.asand add the following content:
package
{
[Bindable]
[RemoteClass(alias="Product")]
public class Product
{
public function Product()
{
}
public var id:int;
public var name:String;
public var description:String;
public var image:String;
public var category:String;
public var price:Number;
public var qtyInStock:int;
}
}
Note that themain.mxml,ProductForm.mxmlandProduct.asfiles are for 99% a copy from the BlazeDS Test Drive sample. Inmain.mxmlandProductForm.mxmlonly thedestination idof theRemoteObjectwas changed. And inProduct.asthealiasof theRemoteClassand the name of theproductIdcolumn was changed. - Before we run the example, let's add some data to the embedded HSQLDB database. In your application's
BootStrap.groovyclass create some records as shown below:
class BootStrap {
def init = { servletContext ->
new Product(name: 'Nokia 6010', category: '6000', image: 'Nokia_6010.gif', price: 99.00, description: 'Easy to use without sacrificing style, the Nokia 6010 phone offers functional voice communication supported by text messaging, multimedia messaging, mobile internet, games and more.', qtyInStock: 21).save()
new Product(name: 'Nokia 3100 Blue', category: '9000', image: 'Nokia_3100_blue.gif', price: 109.00, description: 'Light up the night with a glow-in-the-dark cover - when it is charged with light you can easily find your phone in the dark. When you get a call, the Nokia 3100 phone flashes in tune with your ringing tone. And when you snap on a Nokia Xpress-on gaming cover, you will get luminescent light effects in time to the gaming action.', qtyInStock: 99).save()
new Product(name: 'Nokia 3100 Pink', category: '3000', image: 'Nokia_3100_pink.gif', price: 139.00, description: 'Light up the night with a glow-in-the-dark cover - when it is charged with light you can easily find your phone in the dark. When you get a call, the Nokia 3100 phone flashes in tune with your ringing tone. And when you snap on a Nokia Xpress-on gaming cover, you will get luminescent light effects in time to the gaming action.', qtyInStock: 30).save()
new Product(name: 'Nokia 3120', category: '3000', image: 'Nokia_3120.gif', price: 159.99, description: 'Designed for both business and pleasure, the elegant Nokia 3120 phone offers a pleasing mix of features. Enclosed within its chic, compact body, you will discover the benefits of tri-band compatibility, a color screen, MMS, XHTML browsing, cheerful screensavers, and much more.', qtyInStock: 10).save()
new Product(name: 'Nokia 3220', category: '3000', image: 'Nokia_3220.gif', price: 199.00, description: 'The Nokia 3220 phone is a fresh new cut on some familiar ideas - animate your MMS messages with cute characters, see the music with lights that flash in time with your ringing tone, download wallpapers and screensavers with matching color schemes for the interface.', qtyInStock: 20).save()
new Product(name: 'Nokia 3650', category: '3000', image: 'Nokia_3650.gif', price: 200.00, description: 'Messaging is more personal, versatile and fun with the Nokia 3650 camera phone. Capture experiences as soon as you see them and send the photos you take to you friends and family.', qtyInStock: 11).save()
new Product(name: 'Nokia 6820', category: '6000', image: 'Nokia_6820.gif', price: 299.99, description: 'Messaging just got a whole lot smarter. The Nokia 6820 messaging device puts the tools you need for rich communication - full messaging keyboard, digital camera, mobile email, MMS, SMS, and Instant Messaging - right at your fingertips, in a small, sleek device.', qtyInStock: 8).save()
new Product(name: 'Nokia 6670', category: '6000', image: 'Nokia_6670.gif', price: 319.99, description: 'Classic business tools meet your creative streak in the Nokia 6670 imaging smartphone. It has a Netfront Web browser with PDF support, document viewer applications for email attachments, a direct printing application, and a megapixel still camera that also shoots up to 10 minutes of video.', qtyInStock: 2).save()
new Product(name: 'Nokia 6620', category: '6000', image: 'Nokia_6620.gif', price: 329.99, description: 'Shoot a basket. Shoot a movie. Video phones from Nokia... the perfect way to save and share life\u2019s playful moments. Feel connected.', qtyInStock: 10).save()
new Product(name: 'Nokia 3230 Silver', category: '3000', image: 'Nokia_3230_black.gif', price: 500.00, description: 'Get creative with the Nokia 3230 smartphone. Create your own ringing tones, print your mobile images, play multiplayer games over a wireless Bluetooth connection, and browse HTML and xHTML Web pages. ', qtyInStock: 10).save()
new Product(name: 'Nokia 6680', category: '6000', image: 'Nokia_6680.gif', price: 222.00, description: 'The Nokia 6680 is an imaging smartphone that', qtyInStock: 36).save()
new Product(name: 'Nokia 6630', category: '6000', image: 'Nokia_6630.gif', price: 379.00, description: 'The Nokia 6630 imaging smartphone is a 1.3 megapixel digital imaging device (1.3 megapixel camera sensor, effective resolution 1.23 megapixels for image capture, image size 1280 x 960 pixels).', qtyInStock: 8).save()
new Product(name: 'Nokia 7610 Black', category: '7000', image: 'Nokia_7610_black.gif', price: 450.00, description: 'The Nokia 7610 imaging phone with its sleek, compact design stands out in any crowd. Cut a cleaner profile with a megapixel camera and 4x digital zoom. Quality prints are all the proof you need of your cutting edge savvy.', qtyInStock: 20).save()
new Product(name: 'Nokia 7610 White', category: '7000', image: 'Nokia_7610_white.gif', price: 399.99, description: 'The Nokia 7610 imaging phone with its sleek, compact design stands out in any crowd. Cut a cleaner profile with a megapixel camera and 4x digital zoom. Quality prints are all the proof you need of your cutting edge savvy.', qtyInStock: 7).save()
new Product(name: 'Nokia 6680', category: '6000', image: 'Nokia_6680.gif', price: 219.00, description: 'The Nokia 6680 is an imaging smartphone.', qtyInStock: 15).save()
new Product(name: 'Nokia 9300', category: '9000', image: 'Nokia_9300_close.gif', price: 599.00, description: 'The Nokia 9300 combines popular voice communication features with important productivity applications in one well-appointed device. Now the tools you need to stay in touch and on top of schedules, email, news, and messages are conveniently at your fingertips.', qtyInStock: 26).save()
new Product(name: 'Nokia 9500', category: '9000', image: 'Nokia_9500_close.gif', price: 799.99, description: 'Fast data connectivity with Wireless LAN. Browse the Internet in full color, on a wide, easy-to-view screen. Work with office documents not just email with attachments and memos, but presentations and databases too.', qtyInStock: 54).save()
new Product(name: 'Nokia N90', category: '9000', image: 'Nokia_N90.gif', price: 499.00, description: 'Twist and shoot. It is a pro-photo taker. A personal video-maker. Complete with Carl Zeiss Optics for crisp, bright images you can view, edit, print and share. Meet the Nokia N90.', qtyInStock: 12).save()
}
def destroy = {
}
} - Now run the application by executing
grails run-appand when started open a browser and navigate tohttp://localhost:8080/product/main.mxml

Now enjoy and look at the list of products retrieved during startup of the application. Navigate trough the product list and change the properties on the right side. Pushing the Update button will update the record in the database.
Note that updating causes a long stacktrace in the console window, but the update succeeds anyway. It's a StackOverflowError within HSQLDB; I tested the code on a MySQL database and then there is no error thrown.
Sunday, January 20, 2008
Flex on Grails: Introducing the Grails Flex plugin

I haven't touched this blog for the past months as life has been very busy since my son was born. But now it's time again, as I released the Grails Flex Plugin today.
With the plugin Grails services can be exposed as RPC remoting destinations within BlazeDS - Adobe's server-based Java remoting and messaging technology. These remoting destinations can be used in Flex rich internet applications to communicate with the server to restrieve or send data.
Great thing is you can use all nice features from Grails like reloading, GORM etc. and have a real RIA application on the front-end.
See Grails Flex Plugin for more information and a simple usage example.
Posted by
Marcel Overdijk
at
6:51 AM
24
comments
Wednesday, August 1, 2007
Another Sexy Flex Grails Example

Here is another (quick) Flex example which uses Grails on the back-end. It contains all CRUD operations in just one screen. Just follow the steps below to see it for yourself:
- Create a new grails application:
grails create-app flexongrails - Create a new Book domain class:
grails create-domain-class Book
Open the Book domain class and replace the code with:class Book {
String isbn
String title
String author
Float price
String format
static constraints = {
isbn(maxLength:20, unique:true)
title(maxLength:50)
author(maxLength:50)
price(min:0F, max:999F, scale:2)
format(inList:["Hardcover", "Paperback", "e-Book"])
}
} - Create a new Book controller:
grails create-controller Book
Open the Book controller class and replace the code with:class BookController {
def index = { redirect(action:list, params:params) }
// the delete, save and update actions only accept POST requests
// def allowedMethods = [delete:'POST', save:'POST', update:'POST']
def list = {
response.setHeader("Cache-Control", "no-store")
def bookList = Book.list(params)
render(contentType:"text/xml") {
data {
for(i in bookList) {
book {
id(i.id)
isbn(i.isbn)
title(i.title)
author(i.author)
price(i.price)
format(i.format)
}
}
}
}
}
def save = {
def book
if(params.id) {
book = Book.get(params.id)
}
else {
book = new Book()
}
book.properties = params
book.save()
render ""
}
def delete = {
def book = Book.get(params.id)
if(book) {
book.delete()
}
render ""
}
}
What you can see here is that thelistaction return xml data which will be used by Flex. Important is setting the cache control in the response header. Thesaveaction will be used both creating as editing. - Create a new Flex project (in Flex builder) and replace the code in the main mxml file with:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="listService.send()">
<mx:HTTPService id="listService" url="http://localhost:8080/flexongrails/book/list" useProxy="false" method="GET"/>
<mx:HTTPService id="saveService" url="http://localhost:8080/flexongrails/book/save" useProxy="false" method="POST" result="listService.send()">
<mx:request xmlns="">
<id>{book_id.text}</id>
<isbn>{isbn.text}</isbn>
<title>{title.text}</title>
<author>{author.text}</author>
<price>{price.text}</price>
<format>{format.text}</format>
</mx:request>
</mx:HTTPService>
<mx:HTTPService id="deleteService" url="http://localhost:8080/flexongrails/book/delete" useProxy="false" method="POST" result="listService.send()">
<mx:request xmlns="">
<id>{dg.selectedItem.id}</id>
</mx:request>
</mx:HTTPService>
<mx:NumberFormatter id="priceFormatter" precision="2"/>
<mx:Script>
<![CDATA[
[Bindable]
private var formatArray:Array = ["Hardcover", "Paperback", "e-Book"];
private function clearForm():void {
book_id.text = "";
isbn.text = "";
title.text = "";
author.text = "";
price.text = "";
format.selectedIndex = 0;
}
private function formatPrice(item:Object, column:DataGridColumn):String {
return priceFormatter.format(item.price);
}
]]>
</mx:Script>
<mx:VDividedBox x="0" y="0" height="100%" width="100%" paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10">
<mx:Panel width="100%" height="300" layout="absolute" title="Create/Update Book">
<mx:Form x="10" y="10" width="930" height="200">
<mx:FormItem label="ID">
<mx:TextInput width="120" id="book_id" text="{dg.selectedItem.id}" enabled="false"/>
</mx:FormItem>
<mx:FormItem label="ISBN">
<mx:TextInput width="220" id="isbn" text="{dg.selectedItem.isbn}" maxChars="20"/>
</mx:FormItem>
<mx:FormItem label="Title">
<mx:TextInput width="320" id="title" text="{dg.selectedItem.title}" maxChars="50"/>
</mx:FormItem>
<mx:FormItem label="Author">
<mx:TextInput width="320" id="author" text="{dg.selectedItem.author}" maxChars="50"/>
</mx:FormItem>
<mx:FormItem label="Price">
<mx:TextInput width="120" id="price" text="{priceFormatter.format(dg.selectedItem.price)}"/>
</mx:FormItem>
<mx:FormItem label="Format" width="220">
<mx:ComboBox id="format" selectedIndex="{formatArray.indexOf(dg.selectedItem.format)}">
<mx:dataProvider>{formatArray}</mx:dataProvider>
</mx:ComboBox>
</mx:FormItem>
</mx:Form>
<mx:ControlBar horizontalAlign="right">
<mx:Button label="New" click="clearForm()"/>
<mx:Button label="Save" click="saveService.send(); clearForm()"/>
</mx:ControlBar>
</mx:Panel>
<mx:Panel width="100%" height="444" layout="absolute" title="Book List">
<mx:DataGrid x="0" y="0" width="100%" height="100%" id="dg" dataProvider="{listService.lastResult.data.book}">
<mx:columns>
<mx:DataGridColumn width="120" headerText="ID" dataField="id"/>
<mx:DataGridColumn width="220" headerText="ISBN" dataField="isbn"/>
<mx:DataGridColumn width="320" headerText="Title" dataField="title"/>
<mx:DataGridColumn width="320" headerText="Author" dataField="author"/>
<mx:DataGridColumn width="120" headerText="Price" dataField="price" labelFunction="formatPrice"/>
<mx:DataGridColumn width="220" headerText="Format" dataField="format"/>
</mx:columns>
</mx:DataGrid>
<mx:ControlBar horizontalAlign="right">
<mx:Button label="Delete" click="deleteService.send()" enabled="{dg.selectedItem != null}"/>
</mx:ControlBar>
</mx:Panel>
</mx:VDividedBox>
</mx:Application> - Run the Flex application
You now have a basic CRUD Flex application which uses Grails as back-end. The example is very basic and the Grails Book controller isn't responding back any possible validation errors; they just get absorbed. Also for a couple of row retrieving all the rows at once is no problem, but in real life it will me thousands and thousands of records, so also server side paging and ordering is needed. Well, this could be easily implemented in the Book controller (as it is basic Grails functionality) but most of the work will go in the Flex application I think...
For server-side paging/sorting a custom Flex component would be needed which remembers the current page and just submits the required paging and sorting fields to the serve-side. This is independent to Grails as it could be used by any server-side application. I guess I'm not the first one looking into this so I might find something on the internet.
Thursday, July 26, 2007
Sexy Flexy Grails
With Flex you can create rich internet applications with highly interactive GUI's. It offers various methods for talking with server-side components to retrieve and store data. You can use simple HTTP GET/POST services, WEB services, RPC calls or Flex Data Services.
Using HTTP GET/POST or WEB services gives you the freedom to choose the server-side technology: PHP, Servlets, JSP, Ruby on Rails etc.
Mike Potter already has written a small tutorial about integrating Flex and PHP.
I will use Mike's tutorial to show you how easy it is to integrate Flex and Grails.
Prerequisites: I assume you have basic understanding of Flex and Grails and that you have installed them both already.
Where to start:
- First read Mike's tutorial about integrating Flex and PHP. After reading it you will understand that a simple PHP page is used to retrieve/return User records in XML data and to store new User records. In the next steps we will do the same, but then using Grails.
- It's time to create a new Grails application so from the command line type:
grails create-app sexyflexygrails
- Create the User domain class:
grails create-domain-class User
It's not needed to create the User table like in the PHP tutorial. Grails will automatically create the table when you run the application. - Add the Username and Emailaddress properties to the created domain class:
class User {
String username
String emailaddress
} - reate a new controller to retrieve and store data:
grails create-controller User
- Implement the needed logic in the created UserController:
When the index action is called it will check if the Username and Emailaddress parameters are in request parameters, and if so it will create a new User. In any case, the call to the index action will return all Users in the database as XML. Flex will use this XML to display the Users in a table.class UserController {
def index = {
if (params.username && params.emailaddress) {
def user = new User()
user.properties = params
user.save()
}
def userList = User.list()
render(contentType:"text/xml") {
users {
for(i in userList) {
user {
userid(i.id)
username(i.username)
emailaddress(i.emailaddress)
}
}
}
}
}
} - That's it for the server-side Grails part. Start the Grails application with:
grails run-app
- Open a browser and goto http://localhost:8080/sexyflexygrails/user. You will see no data as there is nothing in the database yet. Try adding a new User with http://localhost:8080/sexyflexygrails/user?username=MyUsername&emailaddress=MyUsername@MyCompany.com. This will create the new User in the database and will render the XML in the browser.
- Now start Flex Builder and create a new Flex project. Chooser Other/None as Server type.
- Open the automatically created main mxml file and copy in the MXML code from Mike's tutorial: http://www.adobe.com/devnet/flex/articles/flex2_php_03.html
- In the MXML code change the HTTPService url to: http://localhost:8080/sexyflexygrails/user
- Now run the Flex application and it will use Grails for retrieving and storing the data from the back-end.
In the next weeks I will have a further look at Flex, and see if I can create a small Flex CRUD application that uses Grails at the server-side. I will post my findings here, so stay tuned.
