I’m currently working on a little Grails bug tracking application which should see the light of day on Sourceforge sometime in the new year. One of the features I just love about Jira is the RSS support - I’m always watching the Grails recently closed issues to see what’s been fixed in the SVN.

So I decided to see what’s involved in generating a feed. I could use the standard Groovy MarkupBuilder for sure, but then I’d have to work out the details for each of the XML formats in the common RSS and Atom formats. There has to be an easier way.

Enter ROME, a fantastic little library for generating ALL the common RSS feed formats. My goal was to have a controller action that takes a parameter for feed type so I could set up a bunch of links for the common cases. Turns out there’s a great tutorial to get you started. Using it from Grails makes it even simpler…

Copy your rome.jar file to your project/lib directory, create a new FeedController, and you’re off and running. Here’s a sample controller I’ve whipped up that generates a ton of standard feeds:


import com.sun.syndication.feed.synd.*;
import com.sun.syndication.io.SyndFeedOutput;

class FeedController {

    def supportedFormats = [ "rss_0.90", "rss_0.91", "rss_0.92", "rss_0.93", "rss_0.94", "rss_1.0", "rss_2.0", "atom_0.3"]

    def rss = {

            render(text: getFeed("rss_1.0"), contentType:"text/xml", encoding:"UTF-8")

    }

    def atom = {

            render(text: getFeed("atom_1.0"), contentType:"text/xml", encoding:"UTF-8")

    }

    // or specify your own feed type
    def all = {
            def format = params.id
            if (supportedFormats.contains(format)) {
        		render(text: getFeed(format), contentType:"text/xml", encoding:"UTF-8")
            } else {
                response.sendError(response.SC_FORBIDDEN);
            }
    }

    def getFeed(feedType) {

        def issues = Bug.list(max: 5, sort: "created", order: "desc")

        def entries = []
        issues.each { issue ->
            def desc = new SyndContentImpl(type: "text/plain", value: issue.description);
            def entry = new SyndEntryImpl(title: issue.name + " - " + issue.summary,
            		link: 'http://bugga.sf.net/issues/show/' + issue.name,
            		publishedDate: issue.created, description: desc);
            entries.add(entry);

        }
        SyndFeed feed = new SyndFeedImpl(feedType: feedType, title: 'Recently Closed Bugga Issues',
                link: 'http://bugga.sf.net', description: 'Bugga issues closed in the last few days',
                entries: entries);

        StringWriter writer = new StringWriter();
        SyndFeedOutput output = new SyndFeedOutput();
        output.output(feed,writer);
        writer.close();

        return writer.toString();

    }

}

Then you just need to point your aggregator to http://localhost/bugga/feed/atom and you’re in business. Or if you want to use any of the supported feeds, head on over to http://localhost:8080/bugga/feed/all/rss_2.0 or whatever you fancy.

Here’s a grab from NewzCrawler:

Bugga Feed running in NewzCrawler

Props to the ROME team for a first class little library that does one thing very well.