Thursday, November 20, 2008

RestoreState Plugin Released



After some posts (1, 2, 3) about remembering/restoring the state of list pages I decided to finally put this functionality in a tiny plugin.

The plugin provides convenient methods to controller classes for remembering state. With the default Grails scaffolding each time you navigate away from a list page (e.g. going to the show page) the state of the list is lost when you return. This can be annoying when you paged to a certain page within the list. A typical use case for this plugin is restoring the pagination state (like offset and sorting) of list pages

Have a look at the plugin page for more information.

Wednesday, November 19, 2008

Grails on NetBeans



Today NetBeans 6.5 was released. The Groovy and Grails support is really nice. I'm using it for a couple of months now since M1 and I'm really satified. If you haven't tried it and working on Grails projects you really should give NetBeans a go!

Can't wait though on better Groovy and Grails support in Eclipse. Let's see what SpringSource will bring us.

Tuesday, November 11, 2008

G2One - the Groovy/Grails company - acquired by SpringSource



Just want to mention I'm really amazed by the latest Groovy/Grails news that SpringSource has acquired G2One - the company (including the developers) behind Groovy/Grails. Congratulations guys!

Perhaps this was the last little push which was needed to get a full enterprise wide adoption of Groovy and Grails.

Also for the community good news. I guess there will be more money and resources available to further improve and enhance both technologies.

Friday, October 31, 2008

Grails wins RAD Race 2008



Paul Bakker blogged recently about winning the QNH RAD Race 2008 using Grails! On his blog he explains clearly that while Grails improves productivity dramatically also the architecture would work for large projects.

Jamie Craane - who partnered with Paul - also blogged about his impressions.

Wednesday, October 29, 2008

Grails article in Java Magazine



My article about Grails was published in the latest edition of Java Magazine. Java Magazine is an independent magazine (in Dutch language) which has a circulation of 5200 paid subscriptions in the Benelux.

If you understand Dutch you can download the article here.

Wednesday, October 1, 2008

Xtemplates Plugin Released



I've just released the Xtemplates Plugin in the Grails Plugins repository.

This Grails plugin increases the customization capabilities of the scaffolding templates. The default scaffolding templates are limited to generate list, create, edit and show views. With the xtemplates plugin you can generate any number of views. In the xtemplates generation process also a runtime loaded Helper class (which can be customized just like the templates) is binded so you can execute any code in the generation process.

I think both improvements would be a good candidate to be added to Grails core!

Just get started with installing the plugin:


grails install-plugin xtemplates


And then install the xtemplates:


grails install-xtemplates


The templates will be installed under \src\xtemplates\scaffolding
After changing the xtemplates you can use the following xgenerate commands:


grails uber-xgenerate
grails xgenerate-all
grails xgenerate-controller
grails xgenerate-views

Tuesday, September 16, 2008

Are you the ONE?



Check www.HierGeenNummer.nl to see if you are the ONE.

Tuesday, August 19, 2008

Remembering state of a Grails list page (3)



I'm taking remembering (or maybe I should call it restoring?) the state of a Grails list page a little bit further.

In my Grails project I've added a 'restoreState' method dynamically using the ExpandoMetaClass to all Controller classes. The method is simply added in BootStrap.groovy:


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

class BootStrap {

def init = { servletContext ->
def application = servletContext.getAttribute(GrailsApplication.APPLICATION_ID)

// add restoreState to controller classes
application.controllerClasses.each { controller ->
controller.metaClass.restoreState << { stateParams ->

// get (or initialize) session state params
def sessionStateParams = delegate.session[delegate.controllerName] = (delegate.session[delegate.controllerName] ?: [:])

// store state params and its values in params and session object
// (1) use params (2) or use session (3) otherwise use default value
stateParams.each { k, v ->
delegate.params[k] = sessionStateParams[k] = (delegate.params[k] ?: (sessionStateParams[k] ?: v))
}
}
}
}

def destroy = {
}
}


The 'restoreState' method can now be invoked from the Controller list action to restore the state of the list page like:


Class AuthorController {

def list = {
restoreState([max: 10, offset: 0, sort: id, order: 'asc'])
[ authorList: Author.list( params ) ]
}
}


Note: after writing this entry and using the technique myself I found out that using the BootStrap class to add this restoreState method dynamically is not really useful.

The problem is that it only adds this method during startup of the container and not during reload. So when you change your controller, you loose the method. Solution would be moving this method to a plugin which also depends on the Controller's core plugin, so also during reload the method would be added.

I ended up creating a BaseController which included this method, and my other Controller classes are extending this BaseController. I further installed the Grails templates and changed the Controller templates so they extend the BaseController by default.

Remembering state of a Grails list page (2)



Here is a enhanced version of remembering the state of your Grails list page using a rememberListState method. Just pass in a map of params you want to store in the session and want to use in the list query. The specified param values act as default values.

Controller code:


private rememberListState(stateParams) {
// get (or initialize) session state params
def sessionStateParams = session[controllerName] = (session[controllerName] ?: [:])
// store state params and its values in params and session object
// (1) use params (2) or use session (3) otherwise use default value
stateParams.each { k, v ->
params[k] = sessionStateParams[k] = (params[k] ?: (sessionStateParams[k] ?: v))
}
}

def list = {
rememberListState([max: 10, offset: 0, sort: id, order: 'asc'])
[ authorList: Author.list( params ) ]
}



I think you could even add this rememberListState() method to all Controller classes using the ExpandoMetaClass.

Monday, August 18, 2008

Remembering state of a Grails list page



With the default Grails scaffolding each time you navigate away from the list page (e.g. going to the show page) the state of the list is lost when you return. This can be annoying when you paged to a certain page within the list.

It's easy to make your application remember the state of your list pages. See the example below which is a modified list action:


def list = {
if (!session[controllerName]) session[controllerName] = [:]
if (!session[controllerName]?.max) session[controllerName].max = 10
if (!session[controllerName]?.offset) session[controllerName].offset = 0
if (!session[controllerName]?.sort) session[controllerName].sort = "id"
if (!session[controllerName]?.order) session[controllerName].order = "asc"

params.max = session[controllerName].max = params.max ?: session[controllerName].max
params.offset = session[controllerName].offset = params.offset ?: session[controllerName].offset
params.sort = session[controllerName].sort = params.sort ?: session[controllerName].sort
params.order = session[controllerName].order = params.order ?: session[controllerName].order

[ authorList: Author.list( params ) ]
}


It stores (per controller) the needed params in the session and when performing the action again, it will take the values from session and your list page remembers its state.

Wednesday, August 13, 2008

Grails on Sakila



After spending some time on Flex and AIR in my spare time - with Peek in Grails as result - I'm happily coding in Grails again. Ahh what a pleasure ;-)

Some time ago I wanted to create a real-life demo application in Grails. Back in May or June I took the MySQL Sakila sample database and started building a Grails application on top of it. I also integrated Yahoo! User Interface Library for a better and richer user experience like using the DataTable (see also the Grails YUI DataTable example).

As I continued working on the demo application I added a YUI MenuBar and customized the Grails scaffolding templates so it generates a YUI enabled user interface.
Currently I'm only using the YUI DataTable, MenuBar, Button and Grids CSS but I want to extend this to use the YUI Calendar, AutoComplete, Rich Text Editor and Charts and perhaps some other nice-looking YUI widgets as well. Under water it also uses required YUI utilities for easily sending XMLHttpRequests and processing JSON results.

I basically only changed the main.gsp, main.css and scaffolding templates and see below what can be generated.



I also created the Grails on Sakila project hosted on Google Code, so if you want you can browse or download the source code yourself. It is currently under development so you are warned.

Friday, August 8, 2008

Word Cloud

After connecting to another Dutch Grails enthousiast (Mr. Haki) on LinkedIn I checked his personal blog and found a nice little tool called Wordle.

Pictures say more then words so look below what you can generate with Wordle.



The above Word Cloud is based on this blog and as expected Grails is on top ;-)

Thursday, August 7, 2008

NLGUG Announced



Just like to mention that I've created a space on Google groups to host the Nederlandse Groovy & Grails User Group (NLGUG). It's a Groovy & Grails User Group for people in The Netherlands.

I started this group to start building up a network of Groovy & Grails developers in The Netherlands. If the group grows and people are interested in meeting up this would be great!

So if you are located in The Netherlands and interested in joining, go to http://groups.google.com/group/nlgug.

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.

Monday, June 16, 2008

Grails YUI DataTable example



After I quit looking into the Ext JS library because of the license problems, I wanted to use another AJAX framework which comes bundled with nice widgets. Before I started with Ext I had already looked into The Yahoo! User Interface Library (YUI) but preferred Ext that time. So switching back to YUI makes sense... The only reason I was hesitating was because of Yahoo's possible takeover by Microsoft as it would be uncertain what would happen to YUI on the short and long term. The news that the deal between Microsoft and Yahoo seems to be off the table definitely, made my choise to use YUI easy.

Today I created an example on how to use YUI's DataTable within a Grails application.



The example is based on this simple Country domain class:



class Country {

String country

}





The Grails CountryController looks like:



class CountryController {

def index = { redirect(action: list, params: params) }

def list = {
}

def listData = {
def countryList = Country.list(params)
response.setHeader("Cache-Control", "no-store")
render(contentType: "text/json") {
totalRecords(Country.count())
records {
for (r in countryList) {
country(id: r.id, country: r.country)
}
}
}
}

}



As you can see the list action is doing nothing except rendering the list.gsp. The listData action will be called by the YUI DataTable and returns the records in JSON format together with the total count of records (totalRecords). So at last - and a big chunk of JavaScript code - we need the list.gsp to create and display the YUI DataTable:



<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="layout" content="main" />
<title>Country List</title>
</head>
<body>

<div id="demo">
<div id="paging"></div>
<div id="dt"></div>
</div>

<script type="text/javascript">
// Set up DataSource
var myDataSource = new YAHOO.util.DataSource('${createLink(action: 'listData')}?');
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
myDataSource.responseSchema = {
resultsList : 'records',
fields : ['id', 'country'],
metaFields : {
totalRecords: 'totalRecords'
}
};

// A custom function to translates sorting and pagination values
// into a query string the server will accept
var buildQueryString = function (state,dt) {
return "offset=" + state.pagination.recordOffset +
"&max=" + state.pagination.rowsPerPage +
"&sort=" + state.sorting.key +
"&order=" + ((state.sorting.dir === YAHOO.widget.DataTable.CLASS_ASC) ? "asc" : "desc");
};

// Custom function to handle pagination requests
var handlePagination = function (state,dt) {
var sortedBy = dt.get('sortedBy');

// Define the new state
var newState = {
startIndex: state.recordOffset,
sorting: {
key: sortedBy.key,
dir: ((sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? YAHOO.widget.DataTable.CLASS_DESC : YAHOO.widget.DataTable.CLASS_ASC)
},
pagination : { // Pagination values
recordOffset: state.recordOffset, // Default to first page when sorting
rowsPerPage: dt.get("paginator").getRowsPerPage()
}
};

// Create callback object for the request
var oCallback = {
success: dt.onDataReturnSetRows,
failure: dt.onDataReturnSetRows,
scope: dt,
argument: newState // Pass in new state as data payload for callback function to use
};

// Send the request
dt.getDataSource().sendRequest(buildQueryString(newState), oCallback);
};

var myColumnDefs = [
{key: "id", label: "Id", sortable: true},
{key: "country", label: "Country", sortable: true}
];

var myTableConfig = {
initialRequest : 'max=10&offset=0&sort=id&order=asc',
generateRequest : buildQueryString,
paginator : new YAHOO.widget.Paginator({containers: "paging", rowsPerPage: 10}),
paginationEventHandler : handlePagination,
sortedBy : {key: "id", dir: YAHOO.widget.DataTable.CLASS_ASC}
};

var myDataTable = new YAHOO.widget.DataTable('dt', myColumnDefs, myDataSource, myTableConfig);

// Override function for custom server-side sorting
myDataTable.sortColumn = function(oColumn) {
// Default ascending
var sDir = "asc";

// If already sorted, sort in opposite direction
if(oColumn.key === this.get("sortedBy").key) {
sDir = (this.get("sortedBy").dir === YAHOO.widget.DataTable.CLASS_ASC) ? "desc" : "asc";
}

// Define the new state
var newState = {
startIndex: 0,
sorting: { // Sort values
key: oColumn.key,
dir: (sDir === "desc") ? YAHOO.widget.DataTable.CLASS_DESC : YAHOO.widget.DataTable.CLASS_ASC
},
pagination : { // Pagination values
recordOffset: 0, // Default to first page when sorting
rowsPerPage: this.get("paginator").getRowsPerPage()
}
};

// Create callback object for the request
var oCallback = {
success: this.onDataReturnSetRows,
failure: this.onDataReturnSetRows,
scope: this,
argument: newState // Pass in new state as data payload for callback function to use
};

// Send the request
this.getDataSource().sendRequest(buildQueryString(newState), oCallback);
};
</script>
</body>
</html>



The Grails' createLink taglib is used to define the url to retrieve the JSON data from. For the rest, the YUI DataTable configuration and paginator take care of all server side paging and sorting.

Note that I included the following YUI javascript and css files in my main.gsp:



<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/reset-fonts-grids/reset-fonts-grids.css">
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.5.2/build/assets/skins/sam/skin.css">
<script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/utilities/utilities.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/calendar/calendar-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/datasource/datasource-beta-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/datatable/datatable-beta-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/container/container_core-min.js"></script>
<script type="text/javascript" src="http://yui.yahooapis.com/2.5.2/build/menu/menu-min.js"></script>



Off course it's also possible to install the Grails YUI plugin if you don't want to use the hosted files.

PS: a little bit of refactoring to make it more re-usable, and it would not be difficult to use in the scaffolding generation.

Thursday, June 12, 2008

Grails at LinkedIn



Today Dmitriy Kopylenko posted a link to The LinkedIn Blog on the Grails user mailing list which features a new blog series about Grails. The first post contains a presentation with their experience using Grails to build applications. Their conclusion is that Grails is more productive than the current crop of mainstream Java web application frameworks and that Grails can work well in an Enterprise environment.

This is a must read for Grails adepts who are trying to convince their managers to use Grails. If LinkedIn is using it, why can't your company use it?

Tuesday, May 27, 2008

Ext plugin discontinued



I just want to mention that I decided to discontinue working on the Ext (Scaffolding) plugin for Grails.

The reason is Ext's recent license change (1, 2, 3, 4) to GPL v3. Although the Ext team has announced some Open Source FLOSS exceptions, for me it's end of the line here Ext concerned.

Monday, May 12, 2008

Uber Generate All script for Grails



While I was creating a Grails application on an exsiting database schema I was to lazy to run grails generate-all 15 times. So I created an Uber Generate All script, which generates controllers and views for all domain classes in your application.

If you want to use it create a new script within your application (e.g. grails create-script UberGenerateAll) and copy in the code below:


import org.codehaus.groovy.grails.commons.GrailsClassUtils as GCU
import groovy.text.SimpleTemplateEngine
import org.codehaus.groovy.grails.commons.spring.GrailsRuntimeConfigurator;
import org.codehaus.groovy.grails.scaffolding.*
import org.springframework.mock.web.MockServletContext
import org.springframework.core.io.Resource;

Ant.property(environment:"env")
grailsHome = Ant.project.properties."environment.GRAILS_HOME"

includeTargets << new File ( "${grailsHome}/scripts/Package.groovy" )

generateViews = true
generateController = true

target('default': "The description of the script goes here!") {
    depends( checkVersion, packageApp )
    typeName = "Domain Class"
    doStuff()
}

target(doStuff: "The implementation task") {

    rootLoader.addURL(classesDir.toURL())
    def beans = new grails.spring.WebBeanBuilder().beans {
        resourceHolder(org.codehaus.groovy.grails.commons.spring.GrailsResourceHolder) {
            this.resources = "file:${basedir}/grails-app/domain/**/*.groovy"
        }
        grailsResourceLoader(org.codehaus.groovy.grails.commons.GrailsResourceLoaderFactoryBean) {
            grailsResourceHolder = resourceHolder
        }
        def pluginResources = [] as Resource[]
        if(new File("${basedir}/plugins/*/plugin.xml").exists()) {
            pluginResources = "file:${basedir}/plugins/*/plugin.xml"
        }

        pluginMetaManager(org.codehaus.groovy.grails.plugins.DefaultPluginMetaManager, pluginResources)
        grailsApplication(org.codehaus.groovy.grails.commons.DefaultGrailsApplication.class, ref("grailsResourceLoader"))
    }
                                                    
    appCtx = beans.createApplicationContext()    
    appCtx.servletContext = new MockServletContext()
    grailsApp = appCtx.grailsApplication

    grailsApp.initialise()
    def domainClasses = grailsApp.domainClasses

    if(!domainClasses) {
        println "Domain classes not found in grails-app/domain, trying hibernate mapped classes..."    
        try {
            def config = new GrailsRuntimeConfigurator(grailsApp, appCtx)
            appCtx = config.configure(appCtx.servletContext)                
        }
        catch(Exception e) {
            println e.message
            e.printStackTrace()
        }
        domainClasses = grailsApp.domainClasses
    }
    
    if(domainClasses) {
        def generator = new DefaultGrailsTemplateGenerator()
        domainClasses.each { domainClass ->
            if(generateViews) {
                event("StatusUpdate", ["Generating views for domain class ${domainClass.fullName}"])                
                generator.generateViews(domainClass,".")                                                        
            }                                                                                    
            if(generateController) {
                event("StatusUpdate", ["Generating controller for domain class ${domainClass.fullName}"])    
                generator.generateController(domainClass,".")            
            }
            event("StatusUpdate", ["Finished generation for domain class ${domainClass.fullName}"])            
        }
        event("StatusFinal", ["Finished generation for domain classes"])
    }
    else {
        event("StatusFinal", ["No domain class found"])
    }
}


Then just run grails uber-generate-all and watch!

I have only tested it for Groovy domain classes, but I think it will also work for Hibernate mapped classes. Enjoy.

Sunday, May 4, 2008

Ref Code plugin



Back in my days as Oracle Designer developer we had a nice CG_REF_CODES table in which we could store (semi)-static domains. You can think about these domains as simple code/description lookup tables; for example OrderStatus, OrganizationType, MaritalState etc. As these lookups only contained codes and descriptions it's not worth creating specific tables. That's why we had this CG_REF_CODES tables; we stored all these simple domains, and it's allowable values in 1 simple table.

As this is convienient for any application I created a simple Grails plugin which provides the same functionality. You can store all your simple reference codes in a single table, and use it to render dropdown boxes or validate user input in custom constraints. Have a look at http://grails.org/Ref+Code+Plugin.

Saturday, April 26, 2008

Grails wins 2nd prize in JAX Innovation Award



Just read that Grails has won the Second prize (5.000 euro) in the yearly JAX Innovation Award. And that after Groovy won the First prize a year earlier. As Grails developers we know the Grails/Groovy platform is truly innovative compared to other web frameworks, but it is good to see the recognition from such a public award.

The JAX Innovation Award were presented and awarded to the winners during the conferences JAX, SOACON and Eclipse Forum Europe. The three conferences are taking place at the same time in Wiesbaden and represent the most important meeting for professional information technology in Europe.

Thursday, April 24, 2008

Busy time in Grails plugin land: Announcing the OpenID plugin



It are busy times in Grails plugin land. This week the Searchable Plugin 0.4.1, Acegi Plugin 0.2.1, and the Authentication Plugin 1.0 were already released. And I've just added another one myself: the OpenID Plugin

While Marc Palmer was releasing his Authentication plugin, I was working one a OpenID plugin myself. When I was reading through the Authentication plugin documentation I really got inspired by some features of it. So I borrowed/copied some concepts of it to the OpenID plugin. Thanks Marc!

The OpenID plugin does all the plumbing to communicate with OpenID providers (using the OpenID4Java library) to authenticate/identify users for your website. The great thing is that you are in full control where the controller and the OpenID providers will redirect to in case of successfull login or error.

Here is an example:


<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="layout" content="main" />
<title>Login</title>
<openid:css />
</head>
<body>
<div class="nav">
<span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
</div>
</div class="body">

<h1>Login</h1>
<openid:hasLoginError>
<div class="errors">
<ul>
<li></li>
</ul>
</div>
</openid:hasLoginError>
<openid:form success="[action:'loggedin']">
<openid:input size="30" value="http://" /> (e.g. http://username.myopenid.com)
<br/>
<g:submitButton name="login" value="Login" />
</openid:form>
</div>
</body>
</html>



For full documentation see: http://grails.org/OpenID+Plugin

Friday, April 11, 2008

Update on Ext Scaffolding



I'm making some progress on the Ext Scaffolding for Grails.
Currently I can generate all CRUD functionality for a simple domain class with String properties.

It's done by overriding the default scaffolding templates (aka grails install-templates). So when finished the Ext plugin will contain a plugin to install these templates, which then can be used with the generate-all command.

See below some additional screenshots.



Friday, April 4, 2008

Sneak Preview: Ext Scaffolding for Grails



A short post just before the weekend. Have a look at the nice screenshots below, which are the result of Grails Ext Scaffolding templates I'am working on currently. I hope to include the templates in the Grails Ext plugin soon.



Wednesday, March 26, 2008

First experience with Groovy and Grails support in NetBeans 6.1



Recently many blog postings related to NetBeans with Groovy and Grails integration appeared on groovyblogs.org. See Guillaume Laforge's Groovy / Grails support in NetBeans and GlassFish blog entry for a complete summary and links to related postings.

When I was reading those posting I felt it was time to try it out myself.
After downloading the latest NetBeans 6.1 nightly build and installing the Groovy and Grails plugin, I was ready for a testdrive.

Creating a Grails project


Creating a Grails project has never been easier. The NetBeans 6.1 Groovy and Grails plugin contains a New Project wizard for creating a new Grails project.



After creating the project (NetBeans is calling the Grails create-app command underwater), NetBeans displays a nice project structure specially for Grails projects.



See how NetBeans nicely devides all Grails artifact types (like Controllers, Domain classes, Scripts, Services, etc.) in separate folders.

Creating Grails artifacts


The NetBeans 6.1 Groovy and Grails plugin features context menus for creating Grails artifacts like Domain classes etc easily. After clicking the context menu item, a dialog is opened to enter information needed to create the Grails artifact.





After creating the domain class a context menu is available for generating the controller and views.



NetBeans is always calling the Grails commands underwater and this means that when generating the Views from within NetBeans also customized templates can be used. As far as I could find the install-templates command could not be executed from within NetBeans, so you still need the Grails command line.

Code-completion and syntax color highlighting


The NetBeans 6.1 Groovy and Grails plugin offers Groovy code completion and syntax color highlighting. To bad the current version does not yet include code-completion for Grails dynamic methods.



Running a Grails application


From within NetBeans the Grails application can be started (run-app) using a context menu on application level.



Notice also the context menu item to generate a war archive or to see statistics.

Deploying a Grails application to GlassFish


Reading the other blog posting it is also possible to deploy your Grails application in GlassFish, but I didn't tested this myself yet.

Summary


My first experience with the NetBeans 6.1 Groovy and Grails plugin is good. Nice to have those context menu for executing the Grails commands from the IDE. However code completion, and specially for Grails dynamic methods, need to improve to really compete with IntelliJ. But I really believe this will happen in the near future!

Tuesday, March 25, 2008

Groovy User Group on LinkedIn



I just created the Groovy User Group on Linkedin and already 50 members have joined.
If you are interested join this group at http://www.linkedin.com/groupInvitation?groupID=76751&sharedKey=7038BD424E3B.

For Grails there was already a group on LinkedIn. Join this group at http://www.linkedin.com/groupInvitation?groupID=39757&sharedKey=40CA861F1941.

Friday, February 15, 2008

Grails adaptive AJAX support with the Yahoo! UI Library



Grails ships out of the box with adaptive AJAX support.
By default Grails uses the Prototpe library when invoking AJAX request using the Grails remoteLink, formRemote and submitToRemote AJAX tags.

Not only is Grails providing an easy way of integrating AJAX functionality in your pages, but it also allows you to switch to another AJAX library if needed. This is what we mean with adaptive AJAX support.

One of the use cases to switch to another AJAX library is that this library offers more functionality then the default Prototype library. E.g. you want to use the Yahoo! User Interface Library (YUI) within you application for the autocomplete or calendar functionality. In this case you want the YUI library also to be used for your AJAX tags, so you only depend on YUI and not on both YUI and Prototype.

If you want easy support for YUI in your Grails application then just install the Grails Yahoo! UI Library Plugin which I released today. It downloads and installs automatically the latest YUI 2.4.1 2.5.0 distribution in your application, and registers itself to be used with the adapative AJAX tags. It also contains two helper tags to easily include additional YUI javascript and css files as well.

Note that this plugin does not provide any tags for embedding rich ui components without having to deal with javascript libraries. If you are more interested in such a plugin, have a look at the Grails RichUI Plugin. This plugin contains a set of AJAX components not limited to the YUI library.

For a personal application I want to use the YUI autocomplete and calendar functionality so I'm thinking already of a Grails YUI Widgets Plugin which can be installed on top of the YUI Plugin ...

Tuesday, February 5, 2008

Grails 1.0 released today!



Today the release of Grails 1.0 was announced. See release notes at http://grails.org/1.0+Release+Notes

2008 will be the Grails year!!

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.