ONJava.com    
 Published on ONJava.com (http://www.onjava.com/)
 See this if you're having trouble printing code examples


Using Groovy to Send Emails

by Paul King
03/23/2007

New programming languages have recently become an increasingly important discussion point. Emerging dynamic and scripting languages are being used in both client and server frameworks to achieve greater flexibility, agility, and productivity. At the same time, Domain Specific Languages (DSLs) are being used to provide greater expressivity with reduced scaffolding code and language syntax noise.

In the midst of these evolving trends sit several relatively recent dynamic languages, including Groovy. Groovy is one of the more popular dynamic languages designed to run on the JVM. It has a syntax that is very familiar to Java programmers, supports all the latest features and paradigms present in the dynamic language world, reduces large amounts of scaffolding code, and integrates seamlessly with Java. In essence, it allows you to gain from the emerging trends in language evolution while still leveraging the skills, infrastructure, and libraries you already have from the Java world.

As tempting as it might be to just rave about Groovy's great design or do a feature-by-feature comparison with other languages, in this tutorial, I try to dispense with hype (the last paragraph is more than enough!). I instead take a well-known topic (sending email) and show several ways you can do it with Groovy. Along the way we'll be looking at some of Groovy's features, and gain some appreciation of how dynamic languages can make otherwise mundane programming tasks both more productive and more fun.

A Testing Prologue

Test-driven development advocates writing a failing test before writing the production code in order to make the test pass. Let's give that a try for our email example. First, we'll write a little helper class to assist with our testing.

import org.subethamail.wiser.Wiser

class EmailFixture {
    private wiser = new Wiser()
    EmailFixture(int port) {
        wiser.port = port
        wiser.start()
    }

    def assertEmailArrived(args) {
        wiser.stop()
        assert wiser.messages.size() != 0, 'No messages arrived!'
        def message = wiser.messages[0].mimeMessage
        assert message?.subject == args['subject']
        assert message?.from[0].toString() == args['from']
    }
}

We have wrapped the Wiser email testing library inside a fixture class. Wiser acts as an SMTP server on your system and makes the messages it receives directly available to your Groovy (or Java) program. This saves you from having to use a real messaging service, keeping our test insulated from unreliable and slow external systems. Wiser's details are all hidden away inside our fixture, so you could easily swap it for an alternative like Dumbster or some other library with similar functionality.

Some points of interest:

We are now in a position to write our failing test:

fixture = new EmailFixture(port)
// ... code to send email goes here ...
fixture.assertEmailArrived(from:'cruise@myhost.org',
                           subject:'Test build')

Our test is making sure that the email arrives with the correct from address and subject, but we could extend this to other parts of the email content if we wished. Note that we are using named parameters with the assertEmailArrived method. This allows us to reorder our parameters or add new parameters without breaking existing code. Under the covers, a java.util.Map is being passed into our fixture from our calling code, but that is transparent to the Groovy developer.

I should point out that the details being sent in our email examples aren't important. We are mimicking the kind of functionality that is already built into many build systems to notify interested parties of successful (and potentially failing) builds. The same kind of functionality is needed in many ecommerce scenarios.

Streamlining Apache Commons Net

Apache Jakarta Commons Net implements the client side of over a dozen basic Internet protocols including the email protocols SMTP and POP3. The library concentrates on providing a uniform way of interacting with a wide range of protocols at a fairly low level rather than on implementing powerful higher-level abstractions. We will just be using its SMTP facilities here. It would be a good choice if you only have simple needs or if you need to interact with some of the other protocols it supports.

import org.apache.commons.net.smtp.*

port = 1025
org = 'mycompany.org'
fixture = new EmailFixture(port)

client = new SMTPClient()
client.connect('localhost', port)
client.login()

// set sender and recipient
client.sender = "cruise@$org"
client.addRecipient("devteam@$org")

// create and send header information
header = new SimpleSMTPHeader("cruise@$org",
        "devteam@$org", 'Successful build')
header.addCC("partners@$org")
header.addHeaderField('Organization', org)
writer = new PrintWriter(client.sendMessageData())
writer << header

// send body of message
writer << 'Successful build for ' + new Date()
writer.close()

client.logout()
client.disconnect()

fixture.assertEmailArrived(from: "cruise@$org",
                           subject: 'Successful build')

Groovy helped a little here, but the code is still a bit tedious. The Commons Net SMTP API does provide some more-powerful abstractions, which would have reduced the amount of code we needed to write, but none of these exactly matched our requirements. We had to operate at the API's lowest level.

Some points of interest:

Taming JavaMail

Java has its own answer to interaction with email-related systems: JavaMail. JavaMail is an optional extension for the standard edition of Java and is included in the enterprise edition. It proves to be a useful choice if you are already working in an Enterprise container or if you need the full power of handling MIME types.

Here is what our code would look like:

import javax.mail.*
import javax.mail.internet.*

port = 1025
fixture = new EmailFixture(port)

props = new Properties()
props.put('mail.smtp.host', 'localhost')
props.put('mail.smtp.port', port.toString())
session = Session.getDefaultInstance(props, null)

// Construct the message
msg = new MimeMessage(session)
devteam = new InternetAddress('devteam@mycompany.org')
partners = new InternetAddress('partners@mycompany.org')
msg.from = new InternetAddress('cruise@mycompany.org')
msg.sentDate = new Date()
msg.subject = 'Successful build'
msg.setRecipient(Message.RecipientType.TO, devteam)
msg.setRecipient(Message.RecipientType.CC, partners)
msg.setHeader('Organization', 'mycompany.org')
msg.setContent('Successful build for ' + new Date(),
               'text/plain')

// Send the message
Transport.send(msg)

fixture.assertEmailArrived(from:'cruise@mycompany.org',
                           subject:'Successful build')

Some points of interest:

Leveraging the Spring Framework

Spring is a full-stack Java/JEE application framework. It has many features that make Java development more productive, and also has great Groovy integration. The part of Spring we are most interested in is its email services, but we are also going to make use of its bean wiring capabilities.

Spring makes it easy to create and use functionality encapsulated in independent and easily testable beans. Let's create such a bean that encapsulates our email-sending functionality and call the bean BuildNotifier. It in turn will make use of Spring's underlying email capabilities.

package org.mycompany

import org.springframework.mail.*

class BuildNotifier {
    def MailSender mailSender
    def SimpleMailMessage templateMessage
    def sendEmail(internalBuild) {
        def msg = new SimpleMailMessage(templateMessage)
        msg.text = 'Successful build for ' + new Date()
        msg.from = 'cruise@mycompany.org'
        if (!internalBuild)
            msg.cc = ['partners@mycompany.org'] as String[]
        mailSender.send(msg)
    }
}

The mailSender and templateMessage beans are initialized for us by Spring. We'll see how later in the XML wiring file. Typically, any properties that don't change can actually be set in the wiring file (the to address is set in the wiring file as an example). Information that may change—e.g., the cc field, which is optionally set, and the text, which includes the current date and time—need to be set within the bean itself.

With our build notifier helper bean ready to go, we can now write our mail application again. It turns out to be fairly simple.

import org.springframework.context.support.*

ctx = new ClassPathXmlApplicationContext('beans.xml')
fixture = ctx.getBean('emailFixture')
notifier = ctx.getBean('buildNotifier')
notifier.sendEmail(false)

fixture.assertEmailArrived(from:'cruise@mycompany.org',
                           subject:'Successful build')

The final piece that is missing in our Spring example is the wiring file. This tells the Spring framework how our beans are configured together and sets any static properties. Here is what it would look like:


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/lang
http://www.springframework.org/schema/lang/spring-lang-2.0.xsd">
    <bean id="emailFixture" class="EmailFixture">
        <constructor-arg type="int" value="1025"/>
    </bean>
    <bean id="mailSender"
    class="org.springframework.mail.javamail.JavaMailSenderImpl">
        <property name="host" value="localhost"/>
        <property name="port" value="1025"/>
    </bean>
    <bean id="templateMessage"
          class="org.springframework.mail.SimpleMailMessage">
        <property name="to" value="devteam@mycompany.org"/>
        <property name="subject" value="Successful build"/>
    </bean>
    <bean id="buildNotifier" class="org.mycompany.BuildNotifier">
        <property name="mailSender" ref="mailSender"/>
        <property name="templateMessage" ref="templateMessage"/>
    </bean>
</beans>

Note that properties such as port, host, the to address, and the subject are all constant fields and are defined here in one file for easy reconfiguration. See the Spring documentation for further details about how we could even supply the build notifier bean as an embedded Groovy script in the wiring file itself.

Towards a DSL: Groovy + Ant

Groovy also provides Ant integration through its AntBuilder class. This class follows a special DSL-style pattern of coding supported by Groovy's built-in builder facilities and bundled builder classes. Builders are most useful for creating any kind of nested data structure, whether this be a straight XML file, a hierarchical object structure, or nested widgets in a GUI. AntBuilder builds Ant projects that contain a nested set of build instructions. It interfaces directly with the Ant API, so the normal Ant XML build file is skipped altogether. The advantage is that AntBuilder code tends to be less verbose than the equivalent XML and can make use of Groovy features such as conditional statements and loops, as during construction of the Ant project.

Ant supports sending emails through its mail task. Here is what our code looks like:

port = 1025
fixture = new EmailFixture(port)

ant = new AntBuilder()
ant.mail(mailhost:'localhost', mailport:"$port",
         subject:'Successful build'){
    from(address:'cruise@mycompany.org')
    cc(address:'partners@mycompany.org')
    to(address:'devteam@mycompany.org')
    message("Successful build for ${new Date()}")
}

fixture.assertEmailArrived(from:'cruise@mycompany.org',
                           subject:'Successful build')

Writing this class was the easiest of all the solutions and has yielded short, easy-to-maintain code. But we could do a lot more if we wanted. Suppose we wanted to send attachments of the build artifacts along with our notification email. Also suppose we wanted the content of the message to be HTML instead of the simple text content used in previous examples. Sound like a lot of work? It really isn't. Here is what the result would look like:

results = [Unit: '898 Tests, 0 Failures, 0 Errors',
           Integration: '45 Tests, 0 Failures, 0 Errors',
           Acceptance: '75 Tests, 0 Failures, 1 Errors']
ant = new AntBuilder()
writer = new StringWriter()
today = new Date()
allOk = results.every{ entry ->
    entry.value.contains(' 0 Errors') &&
    entry.value.contains(' 0 Failures')
}
new groovy.xml.MarkupBuilder(writer).html {
    body {
        h1(allOk ? 'Successful' : 'Failed' + " build for $today")
        table {
            tr {
                th('Test Suite'); th('Result')
            }
            results.each { suite, result ->
                numbers = result.split('').grep(~/\d| /).join()
                passed = numbers.endsWith('0  0 ')
                tr {
                    td(suite)
                    td(bgcolor: passed?'green':'red', result)
                }
            }
        }
    }
}
ant.mail(mailhost:'localhost', messagemimetype:'text/html',
         subject:"Build notification for $today"){
    from(address:'cruise@mycompany.org')
    to(address:'devteam@mycompany.org')
    cc(address:'partners@mycompany.org')
    message(writer)
    attachments(){
        fileset(dir:'dist'){
              include(name:'**/*.zip')
        }
    }
}

Here, build results would typically be extracted from logfiles; for our purposes, we just used a list. We have also ignored the test fixture (but see the next section, where we change the test fixture to use a real email server).

If you point this to a real mail server, you will receive an email similar to the one shown in Figure 1.

Screenshot of received Email
Figure 1. Screenshot of received email

A Testing Epilogue

To wrap up, we are going to look at testing our code against a real email server instead of the Wiser mock server. We'll simply replace the code inside our email fixture with code to access a real email server using the POP3 protocol. We'll use the Canoo WebTest open source testing tool. It is written as an Ant extension, so we can follow the same coding style as shown for sending our email via Ant. Here is what the result would look like:

def ant = new AntBuilder()

def webtest_home = System.properties.'webtest.home'

ant.taskdef(resource:'webtest.taskdef'){
    classpath(){
       pathelement(location:"$webtest_home/lib")
       fileset(dir:"$webtest_home/lib", includes:"**/*.jar")
    }
}

ant.testSpec(name:'Email Test'){
    steps {
        emailSetConfig(server:'localhost', password:'password',
            username:'devteam@mycompany.org', type:'pop3')
        emailStoreMessageId(subject:'/Build notification/',
            property:'msg')
        emailStoreHeader(property:'subject',
            messageId:'#{msg}', headerName:'Subject')
        groovy('''def subject = step.webtestProperties.subject
            assert subject.startsWith('Build notification')''')
        emailMessageContentFilter(messageId:'#{msg}')
        verifyText(text:'Failed build')
    }
}

Some points of interest:

That wraps up our quick tour of some of the common ways to send emails. I certainly haven't tried to provide you with an exhaustive list of ways to send emails from Groovy. For instance, you could use platform-specific means just as easily if portability across platforms is not a high priority. As an example, on Windows, you could write some code that uses Groovy's Scriptom module to talk directly to Outlook and ask it to send an email for you.

Conclusion

Groovy allows you to send emails easily. This isn't because the Groovy language designers particularly set out to make this task easy but because they designed into the language a cohesive set of features that can leverage one anther. This design makes the language productive in many scenarios. In our examples, we made use of some of Groovy's features that support Agile development, its unsurpassed Java integration, the neat syntax for doing closures, and the great support for writing your own DSLs, including the builder concept. Groovy is not alone in having many of these features. Other languages have them as well. Groovy does, however, package these features in a very cohesive way, and this puts the fun back into programming. Even mundane tasks like sending emails are a pleasure with Groovy!

Resources

Return to ONJava.com.

Copyright © 2009 O'Reilly Media, Inc.