G2X Day 0: Landed

Well it was over 20 hours to get here, but I’m landed in Washington DC ahead of the Groovy Grails Exchange which starts tomorrow morning. I’m going to hopefully blog up a bit of the action while I’m here since my talks aren’t until later in the week.

Glen at the Whitehouse

I’ve got to say that Washington is a really beautiful city, and reminds me heaps of Canberra (must be a capital city thing). We’ve been staying a few days in DC to see the sights and have relocated to Reston, Virginia this afternoon. Looks like the conference rooms are all happening downstairs.

Some random observations about Washington DC from an Aussie visitor:

  • Never ever order a large anything, no matter how hungry you are. A regular is already huge.
  • Whatever the price says on the sticker, add about 30% to it to work out what the price really is (There seems to be some sort of VAT/consumption tax happening and if there is a person involved, they’ll want a 19% tip)
  • It’s probably walkable. Really.
  • We’ve found the locals to be really friendly. I’ve stopped a ton of people in the street and every single one has been friendly and helpful.
  • A Starbucks double-shot Caffe Latte is equivalent to an Aussie Flat White.* President’s Day is a great day to visit any monument. Public Holidays are rare in the US, and the locals love to enjoy them at home.
  • Nicole Kidman seems to be quite an Australian Identity.* DC Metro is super-cheap to get around on ($1.35 will get you just about anywhere, $5 for a daypass) and goes most places you want. Though you can walk it pretty easily. :-)

Anyways, big thumbs up to Washington DC. Fantastic place to visit - and even more fun with a couple of hundred Groovy/Grails community members around!

Updates coming soon once the conference is underway… I might even have some news of my own to announce…

[Read More]

Grails UI Extreme Makeover: Going from Good to Great

I’m doing a session at The Groovy and Grails Experience called “Grails UI Extreme Makeover”, so I thought I’d throw out a little teaser on how things are going…

The official blurb I’m putting up for the session, goes something like…

A tour-de-force of Grails plugins and Ajax tips and tricks where we turn a default Grails app into a Web 2.0 VC-attracting machine in 1.5 hours.

In this session we explore a selection of extremely useful and glamorous Grails plugins to turn a basic Grails app into a Web 2.0 buzzword factory. We’ll be looking a Tagclouds, JS Effects, Ajax, Searchable, Timelines, Feeds, Tabs, Hovers and Inplace edits and we’ll do it all live.

We’ll be taking a nice, but plain-looking ToDo app:

The Before Image

And doing a live transformation into a searchable, feedable, tagable, hoverable, tab-friendly, inplace editing, animated, ajax-breathing monster:

The After Image

It’s gonna be a really fun talk.

If you’re in North America from 21-23 Feb, make sure you drop by for a major Groovy geek-out…

[Read More]

Using searchable across domain classes

Mike has done some great sleuthing on how to use Grails uber-cool searchable plugin to search a domain class based on properties of a nested class.

This is exactly what I’ve been looking for! Gravl supports multiple blogs per install, and when you are viewing my blog, I want those seaches to be constrained to fulltext search of just my blog, not all the data in the blog db.

My data model is 1:M from Blog to BlogEntry with a bidirectional link for navigation… So to make it all searchable I’ve got…

class Blog {
  static hasMany = [ blogEntries : BlogEntry, tags : Tag, blogProperties : BlogProperty ]

  static searchable = true

  // ...
}

class BlogEntry {

  static hasMany = [ comments : Comment, tags : Tag ]
  static belongsTo = [ Blog ]

  // we only index title and body, but index all fields in the parent "blog"
  static searchable = {
        title()
        body()
        blog(component: true)
  }

  Blog blog
  //...
}

So by marking blog(component: true) we’re telling Compass to store Blogs fields in the index with the BlogEntry. Why do we want to do that? So we can search BlogEntry and impose criteria related to Blog. In my case, I want to do “find me all BlogEntry objects which contain the text ‘blah’ but limit the hits to those objects that have a blog.blogid of ‘glen’”. The code is simpler than the explanation! Here’s my whole search controller code which powers my search box:

def search = {

        def query = params.query
        def blogid = params.blog

        def results = BlogEntry.search(query + " +blogid:${blogid}", params)

        return [ results: results, query: query ]
 }

And you’re got yourself blog-specific searching… Note that “blog.blogid” becomes just “blogid” in terms of field searching (ie. the component objects fields are collapsed into the parent).

Thanks Mike! Great pickup!

[Read More]

Gravl: Funky Image Preview and Inplace Editing

I’ve been having fun implementing my first cut at Image/File management for Gravl, and along the way I’ve learnt about LightBox and the Scriptaculous InPlaceEditor stuff.

First up, I needed a way of doing an image preview, so I could see what all of those PNGs really meant. Lightbox gives you a great way of “overlaying” the preview image as a popup without the need to refresh the page. Very snazzy. I followed the instructions and I was up and running in a few minutes.

Gravl Image Preview with Lightbox

Everything will make more sense with a little screencast (1.5mb)

Next stop was handling image renaming. I was really after a way to edit the actual filename inplace. Enter InPlaceEditor. This call lets you specify the id of the object you want to edit inplace, along with the url to do the ajax call against (which will get passed the user’s updated value in a params.value). If all is well, you return the changed value and life continues.

Gravl Inplace Rename with Scriptaculous

Well that was quite a lunch hour. I have a basic image management solution to keep me going so I can now upload the images for this blog entry without needing an SCP client!

Happy Previewing in Grails!

[Read More]

Gravl: Autocompleting and Funky Timelines

If you haven’t had a chance to play with the RichUI plugin, you’ve got to take it for a spin - it’s just fantastic. I’ve been meaning to implement autocomplete for Gravl tags on new blog entries, and with the RichUI plugin (which uses Yahoo Autocomplete under the hood) it’s a total snack.

First you declare the RichUI taglibs in your gsp file (note: I’m using the undocumented delimChar setting so that I separate multiple tags with a space):

<resource:autoComplete skin="default" />
<richui:autoComplete name="tagList" delimChar=" " style="width: 100%"
    action="${createLinkTo(dir: params.blog+ '/blog/tagcomplete')}"/>

Then you add yourself a backend action that returns some xml representing the items for the list. I filter the tags by hand because I want to keep everything case insensitive, but your might be able to do the lot in a dynamic finder.

def tagcomplete = {
    Blog blogObj = Blog.findByBlogid(params.blog)
    def queryRegex = "(?i)${params.query}" // case insensitive...
    def tags = blogObj.tags.findAll { tag -> tag.name =~ queryRegex }
    render(contentType: "text/xml") {
        results() {
            tags.each {t ->
                result() {
                    name(t.name)
                }
            }
        }
    }
}

And you’ve got yourself an autocomplete for tags…

Gravl autocomplete in action

Not that we’ve got a nifty tag autocomplete, it’s time to take the timeline virtualisation tag for a spin… One the way into the timeline populate the start date for your entries…

def timeline = {
        Blog blogObj = Blog.findByBlogid(params.blog)
        def entries = BlogEntry.findAllByBlogAndStatus(blogObj, "published", [ sort: 'created', order: 'asc'])

        return [ blogObj: blogObj, startDate: entries[0]?.created  ]
    }

Then create the tags in your page to kick things off..

[Read More]

Gravl: Fulltext Search Redux with Searchable

Thanks to a fantastic tip from Karl on yesterday’s post, I’ve got the searchable plugin up and running on my Grails-RC3 install! Here’s the fix from Karl:

i‘ve got a working Searchable-0.4-SNAPSHOT wo. npe, this is working with the RCs too.
In the plugin‘s java directory u need to slightly modify the
org/codehaus/groovy/grails/plugins/searchable/compass/SearchableCompassUtils.java file.

Here is the “patched” version:
[http://rafb.net/p/prsZip92.html](http://rafb.net/p/prsZip92.html) (nopaste url)

So I patched my version, and I’m up and running. Getting Gravl converted over was a snack. Basically, I just had to remove all my custom code! And, of course, add the magic static searchable=true to my BlogEntry object.

After I patched the plugin, I ran grails install-searchable-config to get a SearchableConfiguration file in my conf directory. From there I could change the compassConnection setting to point to the place I like to store my indexes.

My search action could then shrink to:

def search = {

        def query = params.query
        def blogid = params.blog

        def results = BlogEntry.search(query, params) 

        return [ results: results, query: query ]
    }

The results you get back in ${results.results} are first class domain objects! So it’s pretty much just a matter of a little markup…

<g:each var="result" in="${results.results}">

                <div class='hit'>

                    <div class='hitEntry'>
                        <div class='hitTitle'>
                            <a href='${request.contextPath}/${result.toPermalink()}'>
                                ${result.title}
                            </a>
                        </div>
                        <div class='hitInfo'>
                            <g:niceDate date="${result.created}"/>
                        </div>
                        <p class='hitBody'></p>
                    </div>
                </div>

            </g:each>

Add a g:paginate into the mix, and you’re cooking!

One thing I have lost in the conversion is the ability to do hit term highlighting. I know Compass supports it, so it might just be a matter of digging in a little deeper! I’d also like to be able to store some DynamicMetaData with the index (blogEntry.blog.blogid), but haven’t managed to get the annotations working just yet…

[Read More]

Gravl: Adding Fulltext Search

I’ve just finished adding fulltext search to Gravl, and, while is still needs some more CSS tweaks, it’s working just great. I took the chance to have a good look at the Searchable plugin and it looks awesome! Sadly, it just kept NPEing at me and complaining that it couldn’t find java.lang.Class! Perhaps it’s a victim of the RC-flux, no doubt it will resurface as 1.0 settles in.

Anyways, I ripped the SearchService out of Groovyblogs, and I was up and running in an hour. I must bundle that thing into a LuceneSearch plugin someday… it’s really very handy. Feel free to have a search in that box at the top right, “Pebble” gives you a good chance to see the paginate in action.

gravl Search in action... searching for Gravl!

So that last real piece of the Gravl puzzle is image upload and management. But right now I’ve got to get cracking on my “Grails Extreme Makeover” talk for G2X.

Search on!

[Read More]

Gravl: Referers, Geocoding, Charts

Continuing my obsession with the power of Grails filters, it was time to add some referer tracking and ipaddress Geocoding to Gravl to see where people are coming from. So I created a new RefererFilters class in conf and I was up and running. I already had a little CacheService happening, so I created a new 24 hour cache, scooped up some interesting browser header data, and squirrelled it away for later:

 // Scoop up Referer and User-Agent and remote IP
def userAgent = request.getHeader("User-Agent")
def referer = request.getHeader("Referer")
def ip = request.getRemoteAddr()

// Grails adds request.forwardURI to keep the orig URI
def url = request.forwardURI 

// put in the cache, not use of applicationContext to lookup service bean
CacheService cacheService = applicationContext.getBean('cacheService')
cacheService.putToCache("referers", 60*60*24, Calendar.getInstance(),
                [ userAgent: userAgent, referer: referer, ip: ip, url: url ])

One thing to notice in the above, service injections don’t happen in the filter chain, but you are provided with an applicationContext that you can use to look up your services by name.

Anyways, now I’ve got some stats, it’s time to do some crunching… Referers was the first target to add to my stats page… though the css needs some work to fit the counters in :-)

Gravl referers in need of some css

Now what to do with those ip addresses I was gathering? Picking up on the stuff Simon is doing in the latest Pebble, I thought it might be nice to add some geocoding to that referers list so I can see which country my traffic is coming from.

Turns out there’s a very simple little Java library available from MaxMind called GeoLite Countries. I mavenized the source into a jar, then dropped the resources file into my /src/java/resources. I already had the ip addresses I’d gathered from headers into my cache, so to convert them to a country name, it was time to create a CountryLookupService to wrap all the calls to the GeoLite library:

class CountryLookupService {

    LookupService lookupService

    private LookupService getLookupService() {
        if (lookupService == null) {
            // assume you put your data file in /src/java/resources
            def url = this.class.getResource("/resources/geoIp/GeoIP.dat")
            log.debug ("Looking for GeoIP database at: $url")
            lookupService = new LookupService(new File(url.toURI()),LookupService.GEOIP_MEMORY_CACHE);
        }
        return lookupService
    }

    def getCountryName(String ipAddress) {
        log.debug "Looking up address for ${ipAddress}"
        return getLookupService().getCountry(ipAddress).getName()
    }
}

And we’re off and running. The GeoCoding API requires you to supply a current GeoIP.dat file, and the sample above shows you how to load it from your Grails /src/java/resources folder to keep everything all relative. I’m not sure where the agreed convention is on resource placement in Grails, but there is as good as any for now… After that I just needed to iterate my cache, and we have ourselves a hashmap of countryname to hitcount:

Gravl Geocoding in action

[Read More]

Gravl: Implementing Security on the Cheap...

Grails Filters open up a whole new world of opportunity for jumping into the request pipeline to do your own custom processing. I’m really excited about getting Referer tracking real soon now, since I’ve learned the basics of Filters when I implemented Gravl’s very basic security model.

The Grails community is working on some first class plugins for Acegi and JSecurity, but for if your security model is coarse grain (authenticated users can do this, everyone else can’t), it’s easy enough to write a simple custom filter for the task (and a good way to learn filters to boot!).

First we’ll add a SecurityFilters class to the conf directory. Then we’ll get busy declaring our filter logic…

def filters = {

        loginCheck(controller: '*', action: '*') {

            before = {
                      // do our security checks here, before the controller action is invoked...
            }
      }
}

Based on some ideas I picked up from The Bishop (The Grails CMS sample includes users with certain roles can do certain things, but that was overkill for Gravl), I hold the security model in an a hashmap, where I map the controller name to a list of public actions.

def publicStuff = [
    "blog" : ["archive", "homePage", "displayOneEntry"],
    "comment" : ["newComment", "preview", "save"],
    "feed": ["feeds"],
    "pdf": ["show"],
    "image" : ["display"],
    "login" : ["form", "login"]
]

Then it’s a matter of checking whether (1) you have an account object in your session - in which case you’re in, or (2) If you aren’t authenticated, make sure the action you’re invoking is in the public list. If you’re heading for a secured page as an unauthenticated user, I send you off to login. Filters don’t seem to have a logger injected at the moment, so excuse my printlns, but I’m sure they’ll be there soon:

if (!session.account) {
    boolean allowed = false

    println "Checking access to controller [${controllerName}] with action [${actionName}] (${request.forwardURI})"

    if (publicStuff.keySet().find { it == controllerName }) {

        if (publicStuff[controllerName].find { it == actionName }) {
            println "Access ok to public controller [${controllerName}] with action [${actionName}]"
            if (controllerName != 'login') // keep last unsecured page in session for post-login return
                session.returnurl = request.forwardURI
            allowed = true
        } else {
            println "Access privileged action: ${actionName}"
            allowed = false
        }

    } else {
        allowed = false
        println "Access privileged controller: ${controllerName}"

    }

    if (!allowed) {
        session.returnurl = request.forwardURI
        println "Setting return url to " + session.returnurl
        redirect(controller: 'login', action: 'form')
    }

    return allowed
} else {
    println "Found user in session"
    return true
}

And you’re up and running with a basic security model! Still need to implement a basic “remember me” cookie, but the sirens of referer tracking are singing… I can feel a RefererFilters class coming on…

In the meantime, have fun with the lightweight security goodness that filters open up… Can’t wait to see the next generation of plugins that use this stuff!

[Read More]

Gravl: Mind the dust, we're live with 0.1

Things are likely to be a little rough around the edges over this way… but welcome to the first live edition of Gravl!

A few days ago my Pebble blog data become corrupt, so I was very motivated to get a rough and ready version of Gravl up and going. The code is fairly scrappy around the edges, and there will be issues (particularly not sure which browers will work with the Ajax comment magic going on) but we’ll tidy things up over the next little while.

You can follow along at home by downloading the code from Gravl on Google Code. I’ve made some changes to the bootstrap process, so it should be good to go from “grails run-app” now…

Phew! Anyways… we’re up and going and brimming with ideas… But the weekend is here and it’s time to hang out with the family!

[Read More]

Gravl: CriteriaBuilder

Dynamic finders rock! But if you’re querying across tricky many-to-many joins, you might want to take advantage of GORM’s extremely powerful query DSL, criteriaBuilder.

Criteria wraps Hibernate’s powerful Restrictions API, and gives you a wealth of expressiveness. For the Gravl archive, I wanted a “by tag” archive option, so you could see just the “groovy-related” archive. Because blog entries have a many to many with blog tags, dynamic finders weren’t going to get me there.

CriteriaBuilder will let me do stuff like “find all blog entries that are attached to this blog object, have a status of published, and contain a tag object in their tags collection where the tag name is groovy. Give me a max of 20 objects for my archive page, and offset it with whatever the paginate tags says we’re up to”:

def bc = BlogEntry.createCriteria()
def entries = bc.list {
    eq('blog', blog)
    eq('status', 'published')
    tags {
        eq('name', params.tagName)
    }
    maxResults(20)
    firstResult(offset)
    order("created", "desc")
}

Using the firstResult to be your g:paginate offset, and you’ve got yourself a scrollable, by-category archive to link up to your tagcloud. And we’re in action…

The Groovy Related Archive

You can also also use Criteria to do counting (and even recordset scrolling!). Check out the docs for more details. I know I’ll be using Criteria lot more now that I’ve tasted how simply you can express complex queries.

GORM rocks!

[Read More]

Gravl: Screencast, Command objects, Custom Validators, Pagination

Ok. My comment system is now pretty closed to being done, so it’s time to screencast some progress. Here’s a sample of comments in a Gravl Screencast (4mb)

gravl Screencast in action

I though it might be a good chance to demonstrate command objects, the basic Grails Ajax tags and their usage, and how to handle errors nicely.

I’ve added a Ajax “Preview” feature so you can see your comment markup before you post them. I’m using Command objects for the comment preview since I want to be able to do simple validation without having to satisfy the more strict criteria of my real domain objects.

If you haven’t played with Command objects, they provide a very simple way to automatically marshall an html form into an object. You can define the same rich “constraints” model that Domain objects have, and get all that error message goodness that you’re used to.

Using command objects also provides a good chance to demonstrate custom constraints to do cross-field validation. In the case of my comment object, I don’t want to let them tick “email me when new comments are added” unless they’ve provided a valid email address (which is optional). Here’s how you implement it:

   emailUpdates(validator: { enabled, comment ->
        if (enabled &&  !comment.email ) {
            return false
        }
      })

Implementing the archive was a “lunchtimeable” undertaking once I read up on the g:paginate tag. For paginate to work, you need to know the size of your dataset, and the number of entries per page. This introduced me to the countBy dynamic method which makes implementing pagination a snack:

def offset = params.offset ? params.offset : 0
totalArchiveSize = BlogEntry.countByBlogAndStatus(blog, "published")
entries = BlogEntry.findAllByBlogAndStatus(
    blog, "published",
    [ sort: "created", order: "desc", offset: offset, max: 20 ] )

And you’ve in business with pagination:

[Read More]

Gravl: Google Talk Notifier

Ok, file this one under the “What were you thinking” category, but I’ve written a little service to sent New Comment notifications via Google Talk.

All of the heavy lifting is done via the Smack API, since Google Talk runs on XMPP (the same protocol as Jabber). You can find some (dated but good) background here. Anyways, here’s my beast in action:

Incoming comment notification via Google Talk

To get this going in your own app, we’ll make thing configurable via a few entries in Config.groovy:

chat {
    serviceName = "gmail.com"
    host = "talk.google.com"
    port = 5222
    username = "yourid@gmail.com"
    password = "your_password"
}

Then we’ll add ourselves a simple GoogleNotificationService…

import org.codehaus.groovy.grails.commons.ConfigurationHolder
import org.jivesoftware.smack.Chat
import org.jivesoftware.smack.ConnectionConfiguration
import org.jivesoftware.smack.Roster
import org.jivesoftware.smack.XMPPConnection
import org.jivesoftware.smack.packet.Message

/**
* All these ideas courtesy of chase@osdev.org from a discussion at:
*     http://www.igniterealtime.org/community/message/147918
*/
class GoogleTalkService {

    boolean transactional = false

    def sendChat(String to, String msg) {

        log.debug "Sending notification to: [${to}]"
        ConnectionConfiguration cc = new ConnectionConfiguration(
                ConfigurationHolder.config.chat.host,
                ConfigurationHolder.config.chat.port,
                ConfigurationHolder.config.chat.serviceName)
        XMPPConnection connection = new XMPPConnection(cc)

        try {

            connection.connect()
            connection.login(ConfigurationHolder.config.chat.username,
                    ConfigurationHolder.config.chat.password)

            def chatmanager = connection.getChatManager()

            // we talk, but don't listen, how rude
            Chat chat = chatmanager.createChat(to, null)

            // google bounces back the default message types, you must use chat
            def msgObj = new Message(to, Message.Type.chat)
            msgObj.setBody(msg)
            chat.sendMessage(msgObj)
        } catch (Exception e) {
            log.error("Failed to send message")
        }

        // and close down
        connection.disconnect()

    }
}

You can’t seem to send messages to yourself… So I had to setup a second Gmail account just to test things out! There’s probably a better way, but it’s already pretty late.

Anyways, probably not that useful, but I’ve been wanting to learn a little XMPP for a while, and have finally had the chance!

Happy Grails Chat botting!

[Read More]

gravl: Sorted Sets, Comment Bubbles, Rolodexes

I’ve been doing some work on the comment rendering section of Gravl, and just discovered the joy of Grails SortedSets.

I was keen to keep my comments in date order, but didn’t want the overhead of custom queries when I was enjoying just navigating the object graph (blogEntry.comments.each() did you say?). Enter the SortedSet.

Grails lets you use SortedSets in one-to-many scenarios so you can keep things in you preferred order automatically. All I need to do was change my Comments object to be a SortedSet in the BlogEntry class (the “one” side)…

  static hasMany = [ comments : Comment, tags : Tag ]
  SortedSet comments

Then just needed a little work on the Comments (the “many” side) class to implement comparable and we’re in order..

  class Comment implements Comparable {
  ...
  // we keep comments sorted by date on each entry
  public int compareTo(Object obj) {
    return created <=> obj.created
  }
}

Once that’s done, we just need to mark it up…

Gravl comment bubbles

[Read More]

Gravl: Insanely simple PDFs for Grails

And if I told you that 20 lines of code was probably plenty for fine looking PDFs? So you’ve just written a killer blog entry that legions of fan are keen to copy to their PDAs to read on the way home? Just click Gravl’s “PDF” icon and you’re off:

The result of a PDF export

PDF is a visual medium, so why design everything in non-visual XML… why not design your PDF export in html (which you’ve got great tools for), then pass it through a simple library to tranform from html straight to PDF?

I was totally inspired by Josh’s article on using Flying Saucer (which is a html rendering component for Java which can read a bunch of html then render to a Swing component or to PDF).

Integrating into Gravl was a matter of creating a PdfController then passing it a relative URL to render. Because rendering can take a little time, I wrap it in cache to make it faster for the next guy:

import org.xhtmlrenderer.pdf.ITextRenderer;

class PdfController {

    CacheService cacheService

    def index = {
        redirect(action: show)
    }

    private byte[] buildPdf(url) {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        ITextRenderer renderer = new ITextRenderer();
        renderer.setDocument(url);
        renderer.layout();
        renderer.createPDF(baos);
        byte[] b = baos.toByteArray();
        return b

    }

    def show = {

        def baseUri = request.scheme + "://" + request.serverName + ":" + request.serverPort +
                    grailsAttributes.getApplicationUri(request)
        log.debug "BaseUri is $baseUri"

        def url = baseUri + params.url + "?print=true"
        log.debug "Fetching url $url"

        // grab pdf bytes from cache if possible
        byte[] b = cacheService.getFromCache("pdfCache", 60, url)
        if (!b) {
            b = buildPdf(url)
            cacheService.putToCache("pdfCache", 60, url, b)
        }

        response.setContentType("application/pdf")
        response.setHeader("Content-disposition", "attachment; filename=blog.pdf")
        response.setContentLength(b.length)
        response.getOutputStream().write(b)

    }
}

Then you just need to add yourself a link to the PDF creation...

<g:if test="${print!=true}">
    <g:link controller="pdf" action="show" params="[url: entry.toPermalink('')]">PDF</g:link>
</g:if>

Now Flying Saucer is a strict xhtml renderer, so if all those <p> tags don’t terminate nicely… you need to know a few tricks. If you’re coming to my “whole nine yards” session at 2GX and I’ll let you in on a few other tricks…

Go forth and PDF with pleasure!

[Read More]

Gravl: Feeds, UrlMappings, Codecs, Permalinks, and all that Jazz

Give Marc’s excellent work on the Feeds Plugin, I knew the feed generating work would be pretty straightforward, but there were a few things to learn along the way.

I wanted to mimic Pebble’s feed urls, so a little custom work in UrlMappings would be required. Pebble supports a /rss.xml, a /atom.xml, and also the category specific versions (/categories/Groovy/rss.xml). All very doable:

// feeds for all blog entries
"/$blog/$feedtype" {
    controller = "blog"
    action = "feeds"
    constraints {
        feedtype(inList: ['rss', 'atom'])
    }
}

// feeds for individual categories
"/$blog/categories/$categoryName/$feedtype" {
    controller = "blog"
    action = "feeds"
    constraints {
        feedtype(inList: ['rss', 'atom'])
    }
}

Which gives us a nice matcher. Seems the extension (.xml) gets thrown away for matching purposes, but that makes things even easier for me.

Now on to the work of generating the feed itself. I’m using Marc’s builder since it gives me a little more flexibility. I also have to cater for category specific feeds. Here’s my feeds action:

    def feeds = {

        log.debug "Starting feed generation..."

        def blogId = params.blog
        def category = params.categoryName
        def feedtype = params.feedtype

        def blog = Blog.findByBlogid(blogId)

        log.debug "Rendering feed for blog $blogId of type $feedtype"

        if (blog) {
            def entries = BlogEntry.findAllByBlog(blog, [max: 10, sort: "created", order: "desc"])
            if (category) {
                // filter to supplied category
                entries = entries.findAll {entry ->
                    entry.tags.find {it.name == category}
                }
            }
            def baseUri = request.scheme + "://" + request.serverName + ":" + request.serverPort +
                    grailsAttributes.getApplicationUri(request)

            def builder = new feedsplugin.FeedBuilder()
            def feedTitle = blog.title + (category ? " ($category Related category)" : "")
            builder.feed(title: feedTitle,
                    link: baseUri + (category ? "/categories/$category" : ""),
                    description: feedTitle) {
                entries.each() {blogEntry ->
                    entry() {
                        title = blogEntry.title
                        link = blogEntry.toPermalink(baseUri)
                        publishedDate = blogEntry.created
                        content(type: 'text/html', value: blogEntry.body) // return the content
                    }
                }
            }

            def romeFeed = builder.render(feedtype)

            render(text: romeFeed, contentType: "text/xml", encoding: "UTF-8")

        }
    }

Let’s give it a whirl in RSSOwl to see how it looks:

Gravl RSS Feed running in RSSOwl

Ok. Still not ideal. Should do the filtering in the query rather than afterwards to ensure I end up with the right number of elements. Will tidy that one up soon.

One of the other little neat tricks that I’m using is developing some handy codecs. Codecs let you attach a handy method to your string objects that you can later take advantage of. I’ve created NiceTitleCodec.groovy and placed it in /grails-app/utils to help with permalink generation:

[Read More]

Gravl: Of Tagclouds and Many-to-Many

If you get your data model right, implementing a tagcloud turns out to be a snack. In Gravl, I’ve setup a many-to-many between BlogEntry and Tag, but I also have a link from the parent Blog directly to all available tags:

Data Model from IntelliJ

Implementing many-to-many simplifies to import process when I import my Pebble data. The import process ends up with something like this:

def tag = Tag.findByBlogAndName(newBlog, importedCat)
if (tag == null) {
    tag = new Tag(name: importedCat)
    log.debug "Creating new tag for ${importedCat}"
    // Grails handles the bidirectional link for us
    newBlog.addToTags(tag).save()
} else {
    log.debug "Found existing tag for ${importedCat}"
}
newEntry.addToTags(tag).save()

Once you’ve got an easy way to access all tags for a blog, and a count of how many times they occur, you’re all set to following a simple tag cloud algorithm. Sounds like a perfect case for a TagCloudTagLib? Needs some error handling, but here’s a first cut (apologies for the formatting):

class TagCloudTagLib {

    // Simple Algo from http://www.petefreitag.com/item/396.cfm
    def tagCloud = {attrs, body ->

        def blogId = attrs.blogId
        Blog blog = Blog.findByBlogid(blogId)
        log.debug "Building tagcloud for: $blog.title"

        // step 1: determine tags and their frequency
        def tagToFreq = new TreeMap()

        blog.tags.each {tag ->
            def tagCount = tag.entries.size()
            tagToFreq[tag.name] = tagCount
            log.debug "Setting size of ${tag.name} to $tagCount"
        }
        def allFreq = tagToFreq.collect {tag, freq -> return freq}.sort()
        def minFreq = allFreq[0]  // first entry
        def maxFreq = allFreq[-1] // last entry
        log.debug "Max is $maxFreq and min is $minFreq"

        // step 3: find the spread, use 5 font sizes
        def distrib = maxFreq - minFreq
        def catSize = (distrib / 5).intValue() + 1 // add 1 to simulate ceil()

        // step 4: output your tags
        tagToFreq.each {tag, freq ->
            out << "<a href='/$blogId/tags/$tag' class='tagCloudSize" + (freq / catSize).intValue() + "'>"
            out << tag
            out << "</a> "
            log.debug "Tag class of $tag with size $freq is " +  (freq / catSize).intValue()
        }

    }

}

Which make using tagclouds in a gsp as easy as:

<g:tagCloud blogId="glen"/>

Or something more dynamic..

Of course, you’ll want to style those bad boys once you put them in a GSP. But the CSS is pretty straightforward:

[Read More]

Gravl Week One: Webflowing an Ajax upload

If you haven’t started playing with the Webflow features of Grails, here’s a short demo of Gravl’s Ajax-powered blog upload feature to whet your appetite…

Webflow is one of those technologies that is awesome for one particular aspect of your application: wizard-like things that take multiple steps! If you don’t use a technology like Webflow, you end up writing a simple state engine, and filling your session with all sorts of stuff to keep track of where your objects are up to.

As it turns out, Steven Devijver, one of the Grails founders, and a nice guy, has written a great book on Spring Webflow, so it’s not surprising that the Grails Webflow integration is first class.

I’ve just finished implementing the “Upload Pebble” feature where I can upload a zip file straight into my Gravl blog engine. It’s got the Ajax goodness going on, of course, to keep you posted on how your upload is going:

Ajax Upload in Progress

The sweet looking progress bar is courtesy of Bramus.

Putting Ajax calls in the middle of a Webflow turns out to be a bit of a non-trivial operation, but before we get complex, let’s look at how the flow working in practice. Here’s a pic of how things are implemented at the moment:

Flow of the Ajax Progress Bar

And the implementation in the controller looks something like this:

   def fileUploadFlow = {

        showDialog {
            on("upload") {
                // slurp in the zip here omitted
                importService.setZipFile(zipfile)
                return [ziptype: importService.supportedTypes ]
            }.to "showImportDetails"
            on("cancel").to "headHome"
        }

        showImportDetails {
            on("cancel").to "headHome"
            on("upload") {
                // use our flow-scoped service
                importService.setBlogId(params.blogId)
                importService.setBlogType(params.blogType)
            }.to "ajaxUpload"
        }

        // display ajax upload form
        ajaxUpload {
            on("update").to("refreshResults")
            on("cancel").to "headHome"
        }

        // refresh the ajax progress
        refreshResults {
            action {
                def percent = importService.percentComplete()
                [ percentComplete: percent ]
            }
            on("success").to "_webflowForm"
        }

        // send a webform with the results, unusual
        // _naming allows us to reuse the form
        // as a gsp template in the initial ajaxUpload view
        _webflowForm {
            on("update").to("refreshResults")
            on("complete").to("finishedImport")
            on("cancel").to "headHome"
        }

        finishedImport()

        headHome {
            redirect(action: index)
        }

    }
[Read More]

Give me 1K LOC and I will move the Earth!

Well.. maybe not “move the earth”… but I might just be able to come up with a pretty nice little sample blog app. My goal is to have something to add to the Grails samples distro so that people have a nice, gentle, but complete app to cut their teeth on.

The humble blog app seems to be low handing fruit, (some people have already started work on such beasts) so I’m off and running on my next little project. I’m modelling off Simon Brown’s way cool Pebble (which powers this site at the moment), but if I’m to fit it into 1000 Lines of Code, I’m going to have to dispense with a few features… so… may I introduce:

gravl

Gravl - Pebble, but smaller, and without some of the niceties.

Ok. Here are the rules.

The main aim of the app is as a samples/teaching tool. So I’m cramming a fairly diverse set of stuff in just so people have examples of some of the powerful features in Grails. In my sights right now are:

Boy.. That all sounds a little ambitious.. Let’s see how far I can get by Christmas… It’s Grails, right? I’m sure I’ll have something to show…

[Read More]

First experiences with IntelliJ... and its stunning Groovy/Grails support

After GrailsExchange, I put it on my TODO list to checkout IntelliJ. Some of the guys were demoing certain features of the JetGroovy plugin and some of the Grails support looked just stunning. So I downloaded the trial, installed the JetGroovy plugin, and created a new Grails app.

I’m a hardcore Eclipse user, and have never used Idea before, but the learning curve is pretty nice. So what about the Groovy/Grails stuff? I’m only a week in, but the things I use all the time are the quick switch options between Domain/Controller/View/Test for a given object. That saves so much time..(in screengrab below I have everything related to the Entry domain class instantly onscreen)

Quick switching between stuff

The other massive timesaver is the great work they have put into getting Groovy completion happening. The support is still not complete, but it is a long way there. Being able to control-click on a class to jump there is sensational.. and I haven’t even started playing with the Groovy Refactor support! Autocomplete also works great on Grails custom taglbs in the gsp view (which is a huge timesaver for me since I have the memory of a goldfish…)

They’ve done some cute stuff with the domain modelling gear too. You can get a nice pick of where your dependencies are setup (which is super helpful in getting a “lay of the land” for the project).

Domain Class Modelling Stuff

So first impressions are excellent. The IntelliJ guys have done a really nice What’s new page where you can see all the integration points with a ton of screengrabs.

I’ll be giving it a solid workout over the next month…

[Read More]

Thumbnail Server src now available (and Groovyblogs src now updated to 1.0 RC1)

For those who have been chasing the source to the Browser Thumbnail stuff, it’s now available for download (Apache 2.0 license). It’s a little scrappy and needs a tidy up, but we’re all friends, right?

At the same place you’ll find the updated source for groovyblogs that will play nice with Grails 1.0 RC1. This version now makes plenty of use of the way-cool Grails Config stuff, so it should be a lot easier to customise to your environment. Make sure you check out the Config.groovy file and customise the paths/proxies/whatnot..

The Thumbnail server should be a painless “download and run” for OSX users, others should upgrade their operating system check the README.txt…

To start the service, do an "ant run" from where
you unzipped the distro. There is no GUI, just a
bunch of log messages. :-)

You can create a simple client for testing by following
the instructions here:

http://blogs.bytecode.com.au/glen/2007/11/05/1194230711983.html

FOR NON OSX USERS!!!!

In order to get this running on your own workstation,
you'll need to perform the following steps if you're
not running on OSX:

1\. Download the correct SWT libraries for your platform

Head on over to http://www.eclipse.org/swt/ and grab the
download for your platform. Remove the current ones
from "/lib" in the project and replace them with the
two matching SWT jars for your platform.

2\. Remove the references to -XstartOnFirstThread
from build.xml and ThumbnailServer.groovy.

3\. If running on linux, you'll need to setup the path to
your browser component.

On Ubuntu, I use something like:

export MOZILLA_FIVE_HOME=/usr/lib/firefox

4\. You're done! Do an "ant run" and give it a try

Enjoy the thumbnailing goodness..

[Read More]

Tiny browser thumbnail client, anyone?

While testing out my little browser thumbnail service, I thought I’d throw together a little thumbnail Swing client to tinker with (I was super inspired by Geertjan’s work on Soap clients). You can type in a URL, invoke the Thumbnail service over XMLRPC, then have a look at the image returned:

Thumbnail client in action

If you’re keen for a play, download the latest XML RPC jar (I’m using v0.3), and copy it to your ~/.groovy/lib directory (or %USERPROFILE%.groovylib on Windows). Then fire up groovyConsole and cut and paste….

import groovy.net.xmlrpc.*

def server = new XMLRPCServerProxy("http://www.groovyblogs.org:7999")

def swing=new groovy.swing.SwingBuilder()

def frame=swing.frame(title: 'Thumbnail Service',
    preferredSize: [700, 500],
    background: java.awt.Color.lightGray) {
  vbox {
    panel {
    label 'Enter URL:'
        textField(id: 'url', columns: 40, "http://www.google.com.au/")
    button(text: "Get Thumb", actionPerformed: { event ->
        println "Retrieving URL: $swing.url.text"
        def thumbs = server.getThumbnail(swing.url.text)
        def large = new javax.swing.ImageIcon(thumbs[0])
        def small = new javax.swing.ImageIcon(thumbs[1])
        swing.smallicon.icon = small
        swing.bigicon.icon = large
    })
    }
    panel {
    label(id: 'smallicon')
    label(id:'bigicon')
    }
  }
}
frame.pack()
frame.show()

The service only exposes one method, getThumbnail(url) which returns an array of byte[]. The first element is the large thumbnail bytes, the second is the small one. The format returned is jpeg. Be patient, it can take a few seconds to return…

Once I have a public download of the thumbnail service code, I’ll cut the public API, but it’s a fun experiment of what you can do with a few lines of groovy plus some XMLRPC goodness!

[Read More]

Groovyblogs goes Thumbnails!

Those of you that came to my Grails Exchange talk (slides) will know that I’ve been working hard on a strategy for Browser Thumbnail generation. Well I’ve finally got one that works fairly reliably. I took the opportunity to upgrade groovyblogs to Grails 1.0 RC1 at the same time.

GroovyBlogs with thumbnails

I’ve tried a few approaches to thumbnails with various Swing libraries, but found the best results came from using the SWT Browser component. This component wraps a real browser (ie on windows, Safari on OSX, and Firefox on Linux). It means I can run my thumbnail server on whatever box I like (though the Linux routines produce a significantly more jagged images…). My Java code launches a new shell containing a browser component, points it at a URL, waited until browser component fires its “all done” event, then grabs an Image of the component and writes it to a JPEG.

Why does it generate JPEGs and not PNGs/GIFs? I’ve tried all the formats, but JPEG is the only one that works with all of the websites I’ve sampled. I haven’t got to the bottom of it yet, but the SWT libraries on OSX seems to write PNGs that have a bit-depth that is too complex for some browsers. And if won’t generate GIFs for sites with complex bit-depths at all! Both those cases only happen for a percentage of sites… but enough to be a pain. I haven’t found a site that won’t JPEG reliably, so I’m sticking with that (and the thumbnails are quite a lot smaller in file size, though there is a little artifacting).

When can you get the source? Real soon now… The upgrade to Grails 1.0 RC1 clobbered my build.xml file which builds my distro.zip… and my CVS server is only freshly restored from backup, so I have to hunt down the old version. But I should have something next week for people to download and play with if they’re keen.

The SWT stuff that does the browser grab is written in Java, but exposed by a XML RPC service written in Groovy. I wanted to do all the funky browser-launching stuff in a separate VM from the app server, and XMLRPC was such an easy option with the Groovy XMLRPC module. Things get even trickier cause my server is OSX, and SWT needs a special JVM switch of -XstartOnFirstThread, so it was pretty compelling to have it as a standalone service. I’ll post some more details and sample client code, when I get some downtime…

Happy thumbnailing!

[Read More]

Grails Exchange Day 0: iShowU Screencasting Addiction

Mind the Gap! I’m in London! 33 hours in a plane is worth it to be at Grails Exchange this week. It’s gonna be awesome.

I’m not sure of what the WiFi story is at the Barbican Centre, so I’m screencasting a few bits and pieces of the presento. I did a little homework on Screencasting stuff for OSX, and ended up settling with iShowU which seem to have awesome bang for buck.

iShowU Screencast of Grails Exchange Demo

You can download a sample screencast (744kb) of my GZone app if you’d like. You can tweak all sorts of settings with iShowU including different capture quality, custom screen sizes, CPU impact, with or without voice-over, awesome!

If you need to trim the Quicktime clips you create, you can grab a free copy of MPEG Streamclip which is the perfect editing companion for quick trims and edits (or just dump it into iMovie if you’re super keen).

Having a ball seeing the sights in London… and can’t wait to hang out with you all at Grails Exchange tomorrow!

[Read More]