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.

Friday, July 27, 2007

Java Summer Camp in The Netherlands with Rails and Grails



This years Profict Java Summer Camp will host both Charles Nutter and Graeme Rocher to speak about JRuby and Grails.

This Java Summer Camp is hold on August 24 in The Netherlands. I will be there myself and I'm looking forward to meet other Grails users. Exciting!

If you are interested you can find more information on http://javasummercamp.nl.

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.

Sunday, March 25, 2007

Google Personalized Homepage




I think a week ago or something I enabled the Google Personlized Homepage, which resulted in a homepage full of news feeds relevant to my locale settings. This weekend I customized my homepage, by removing and adding Google Gadgets. I ended up with a single homepage containing a couple of news feeds of pages I normally read, a ToDO list, simple GMail & Google Calendar integration, localized weather news, Babelfish translation and a Wikipedia search box. And all of this on just 1 page!

The Google Gadgets directory contains a lot of useful Gadgets and news feeds. If the RSS or Atom feed is not in the directory (yet), you can also directly add an item to your personlized page by pointing to a RSS or Atom url.

Off course I created a new personlized page containing all the news feeds related to Grails, so I can see the latest activity very easily ;-)

If you want to add them to your own personlized page just click the links below:

Grails Announcements Add to Google

Grails User List Add to Google

Grails Dev List Add to Google

Grails JIRA Activity Add to Google

Grails Build Status Add to Google

Monday, March 12, 2007

Grails Frappr Map

Recently I've created a Grails Frappr Map to see where the Grails users and committers are located. It's just nice to know where people you talk to on the mailing lists live. And maybe you will find out your neighbor is also using Grails!


Monday, February 26, 2007

Getting Dynamic with Grails' ExpandoMetaClass



In Grails 0.4 the ExpandoMetaClass was introduced that allows you to dynamically add methods, constructors, properties and static methods using a neat closure syntax.

A posting on the Grails User Forum today (see http://www.nabble.com/Newbie---Need-to-implement-a-soft-) gave me a perfect example to explain and show the power of what is possible with this ExpandoMetaClass.

The question on the forum was how the dynamic delete method could be overridden by a 'soft' delete. This meaning a deleted flag set to true and the record to be saved.

In this example I'm not implementing this exact functionality. It just gave me the idea for this post. Imagine you never want to delete records from your databse, but always want just want to mark them. For example to implement some Recycle Bin functionality in which you can later restore records or delete them forever.


What you would need is to define a dynamic "softDelete()" method for all domain classes which sets the deleted flag and updates the record. Actually with the ExpandoMetaClass it's so simple.

In the init closure of the ApplicationBootStrap.groovy class you can do some bootstrapping for your application. This bootstrap class is often used to insert some "demo" records. For example the simple-cms sample application included in the Grails distribution does this. But wihtin this bootstrap class you also use the ExpandoMetaClass to define dynamic methods. See the code below:



import org.codehaus.groovy.grails.commons.metaclass.*
import org.codehaus.groovy.grails.commons.GrailsApplication

class ApplicationBootStrap {

def init = { servletContext ->

// get application
def application = servletContext.getAttribute(GrailsApplication.APPLICATION_ID)

// define softDelete() method for all domain classes
application.domainClasses.each { dc ->
dc.metaClass.softDelete << { ->
delegate.deleted = true
delegate.save()
}
}
}
def destroy = {
}
}

Note that the code above assumes that all domain classes have a "deleted" boolean property. This could be added to all domain classes or perhaps been added dynamically. But it's out of scope for this example.
To check if it's working you can do some test like:


// create new book
new Book(title:'The Definitive Guide to Grails').save()

// retrieve saved book
def savedBook = Book.get(1)
assert savedBook.deleted == false

// now softDelete the book
savedBook.softDelete()

// retrieve it again
def softDeletedBook = Book.get(1)
assert savedBook.deleted == true

Wednesday, January 31, 2007

Grails 0.4 Released!



Grails 0.4 has been released today! You can download it from: http://grails.org/Download

Notable improvements include:

Read the full announcement at http://www.nabble.com/-groovy-user--Grails-0.4-Released!-tf3150030.html. Change log at http://jira.codehaus.org/browse/GRAILS?report=com.atlassian.jira.plugin.system.project:changelog-panel.