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
| 121483 views |
  • submit to reddit

Create the domain model

We’ll need a domain object for leads, so listing 1 shows the one we’ll use. It’s not an industrial-strength representation, but it will do for the purposes of the tutorial.

Listing 1. Lead.java, a basic domain object for leads.

package crm.model;

... other imports ...

public class Lead {
private static DateFormat dateFormat = new SimpleDateFormat();

private String firstName;
private String middleInitial;
private String lastName;
private String address1;
private String address2;

... other fields ...

public Lead() { }

public String getFirstName() { return firstName; }

public void setFirstName(String firstName) {
this.firstName = firstName;
}

... other getters and setters, and a toString() method ...
}

There is nothing special happening here at all. So far the Lead class is just a bunch of getters and setters. You can see the full code listing in the download.
If you thought that was underwhelming, just wait until you see the LeadServiceImpl service bean in listing 2.

Listing 2. LeadServiceImpl.java, a dummy service bean.

package crm.service;

import java.util.logging.Logger;
import org.springframework.stereotype.Service;
import crm.model.Lead;

@Service("leadService")
public class LeadServiceImpl implements LeadService {
private static Logger log = Logger.getLogger("global");

public void createLead(Lead lead) {
log.info("Creating lead: " + lead);
}
}

This is just a dummy bean. In real life we’d save the lead to a database. The bean implements a basic LeadService interface that we've suppressed here, but it's available in the code download.
Now that we have our domain model, let’s use Spring Integration to create a service integration tier above it.

Create the service integration tier

If you look back at figure 3, you’ll see that the CRM app pushes lead data to the service bean by way of a channel called newLeadChannel. While it’s possible for the CRM app to push messages onto the channel directly, it’s generally more desirable to keep the systems you’re integrating decoupled from the underlying messaging infrastructure, such as channels. That allows you to configure service orchestrations dynamically instead of having to go into the code.

Spring Integration supports the Gateway pattern (described in the aforementioned Enterprise Integration Patterns book), which allows an application to push messages onto the message bus without knowing anything about the messaging infrastructure. Listing 3 shows how we do this.

Listing 3. LeadGateway.java, a gateway offering access to the messaging system.

package crm.integration.gateways;

import org.springframework.integration.annotation.Gateway;
import crm.model.Lead;

public interface LeadGateway {

@Gateway(requestChannel = "newLeadChannel")
void createLead(Lead lead);
}


We are of course using the Spring Integration @Gateway annotation to map the method call to the newLeadChannel, but gateway clients don’t know that. Spring Integration will use this interface to create a dynamic proxy that accepts a Lead instance, wraps it with an org.springframework.integration.core.Message, and then pushes the Message onto the newLeadChannel. The Lead instance is the Message body, or payload, and Spring Integration wraps the Lead because only Messages are allowed on the bus.
We need to wire up our message bus. Figure 4 shows how to do that with an application context configuration file.

Listing 4. /WEB-INF/applicationContext-integration.xml message bus definition.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:beans="http://www.springframework.org/schema/beans"
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/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd">

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

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

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

 
The first thing to notice here is that we've made the Spring Integration namespace our default namespace instead of the standard beans namespace. The reason is that we're using this configuration file strictly for Spring Integration configuration, so we can save some keystrokes by selecting the appropriate namespace. This works pretty nicely for some of the other Spring projects as well, such as Spring Batch and Spring Security.
In this configuration we've created the three messaging components that we saw in figure 3. First, we have an incoming lead gateway to allow applications to push leads onto the bus. We simply reference the interface from listing 3; Spring Integration takes care of the dynamic proxy. Next we create a publish/subscribe ("pub-sub") channel called newLeadChannel. This is the channel that the @Gateway annotation referenced in listing 3. A pub-sub channel can publish a message to multiple endpoints simultaneously. For now we have only one subscriber—a service activator—but we already know we're going to have others, so we may as well make this a pub-sub channel.

The service activator is an endpoint that allows us to bring our LeadServiceImpl service bean onto the bus. We're injecting the newLeadChannel into the input end of the service activator. When a message appears on the newLeadChannel, the service activator will pass its Lead payload to the leadService bean's createLead() method.
Stepping back, we've almost implemented the design described by figure 3. The only part that remains is the lead creation frontend, which we'll address right now.

Create the web tier

Our user interface for creating new leads will be a web-based form that we implement using Spring Web MVC. The idea is that enrollment staff at campuses or call centers might use such an interface to handle walk-in or phone-in traffic. Listing 5 shows our simple @Controller.

Listing 5. LeadController.java, a @Controller to allow staff to create leads

package crm.web;

import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import crm.integration.gateways.LeadGateway;
import crm.model.Country;
import crm.model.Lead;

@Controller
public class LeadController {

@Autowired
private LeadGateway leadGateway;

@RequestMapping(value = "/lead/form.html", method = RequestMethod.GET)
public void getForm(Model model) {
model.addAttribute(Country.getCountries());
model.addAttribute(new Lead());
}

@RequestMapping(value = "/lead/form.html", method = RequestMethod.POST)
public String postForm(Lead lead) {
lead.setDateCreated(new Date());
leadGateway.createLead(lead);
return "redirect:form.html?created=true";
}
}

This isn't an industrial-strength controller as it doesn't do HTTP parameter whitelisting (for example, via an @InitBinder method) and form validation, both of which you would expect from a real implementation. But the main pieces from a Spring Integration perspective are here. We're autowiring the gateway into the @Controller, and we have methods for serving up the empty form and for processing the submitted form. The getForm() method references a Countries class that we've suppressed (it's in the code download); it just puts a list of countries on the model so the form can present a Country field to the staff member. The postForm() method invokes the createLead() method on the gateway. This will pass the Lead to the dynamic proxy LeadGateway implementation, which in turn will wrap the Lead with a Message and then place the Message on the newLeadChannel.

There are a few other configuration files you will need to put in place, including web.xml, main-servlet.xml and applicationContext.xml. There's also a JSP for the web form. As none of these relates directly to Spring Integration, we won't treat them here. Please see the code download for details.
With that, we've established a baseline system. To try it out, run
  mvn jetty:run
against crm/pom.xml and point your browser at
  http://localhost:8080/crm/main/lead/form.html

You should see a very basic-looking web form for entering lead information. Enter some user information (it doesn't matter what you enter—recall that we don't have any form validation) and press Submit. The console should report that LeadServiceImpl.createLead() created a lead. Congratulations!

Even though we now have a working system, it isn't very interesting. From here on out (this tutorial and the next) we'll be adding some common features to make the lead management system more capable. Our first addition will be confirmation e-mails; the next tutorial will present further additions.

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.