Welcome!

Java Authors: Maureen O'Gara, Liz McMillan, Walter H. Pinson, III, Yakov Werde, Tony Bishop

Related Topics: Java

Java: Article

Mailets and Matchers

Using Apache James and JavaMail to implement variable envelope return paths

James ships with a number of Mailets and Matchers that you can use without writing a line of code, but the developers at Javazon will have to write their own Matcher and Mailet to handle the bounces generated from their e-mail campaigns.

So the first thing they're going to need to do is create a class that intercepts the bounced e-mails. A matcher class can be created in one of two ways: a) create a class that implements the org.apache.mailet.Matcher interface, or b) create a class that extends the org.apache.mailet.GenericMatcher class. Because GenericMatcher already implements both Matcher and MatcherConfig and because it provides a simple version of the lifecycle methods, the path of least resistance is to extend the GenericMatcher. The NewsletterMatcher class is going to "match" only the recipients where the address of the recipient starts with the string "deals-":

public class NewsletterMatcher extends GenericMatcher {

public Collection match(Mail mail) throws MessagingException {
      Collection matches = new ArrayList();

Collection recipients = mail.getRecipients();

for (Iterator i=recipients.iterator(); i.hasNext();) {
      String recipient = (String)i.next();

if (recipient.startsWith("deals-")) {
      recipients.add(recipient);

}

}

return matches;

}

}

The NewsletterMatcher class, as you can see, returns a Collection of String objects, each presumably an e-mail that has bounced. To do something with these matches, developers will need to write a class that either implements the org.apache.mailet.Mailet interface or a class that extends the org.apache.mailet.GenericMailet class. Again, it will be simpler to extend the GenericMailet class:

public class NewsletterMailet extends GenericMailet {

private static CustomerManager mgr = CustomerManager.getInstance();

public void service(Mail mail) throws MessagingException {
Collection recipients = mail.getRecipients();

for (Iterator i=recipients.iterator(); i.hasNext();) {
String recipient = (String)i.next();

if (recipient.startsWith("deals-")) {

int atIndex = recipient.indexOf("@");
String rec = recipient.substring(0,atIndex)
.replaceAll("=", "@")
.replaceAll("deals-", "");

mgr.recordBounce(rec);

mail.setState(Mail.GHOST);
}

}

}

}

In the example above, the NewsletterMailet class overrides the service() method in the GenericMailet class, loops over the list of recipients in the given e-mail message and then checks to see if the recipient e-mail address starts with the string "deals-". If the recipient e-mail address starts with "deals-" then the class decodes the original recipient address by retrieving what is generally the username part of the e-mail address, replacing the equals sign (=) with an @ sign and then replacing the "deals-" prefix. Then the Newsletter mailet class uses CustomerManager (a class that the Javazon developers use to manage customer information) to record the bounced e-mail. If you were to step through the process, you'd see the recipient e-mail address start something like this:

deals-ajohnson=cephas.net@javazon.com
and then change to this:

    deals-ajohnson=cephas.net

and finally to this:

    ajohnson@cephas.net

The last step is to wire the mailet and matcher classes together in the Apache James configuration file, which is usually located here:

$JAMES/apps/james/SAR-INF/config.xml

You'll need to make a number of entries. First, you'll need to let James know where it should look for the mailet and matcher classes you've created by creating <mailetpackage> and <matcherpackage> entries inside the <mailetpackages> and <matcherpackages> elements:

    <mailetpackages>
...
<mailetpackage>com.javazon.mailets</mailetpackage>
      </mailetpackages>
      <matcherpackages>
...
<matcherpackage>com.javazon.matchers</matcherpackage>
    </matcherpackages>

Then add references to the matcher and the mailet using a <mailet> element like this:

<mailet match="NewsletterMatcher" class="NewsletterMailet">
      <processor>transport</processor>
</mailet>

The match attribute of the mailet element specifies the name of the matcher class that should be instantiated when the matcher is invoked by the spool processor and the class attribute specifies the name of the mailet that you want invoked should the matcher class return any hits.

After adding these configuration entries and adding the compiled classes to the $JAMES/apps/james/SAR-INF/lib/ directory, restart the James process.

Testing
To test the configuration/application, you'll have to have a James server configured and available via the Internet via port 25 with a valid DNS name and a corresponding MX record. As an example, the system administrator at Javazon would configure a machine with James, make it available to the Internet on port 25, and assign it a domain name like bounces.javazon.com. The developers could then send an invalid e-mail using JavaMail to:

bounceme@javazon.com

(an account that probably doesn't exist on the main javazon.com mail server) with a return path of:

deals-bounceme=javazon.com@bounces.javazon.com.

The bounced e-mail will be sent to the server associated with the MX record for the domain name bounces javazon.com, which should be the server the system administrator set up above. The NewsletterMatcher class should 'match' on the "deals-" prefix and then pass it to the NewsletterMailet, which should record the bounce using the CustomerManager instance.

Conclusion
After reading this article, you should hurry on over to the Apache James Web site, download the latest distribution, and read the documentation. There are a number of other interesting ways you can improve your e-mail processing by extending James using mailets and matchers.

References
JavaMail

VERP Apache James

More Stories By Aaron Johnson

Aaron Johnson is a senior software engineer at Jive Software. He lives with his wonderful wife, young son and dog in a Portland, Oregon. You can find out more by reading his blog at http://cephas.net/blog/.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.