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 1

08.18.2009
| 164204 views |
  • submit to reddit

Adding confirmation e-mails

[The source for this section is available here]
After an enrollment advisor (or some other staff member) creates a lead in the system, we want to send the lead an e-mail letting him know that that's happened. Actually—and this is a critical point—we really don't care how the lead was created. Anytime a lead appears on the newLeadChannel, we want to fire off a confirmation e-mail. I'm making the distinction because it points to an important aspect of the message bus: it allows us to control lead processing code centrally instead of having to chase it down in a bunch of different places. Right now there's only one way to create leads, but figure 2 revealed that we'll be adding others. No matter how many we add, they'll all result in sending a confirmation e-mail out to the lead.

Figure 4 shows the new bit of plumbing we're going to add to our message bus.

Figure 4. Send a confirmation e-mail when creating a lead.


To do this, we're going to need to make a few changes to the configuration and code.

POM changes

First we need to update the POM. Here's a summary of the changes; see the code download for details:
    •    Add a JavaMail dependency to the Jetty plug-in.
    •    Add an org.springframework.context.support dependency.
    •    Add a spring-integration-mail dependency.
    •    Set the mail.version property.

These changes will allow us to use JavaMail.

Expose JavaMail sessions through JNDI

We'll also need to add a /WEB-INF/jetty-env.xml configuration to make our JavaMail sessions available via JNDI. Once again, see the code download for details. I've included a /WEB-INF/jetty-env.xml.sample configuration for your convenience. As mentioned previously, you'll need access to an SMTP server.
Besides creating jetty-env.xml, we'll need to update applicationContext.xml. Listing 6 shows the changes we need so we can use JavaMail and SMTP.

Listing 6. /WEB-INF/applicationContext.xml changes supporting JavaMail and SMTP

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">

<jee:jndi-lookup id="mailSession"
jndi-name="mail/Session" resource-ref="true" />

<bean id="mailSender"
class="org.springframework.mail.javamail.JavaMailSenderImpl"
p:session-ref="mailSession" />

<context:component-scan base-package="crm.service" />
</beans>

The changes expose JavaMail sessions as a JNDI resource. We've declared the jee namespace and its schema location, configured the JNDI lookup, and created a JavaMailSenderImpl bean that we'll use for sending mail.
We won't need any domain model changes to generate confirmation e-mails. We will however need to create a bean to back our new transformer endpoint.

Service integration tier changes

First, recall from figure 4 that the newLeadChannel feeds into a LeadToEmailTransformer endpoint. This endpoint takes a lead as an input and generates a confirmation e-mail as an output, and the e-mail gets pipes out to an SMTP transport. In general, transformers transform given inputs into desired outputs. No surprises there.
Figure 4 is slightly misleading since it's actually the POJO itself that we're going to call LeadToEmailTransformer; the endpoint is really just a bean adapter that the messaging infrastructure provides so we can place the POJO on the message bus. Listing 7 presents the LeadToEmailTransformer POJO.

Listing 7. LeadToEmailTransformer.java, a POJO to generate confirmation e-mails

package crm.integration.transformers;

import java.util.Date;
import java.util.logging.Logger;
import org.springframework.integration.annotation.Transformer;
import org.springframework.mail.MailMessage;
import org.springframework.mail.SimpleMailMessage;
import crm.model.Lead;

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

private String confFrom;
private String confSubj;
private String confText;

... getters and setters for the fields ...

@Transformer
public MailMessage transform(Lead lead) {
log.info("Transforming lead to confirmation e-mail: " + lead);

String leadFullName = lead.getFullName();
String leadEmail = lead.getEmail();
MailMessage msg = new SimpleMailMessage();

msg.setTo(leadFullName == null ?
leadEmail : leadFullName + " <" + leadEmail + ">");

msg.setFrom(confFrom);
msg.setSubject(confSubj);
msg.setSentDate(new Date());
msg.setText(confText);

log.info("Transformed lead to confirmation e-mail: " + msg);
return msg;
}
}

Again, LeadToEmailTransformer is a POJO, so we use the @Transformer annotation to select the method that's performing the transformation. We use a Lead for the input and a MailMessage for the output, and perform a simple transformation in between.

When defining backing beans for the various Spring Integration filters, it's possible to specify a Message as an input or an output. That is, if we want to deal with the messages themselves rather than their payloads, we can do that. (Don't confuse the MailMessage in listing 7 with a Spring Integration message; MailMessage represents an e-mail message, not a message bus message.) We might do that in cases where we want to read or manipulate message headers. In this tutorial we don't need to do that, so our backing beans just deal with payloads.

Now we'll need to build out our message bus so that it looks like figure 4. We do this by updating applicationContext-integration.xml as shown in listing 8.

Listing 8. /WEB-INF/applicationContext-integration.xml updates to support confirmation e-mails

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:mail="http://www.springframework.org/schema/integration/mail"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/integration/mail
http://www.springframework.org/schema/integration/mail/spring-integration-mail-1.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd">

<context:property-placeholder
location="classpath:applicationContext.properties" />

<gateway id="leadGateway"
service-interface="crm.integration.gateways.LeadGateway" />

<publish-subscribe-channel id="newLeadChannel" />

<service-activator
input-channel="newLeadChannel"
ref="leadService"
method="createLead" />

<transformer input-channel="newLeadChannel" output-channel="confEmailChannel">
<beans:bean class="crm.integration.transformers.LeadToEmailTransformer">
<beans:property name="confFrom" value="${conf.email.from}" />
<beans:property name="confSubject" value="${conf.email.subject}" />
<beans:property name="confText" value="${conf.email.text}" />
</beans:bean>
</transformer>

<channel id="confEmailChannel" />

<mail:outbound-channel-adapter
channel="confEmailChannel"
mail-sender="mailSender" />

</beans:beans>

The property-placeholder configuration loads the various ${...} properties from a properties file; see /crm/src/main/resources/applicationContext.properties in the code download. You don't have to change anything in the properties file.

The transformer configuration brings the LeadToEmailTransformer bean into the picture so it can transform Leads that appear on the newLeadChannel into MailMessages that it puts on the confEmailChannel. As a side note, the p namespace way of specifying bean properties doesn't seem to work here (I assume it's a bug: http://jira.springframework.org/browse/SPR-5990), so I just did it the more verbose way.

The channel definition defines a point-to-point channel rather than a pub-sub channel. That means that only one endpoint can pull messages from the channel.

Finally we have an outbound-channel-adapter that grabs MailMessages from the confEmailChannel and then sends them using the referenced mailSender, which we defined in listing 6.

That's it for this section. We should have working confirmation e-mails. Restart your Jetty instance and go again to
http://localhost:8080/crm/main/lead/form.html
Fill it out and provide your real e-mail address in the e-mail field. A few moments after submitting the form you should receive a confirmation e-mail. If you don't see it, you might check your SMTP configuration in jetty-env.xml, or else check your spam folder.

 Summary

In this tutorial we've taken our first steps toward developing an integrated lead management system. Though the current bus configuration is simple, we've already seen some key Spring Integration features, including
    •    support for the Gateway pattern, allowing us to connect apps to the message bus without knowing about messages
    •    point-to-point and pub-sub channels
    •    service activators to allow us to place service beans on the bus
    •    message transformers
    •    outbound SMTP channel adapters to allow us to send e-mail
The second tutorial will continue elaborating what we've developed here, demonstrating the use of several additional Spring Integration features, including
    •    message routers (including content-based message routers)
    •    outbound web service gateways for sending SOAP messages
    •    inbound HTTP adapters for collecting HTML form data from external systems
    •    inbound e-mail channel adapters (we'll use IMAP IDLE, though POP and IMAP are also possible) for processing incoming e-mails
Enjoy, and stay tuned.

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/.

AttachmentSize
spring-integration-demo-3.zip18.84 KB
spring-integration-demo-4.zip21.61 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

Koen VT replied on Thu, 2009/08/20 - 2:32am

Interesting article. But what if the system failed to create the Lead in the database? Let's say that the database is down. Will the confirmation email still be sent? Can Spring Integration deal with such problems?

Comment viewing options

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