Showing posts with label Flex. Show all posts
Showing posts with label Flex. Show all posts

Monday, August 4, 2008

Peek in Grails



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.

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.

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:


  1. 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.

  2. Expand remotingdestination-annotation.zip

  3. 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.

  4. Download the Spring framework at http://www.springframework.org/download

  5. Locate spring.jar in the dist directory and copy it in the WEB-INF\lib directory of your web application

  6. In your Flex services-config.xml configure the SpringAutowiringBootstrapService:

    <?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>

    ....
    The SpringAutowiringBootstrapService checks all @RemotingDestination annotated classes and registers them within BlazeDS at runtime. It also dynamically creates the Spring Factory.
    Note that the default channel is required when using runtime registration of remoting destinations.

  7. 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.

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!


  1. 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.

  2. Create a new Grails application by executing grails create-app product from the Grails command line.

  3. From within the product application folder, install the Flex plugin by executing: grails install-plugin flex
    All Flex libraries and configuration files will be copied into your application's web-app folder.

  4. Create the Product domain class by executing: grails create-domain-class product

  5. Open Product.groovy and add properties so your domain class looks like:

    class Product {
    String name
    String description
    String image
    String category
    Double price
    Integer qtyInStock
    }

  6. Create the Product service by executing: grails create-service product

  7. Open ProductService.groovy and 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 a flex-remoting service.

  8. Now we are finished with the domain and service layer and we can focus on the Flex front-end. Within the application's web-app folder create a file called main.mxml and 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>

  9. Within the application's web-app folder create a file called ProductForm.mxml and 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>

  10. Within the application's web-app folder create a file called Product.as and 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 the main.mxml, ProductForm.mxml and Product.as files are for 99% a copy from the BlazeDS Test Drive sample. In main.mxml and ProductForm.mxml only the destination id of the RemoteObject was changed. And in Product.as the alias of the RemoteClass and the name of the productId column was changed.

  11. Before we run the example, let's add some data to the embedded HSQLDB database. In your application's BootStrap.groovy class 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 = {
    }
    }

  12. Now run the application by executing grails run-app and when started open a browser and navigate to http://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.

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:


  1. Create a new grails application: grails create-app flexongrails

  2. 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"])
    }
    }

  3. 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 the list action return xml data which will be used by Flex. Important is setting the cache control in the response header. The save action will be used both creating as editing.

  4. 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>

  5. 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



Competition in the RIA space is heating up lately. Adobe Flex is already here for some time, and with the announcement of JavaFX and Microsoft Silverlight recently a real RIA WAR has been started. I had a look af Adobe Flex some time ago and I must say my first impression is WOW! These Flash/Flex applications feel so natural!

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:
  1. 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.
  2. It's time to create a new Grails application so from the command line type:
    grails create-app sexyflexygrails
  3. 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.

  4. Add the Username and Emailaddress properties to the created domain class:
    class User { 
    String username
    String emailaddress
    }
  5. reate a new controller to retrieve and store data:
    grails create-controller User
  6. Implement the needed logic in the created UserController:
    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)
    }
    }
    }
    }
    }
    }
    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.
  7. That's it for the server-side Grails part. Start the Grails application with:
    grails run-app
  8. 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.
  9. Now start Flex Builder and create a new Flex project. Chooser Other/None as Server type.
  10. 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
  11. In the MXML code change the HTTPService url to: http://localhost:8080/sexyflexygrails/user
  12. 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.