Sunday, April 5, 2009

Use Apache Camel plugin in Grails

We've learned how to use Apache Camel from Groovy code. In this post we learn how to use Apache Camel in a Grails application.

To include Apache Camel in a Grails application is so easy, thanks to the plugin system of Grails. We can install Camel with the Camel plugin. This plugin will add the Camel classes to our application, the possbility to send and route messages directly from services and controllers and a new Grails artifact: routes. We are going to install the plugin and create a new route which will poll a Gmail account and save the attachment to a specified directory.

$ grails install-plugin camel
$ grails create-route GetMail

In the directory grails-app/routes we have a new file GetMailRoute.groovy. The great thing is we can use closures for the filter, when and process methods from the Java DSL. The following code fragment shows how we can poll for e-mails and save the attachment to a directory. We use values from the Grails configuration in our code for mail username and password and the directory we want the files to be saved.:

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

class GetMailRoute {
    def configure = {
        def config = ConfigurationHolder.config

        from("imaps://imap.gmail.com?username=" + config.camel.route.gmail.username
            + "&password=" + config.camel.route.gmail.password
            + "&folderName=GoogleAnalytics"
            + "&consumer.delay=" + config.camel.route.gmail.pollInterval)
        .filter { 
        .process{ exchange ->
            exchange.in.attachments.each { attachment ->
                def datahandler = attachment.value
                def xml = exchange.context.typeConverter.convertTo(String.class, datahandler.inputStream)
                def file = new File(config.camel.route.save.dir, datahandler.name) << xml
                log.info "Saved " + file.name

That's it. We now have created our own route and when we start the application the route is executed. The only thing we need to do is to include the JavaMail libraries, because they are used by the Camel mail component. Therefore we copy activation.jar and mail.jar to the lib of the Grails application.


Kimble said...

Yet another nice post!

Do you think it would be possible to create routes run time? If I want to poll multiple email addresses defined in a database.

mrhaki said...

@Kimble: maybe you can use the Camel JDBC component (http://camel.apache.org/jdbc.html) to achieve the desired effect. Then you don't need to create routes dynamically.

Anonymous said...

Is it possible to use Grape/Ivy within the Grails app to pull the dependencies - activation.jar?

mrhaki said...

@Anonymous: For Grails 1.1.x applications you can use the ivy plugin: http://mrhaki.blogspot.com/2009/05/use-ivys-dependency-mechanisme-with.html.
Grails 1.2 has built-in dependency management which uses Ivy under the hood.

dman123 said...

you can actually define as many routes as you want within a single RouteBuilder class (at runtime)

Pavan Kumar said...

When I use this code on a gmail account, it reads the mail (I know this as the unread mails are being marked as read).
But it is unable to create a new file or even log its name to console.

I have included the dependencies activation.jar and mail.jar.
Is thr anything else I need to include?

Thank You

Maurus Vitor said...

hello. i am currently trying this groovy code. i am accessing the gmail account but there is an error everytime it gets the attachment from it. how can i customize this: config.camel.route.save.dir?

Hubert Klein Ikkink said...

@Maurus Vitor: It has been a while since I wrote the blog post, but I think you can set or override it in grails-app/conf/Config.groovy.

Charsee said...

Gr8 post :) but how can we parse body of the email.

Charsee said...

@Hubert: please help me out

Hubert Klein Ikkink said...

@Charsee: I think it is exchange.in.body. This should access the body content of an e-mail message. The Exchange object has a method getIn() that returns a Message object. And the Message object has a getBody() method.

Post a Comment