• No results found

XML messaging

In document Spring Integration in Action (Page 173-178)

Handling messages with XML payloads

8.1 XML messaging

In our Spring Integration travel example application, a quotation request for a trip may contain multiple legs. Once the trip is split into legs, the message flow needs to generate quotation requests for the different elements of the trip, such as the hotel accommodation and car rental. To do this, you must separate the constituent parts of the trip relating to hotel, flight, and car rental before transforming the internal canonical representation to one that is understood by the third-party systems. One approach is to maintain a number of Java payload types representing the request for- mats of each individual system and a set of transformations between the canonical form and the third-party form. This task can become unwieldy as the number of sys- tems with which you integrate grows. The request format of the third-party system is likely to be some form of XML, so the process of generating a quote for each leg of the trip should be implemented as an XML-based message flow.

The trigger for the leg-quote-generation flow is the receipt of a message containing a LegQuoteCommand. It contains the requirements for the start and end locations of the leg, the desired dates for travel, and additional specifications for hotel and car rental if required. On receipt of this message, the system first converts it into the canonical XML form of a trip leg. XSLT is then used to add the leg information to each criteria section prior to splitting. Having duplicated the leg information to each criteria section, you can then split the document into separate requests. Since the requests are now self- contained, they can then be processed in parallel, a common strategy for speeding up

143

XML messaging

the overall processing of requests. In this case the splitting is achieved by the use of XPath, which splits the document into one document per child of the legQuote ele- ment. The leg-quote flow can then process each request in parallel and route it to the appropriate third-party system. Again, this is done using an XPath expression, which routes the message according to the route element name. Finally, before making a request to the third-party system, you validate that each request conforms to the schema that defines the third-party request format. This is achieved by using the out-of-the-box support for validating messages against an XML schema definition.

The next section discusses the support for marshalling, which allows you to con- vert the payload of the message into an XML form ready for subsequent processing and dispatching to the external systems.

8.1.1 Marshalling LegQuoteCommand into XML

To convert your message between XML and objects, you use OXM. This conversion process is commonly called marshalling and unmarshalling. Spring provides a generic technology-agnostic abstraction of the process of mapping between Java and XML in the Spring OXM module. Spring OXM provides a common interface across a number of leading Java OXM solutions. By providing a common abstraction, the OXM module decouples code from the implementation details and provides a consistent exception hierarchy regardless of the OXM technology being used. This decoupling is achieved by encapsulating the mapping process behind implementations of the Marshaller inter- face, which maps from an Object to the XML Result, and the Unmarshaller interface, which maps from a Source to the Object representation. Both of these interfaces are shown in the following code snippets, and the unmarshaller is visualized in figure 8.1:1

package org.springframework.oxm; public interface Marshaller {

boolean supports(Class<?> clazz);

void marshal(Object graph, Result result)

throws IOException, XmlMappingException; }

package org.springframework.oxm; public interface Unmarshaller {

boolean supports(Class<?> clazz); Object unmarshal(Source source)

throws IOException, XmlMappingException; }

1 Spring OXM was developed as part of the Spring WS project. As of Spring 3.0, the OXM framework is part of

144 CHAPTER 8 Handling messages with XML payloads

ANNOTATING DOMAIN CLASSES FOR USE WITH JAXB

For the leg-quote flow, you can use the Java Architecture for XML Binding (JAXB) v2 OXM technology in conjunction with Spring OXM. Because the sample already con- tains a set of existing rich domain classes, you can annotate the domain classes with JAXB annotations rather than generate the classes from an XML schema. Following is the annotated LegQuoteCommand, which maps to the root of the canonical XML leg- quote document. To indicate that this maps to the root of an XML document, you annotate it with the @XmlRootElement annotation. You also specify the name of the XML element with the name attribute of the annotation. This contains fields represent- ing the criteria for each of the constituent parts of the leg quote:

package siia.booking.domain.trip; import siia.booking.domain.Command; import siia.booking.domain.car.CarCriteria; import siia.booking.domain.flight.FlightCriteria; import siia.booking.domain.hotel.HotelCriteria; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "legQuote")

public class LegQuoteCommand implements Command { private Leg leg;

private HotelCriteria hotelCriteria; private FlightCriteria flightCriteria; private CarCriteria carCriteria; /**

* Private constructor for use by JAXB */

private LegQuoteCommand() { }

public LegQuoteCommand(Leg leg) { this.leg = leg;

}

//setters and getters omitted } <trip> <flight/> <hotel/> <car/> </trip flight : Flight hotel : Hotel car : CarRental Trip

Unmarshaller Figure 8.1 The trip

unmarshaller implements the

Unmarshaller interface and can convert trip XML messages to instances of the Trip class.

145

XML messaging

By default, JAXB outputs all fields of the annotated type, although it can only convert standard Java types out of the box. This functionality can be extended by providing XmlAdapter implementations capable of mapping types unsupported by JAXB to types supported by JAXB. An example is the Leg class, which contains start and end dates and locations. This class uses the Joda Time project (http://joda-time.sourceforge.net) to represent dates. The following code shows use of the @XmlJavaTypeAdapter annota- tion to reference a custom adapter capable of converting the DateTime instance to a recognized instance of java.util.Calendar:

package siia.booking.domain.trip; import siia.booking.domain.Location; import siia.booking.domain.binding.JodaDateTimeAdapter; import org.apache.commons.lang.builder.HashCodeBuilder; import org.joda.time.DateTime; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @XmlRootElement

public class Leg { @XmlElement

@XmlJavaTypeAdapter(JodaDateTimeAdapter.class) private DateTime startOfLegDate;

@XmlElement

@XmlJavaTypeAdapter(JodaDateTimeAdapter.class) private DateTime endOfLegDate;

@XmlElement

private Location startLocation; @XmlElement

private Location endLocation; private Leg() {

}

public Leg(DateTime startOfLeg, DateTime endOfLeg,

Location startLocation, Location endLocation) { this.startOfLegDate = startOfLeg; this.endOfLegDate = endOfLeg; this.startLocation = startLocation; this.endLocation = endLocation; } ... }

The custom adapter is an implementation of javax.xml.bind.annotation.adapters .XmlAdapter, shown in the following snippet. This adapter converts between the date types using coordinated universal time (UTC):

146 CHAPTER 8 Handling messages with XML payloads package siia.booking.domain.binding; import org.joda.time.DateTime; import org.joda.time.chrono.ISOChronology; import javax.xml.bind.annotation.adapters.XmlAdapter; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.TimeZone;

public class JodaDateTimeAdapter extends

XmlAdapter<Calendar, DateTime> { @Override

public DateTime unmarshal(Calendar cal) throws Exception {

return new DateTime(cal.getTime(), ISOChronology.getInstanceUTC()); }

@Override

public Calendar marshal(DateTime dt) throws Exception { GregorianCalendar cal = new GregorianCalendar(); cal.setTimeInMillis(dt.getMillis());

cal.setTimeZone(TimeZone.getTimeZone("UTC")); return cal;

} }

CONFIGURING THE SPRING INTEGRATION MARSHALLER ENDPOINT

The Spring Integration marshalling and unmarshalling support is a fairly thin adapter layer on top of Spring OXM, so you first configure an instance of a marshaller or unmarshaller, as appropriate. The out-of-the-box implementations all implement both the Marshaller and Unmarshaller interfaces. The following code example shows how to configure an instance of Jaxb2Marshaller, providing a list of the anno- tated types, in this case just the LegQuoteCommand:

<beans:bean id="legMarshaller"

class="org.springframework.oxm.jaxb.Jaxb2Marshaller"> <beans:property name="classesToBeBound"

value="siia.booking.domain.trip.LegQuoteCommand"/> </beans:bean>

On top of this, you can configure a marshalling endpoint to convert from the Leg- QuoteCommand to a canonical XML form using the Spring Integration XML namespace, as in the next example:

<beans:beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/integration" xmlns:si-xml="http://www.springframework.org/schema/integration/xml" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/ ➥integration/spring-integration.xsd http://www.springframework.org/schema/integration/xml http://www.springframework.org/schema/ ➥integration/xml/spring-integration-xml.xsd">

147

In document Spring Integration in Action (Page 173-178)