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


Elaborate the Lead class and annotate it for OXM

Listing 1 shows our updated Lead class. Please see the code download for the full version.

Listing 1. Annotating Lead.java to support object/XML mapping

package crm.model;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement
@XmlType(propOrder = {
"firstName", "lastName",
"address1", "address2", "city", "stateOrProvince", "postalCode", "country",
"homePhone", "workPhone", "mobilePhone", "email" })
public class Lead {

... various fields (firstName, etc.) ...

public Lead() { }

@XmlElement(name = "givenName")
public String getFirstName() { return firstName; }

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

@XmlTransient
public String getMiddleInitial() { return middleInitial; }

public void setMiddleInitial(String middleInitial) {
this.middleInitial = middleInitial;
}

@XmlElement(name = "surname")
public String getLastName() { return lastName; }

public void setLastName(String lastName) { this.lastName = lastName; }

public String getFullName() { return firstName + " " + lastName; }

... various getters and setters ...

@XmlElement
public String getCountry() { return country; }

public void setCountry(String country) { this.country = country; }

public boolean isInternational() {
boolean unknown = (country == null);
boolean domestic = ("US".equals(country));
return !(unknown || domestic);
}

... even more getters and setters ...
}

The first thing to discuss is the new isInternational() method. This returns a boolean indicating whether the lead is an international lead. If the country is null or else "US", it's not international; otherwise, it is. (The sample code includes a unit test for this method.) We're adding this method because we're going to create a content-based router in just a few moments. The router will query the lead's isInternational() method and make a routing decision based on that result.

The other change is that we've added several JAXB annotations to support object/XML mapping (OXM). This will allow us to generate a lead document that we can send to the external CRM as a SOAP message. We're not doing anything especially sophisticated here but it gets the basic point across. There's some Spring Integration configuration we'll need to do in order to activate the OXM, and we'll see that in section 1.5 below. Before that, though, let's create the content router.

Add lead routing

In figure 1 there's a router that sits between the newLeadChannel on the one hand and the two CRMs on the other. This router is backed by the POJO appearing in listing 2 below.

Listing 2. CountryRouter.java, a router POJO

package crm.integration.routers;

import java.util.logging.Logger;
import org.springframework.integration.annotation.Router;
import crm.model.Lead;

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

@Router
public String route(Lead lead) {
String destination = (lead.isInternational() ?
"internationalChannel" : "domesticChannel");
log.info("Lead country is " +
lead.getCountry() + "; routing to " + destination);
return destination;
}
}

This is a simple component, with a single route() method that we've annotated with a @Router annotation so that Spring Integration will know which method to use for routing. It should be pretty clear what's going on. As mentioned earlier, we're doing content-based routing here, meaning that we're using the actual message payload (i.e., the Lead instance) to drive the routing decision rather than using message headers. This ability to inspect message payloads is key as it allows us to move the system's "intelligence" into the bus itself instead of forcing clients to have to provide hints (e.g., in the form of message headers) that steer processing in the right direction. This reduces the burden on clients and makes the bus more generally usable.

The last step is to tie it all together in our message bus configuration.

Update the message bus configuration

Listing 3 presents some updates to our message bus.

Listing 3. /WEB-INF/applicationContext-integration.xml updates to support lead routing

<?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:ws="http://www.springframework.org/schema/integration/ws"
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:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
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/integration/ws
http://www.springframework.org/schema/integration/ws/spring-integration-ws-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/util
http://www.springframework.org/schema/util/spring-util-2.5.xsd">

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

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

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

<router input-channel="newLeadChannel">
<beans:bean id="countryRouter"
class="crm.integration.routers.CountryRouter" />
</router>

<channel id="domesticChannel" />

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

<channel id="internationalChannel" />

<util:list id="classesToBeBound">
<beans:value>crm.model.Lead</beans:value>
</util:list>

<beans:bean id="marshaller"
class="org.springframework.oxm.jaxb.Jaxb2Marshaller"
p:classesToBeBound-ref="classesToBeBound" />

<ws:outbound-gateway
request-channel="internationalChannel"
uri="${external.crm.uri}"
marshaller="marshaller" />

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

There are a few things going on here; so we'll take them in turn. It's helpful to look at figure 1 while looking at this configuration.

First, notice that we've added the util and ws namespace declarations and schema locations to the top-level beans element. The ws prefix identifies elements from Spring Integration's web services schema.

Next, we've inserted a router in between the newLeadChannel and the service activator that was previously connected to the newLeadChannel. We've defined the CountryRouter bean as an inner bean to keep its definition hidden from the rest of the context.

After the router, messages can go in one of two directions as we've already discussed. The domestic branch is really just what we had at the end of the previous tutorial. The international branch, however, is new. There's an internationalChannel that feeds into an outbound SOAP web service gateway. We have to give the gateway a marshaller (we've chosen a JAXB marshaller, though other options are available) so it can turn message payloads into SOAP messages, and that's what the marshaller configuration is all about.

You'll also need to add the following property to applicationContext.properties to specify the URI for the external CRM:

external.crm.uri=http://localhost:9091/xcrm/main/leads.xml

 

  Once you've done all that, go ahead and start up both CRMs by running

mvn jetty:run

 against the appropriate module's POM. After doing that, try creating some leads using the staff UI:

http://localhost:8080/crm/main/lead/form.html

When you create a domestic lead it should go to the local CRM, and when you create an international lead it should go to the external CRM (where you'll see the SOAP message displayed on the command line). In either event the system will send a confirmation e-mail to the lead.

The next two sections will show how to integrate so-called requests for information (RFIs), which allow leads to (yep) request information. First we'll do a web-based RFI form, and after that we'll do a legacy e-mail-based RFI.

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.