Enterprise Integration Zone is brought to you in partnership with:

Willie Wheeler is a Principal Applications Engineer with Expedia, working on continuous delivery, including build automation, test automation, configuration management and application performance management. He's also the lead author of the book Spring in Practice (Manning). Willie is a DZone MVB and is not an employee of DZone and has posted 23 posts at DZone. You can read more from them at their website. View Full User Profile

Spring Integration: A Hands-On Tutorial Part 2

08.26.2009
| 52923 views |
  • submit to reddit

Integrating legacy e-mail-based RFIs

[The code for this section is available here]

The last piece of our integration will be a legacy channel for e-mail based RFIs. See Figure 3 below, which represents the final product.

  Figure 3: The final product: an integrated lead management module.

The approach here is to connect our message bus up to an IMAP mailbox, and treat the mailbox like a message queue. When people submit e-mails to the mailbox in question, our message bus will pick them up and process them.

WARNING: THIS WILL DELETE ALL MESSAGES IN YOUR MAILBOX! Do NOT use your personal e-mail account unless you don't mind having all of your messages deleted!

I learned that lesson the hard way, thinking that because it's an IMAP mailbox, the mail will stay on the server. That's the way IMAP works, but it's not IMAP eating up the e-mails. It's Spring Integration (and it's by design). Anyway, you've been warned!

Here are the changes we'll need to make.

Update Lead.java to support notes

First, because e-mail is mostly unstructured, we're going to need a way to capture whatever it is that the prospective student wants to say. So we'll add a notes property to Lead.java. Additionally, we'll add a method to extract the first and last names from the sender's full name. See listing 6 below.

Listing 6. Updates to Lead.java

 

package crm.model;

import java.util.List;
import org.springframework.util.StringUtils;

... various imports ...

... same annotations as before ...
public class Lead {
private List<String> notes;

... various fields and methods ...

@XmlTransient
public List<String> getNotes() { return notes; }

public void setNotes(List<String> notes) { this.notes = notes; }

public void guessNamesFromFullName(String fullName) {
if (fullName == null) { return; }
String[] tokens = fullName.trim().split("\\s+");
int len = tokens.length;
if (len == 0) {
return;
} else if (len == 1) {
if (!tokens[0].equals("")) { setFirstName(tokens[0]); }
} else {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < len - 1; i++) {
builder.append(tokens[i] + " ");
}
setFirstName(builder.toString().trim());
setLastName(tokens[len - 1]);
}
}

... minor toString() update (see code download) ...
}

 

here's enough logic in the guessNamesFromFullName() method to warrant a unit test, and indeed the sample code includes a unit test that covers this method. Basically it would break a name like "Willie Wheeler" down into "Willie" and "Wheeler".

We're going to need to transform e-mails into leads, and we'll look at that now.

Create EmailToLeadTransformer.java

Listing 7 contains the transformer bean for converting e-mails into leads.

Listing 7. EmailToLeadTransformer.java

 

 

package crm.integration.transformers;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.springframework.integration.annotation.Transformer;
import crm.model.Lead;

public class EmailToLeadTransformer {
private static Logger log = Logger.getLogger("global");

@Transformer
public Lead transform(MimeMessage email) {
log.info("Transforming e-mail to lead");
try {
InternetAddress from = (InternetAddress) email.getFrom()[0];
String fullName = from.getPersonal();

Lead lead = new Lead();
lead.guessNamesFromFullName(fullName);
lead.setEmail(from.getAddress());
lead.setDateCreated(email.getSentDate());

StringBuilder builder = new StringBuilder("Full name: " + fullName);
builder.append("\nSubject: " + email.getSubject());

// FIXME Doesn't work with MimeMultipart. Output looks like this:
// javax.mail.internet.MimeMultipart@598d00
builder.append("\n\n" + email.getContent().toString());

List<String> notes = new ArrayList<String>();
notes.add(builder.toString());
lead.setNotes(notes);

log.info("Transformed e-mail to lead: " + lead);
return lead;
} catch (MessagingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

 

 

The code in listing 7 isn't anywhere near industrial-strength: it doesn't, for example, handle the common case of multipart e-mails. We can call that one an exercise left to the reader. (Ha ha.) But it does feature the basics of extracting the name, e-mail address, date, subject and yes, even the body if you're not sending a multipart e-mail.

The piece we really care about, though, is how to get the e-mail onto the message bus in the first place, and that's the topic of the next section.

3.3 Getting e-mail messages onto the message bus

There are at least three different options for getting e-mail messages onto the message bus:

  • Poll a POP3 mailbox

  • Poll an IMAP mailbox

  • Receive e-mail pushes from an IMAP mailbox that supports the IMAP IDLE feature (basically an e-mail push mechanism)

We're going to do the third one since it's easy to get an IMAP mailbox that supports IDLE (e.g., Gmail). To do that, we're going to need to update our configuration in a couple of ways. First, we'll need to create an imap.properties file with the relevant mailbox configuration. See the sample file at /crm/src/resources/imap.properties.sample for an example of that.

I'll repeat my previous warning here: the messages in the mailbox you choose will be deleted. Don't use your personal e-mail account or any other e-mail account where you don't want the whole thing to be wiped out.

OK. Listing 8 shows the updates we need to make to applicationContext-integration.xml.

Listing 8. Updates to /WEB-INF/applicationContext-integration.xml.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
... various namespace and schema location declarations ... >

<!-- Note: This requires Spring Integration 1.0.3 or higher. -->
<context:property-placeholder location="classpath:*.properties" />

<!-- Use this if your server supports IMAP IDLE. Requires JavaMail 1.4.1
or higher on the client side. -->
<mail:imap-idle-channel-adapter
channel="emailLeadChannel"
store-uri="${email.store.uri}"
should-delete-messages="true" />

<!-- Use this if your server doesn't support IMAP IDLE. -->
<!--
<mail:inbound-channel-adapter
channel="emailLeadChannel"
store-uri="${email.store.uri}">
<poller max-messages-per-poll="3">
<interval-trigger interval="30" time-unit="SECONDS" />
</poller>
</mail:inbound-channel-adapter>
-->

<channel id="emailLeadChannel" />

<transformer input-channel="emailLeadChannel" output-channel="newLeadChannel">
<!-- Use inner bean instead of ref because no one else uses this bean -->
<beans:bean class="crm.integration.transformers.EmailToLeadTransformer" />
</transformer>

... other stuff ...

</beans:beans>

The change to the property placeholder piece is just that we now have two properties files to load instead of one. (Recall that we just added imap.properties.)

The more significant change is the addition of the IMAP IDLE channel adapter, which basically sets the message bus up to listen for e-mail notifications from the IMAP mailbox. I've included the should-delete-messages="true" attribute here just to emphasize its existence; it is however unnecessary since the default value is true. (As an aside, I'm not sure I like the default value being true. I understand that this needs to be true if we want to use the mailbox as a message queue, but man, it's a bummer to accidentally delete four or five years' worth of e-mail you've been saving. I might suggest that the Spring Integration team should remove the default value altogether and make this attribute required. Anyway.) If your mailbox doesn't support IDLE, you can use the alternative adapter configuration, which polls on an interval that you can set. And again, there's a POP3 adapter as well if you want that.

The rest of it is stuff we've already seen.

Once you have it all in, start up the CRM app, and send an e-mail to the IMAP mailbox you specified. You should receive a confirmation e-mail shortly thereafter.

Summary

This tutorial completes a two-part series on the basics of Spring Integration. In the first tutorial we provided a bird's eye view of the framework, and then got started with some simple integration. In this second tutorial we completed the work we started. The end result is a fairly capable bus, and one that can be easily reconfigured.

The bus we build is really only the starting point. Besides the router, transformer, gateway, channel and channel adapter components we've seen here, there are several others we haven't covered, including:

  • Filter: Decide whether to drop a message

  • Splitter: Split a message into multiple messages

  • Aggregator: Collapse multiple messages into a single message

  • Resequencer: Orders a group of messages

  • Delayer: Delays the propagation of messages

  • Message Handler Chain: Provides a convenient way to sequence a series of endpoints

  • Messaging Bridge: Connect two channels or channel adapters

In addition to the components above, there are several gateways and channel adapters that you might explore on your own, including those for

  • File support

  • JMS support

  • Web services support (both inbound and outbound)

  • RMI support

  • HttpInvoker support

  • Stream support

  • ...and more

Needless to say, there's a good deal of capability here, and over time I would expect to see more patterns from Enterprise Integration Patterns make their way into the framework.

Some other frameworks/platforms you might find interesting are:

Until next time, have fun!

Willie is a solutions architect with 12 years of Java development experience. He and his brother John are coauthors of the upcoming book Spring in Practice by Manning Publications (www.manning.com/wheeler/). Willie also publishes technical articles (including many on Spring) to wheelersoftware.com/articles/.

Don't forget your voucher code, dzone30 if you purchase the book.

If you enjoyed this article, you'll like Part 1 of the series as well as Willie's other article covering Spring Batch.

 

 

 

 

AttachmentSize
spring-integration-demo-5.zip29.93 KB
spring-integration-demo-6.zip42.65 KB
spring-integration-demo-7.zip44.91 KB
Published at DZone with permission of Willie Wheeler, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Morali Julien replied on Wed, 2010/02/24 - 12:52pm

Hi Willie,

 

Thx for this post, it was really helpfull to me.

Still, I have a problem configuring an IMAP access to a gmail account.

As GMail's usernames are "xxx@gmail.com", the store-uri parameter is incorrect because of the "@"which is considered as a separator...

 I read that you mentionned a gmail account for testing, so did you manage to solve this problem ???

 

Thx by advance for your help !

Julien.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.