Spring Integration with Java Config: An Example from Spring Integration in Action

Reading Time: 3 minutes

The year is 2016. As a developer, the majority of the third-party service endpoints I work with on a daily basis follow (or sort of follow) the RESTful API guidelines. However, RESTful APIs were not always the web transfer methodology of choice. There are numerous ways to transmit information over HTTP. Outside of HTTP, systems might also employ FTP or another use of the TCP/IP protocol to transmit information between computers. For managing multiple myriad types of integrations, we have Spring Integration.

Screen Shot 2014-08-10 at 9.10.35 AM

To get an overview of Spring Integration and understand what the tool can do, I recommend sections 3.3.1—3.4.7 of the documentation. These sections explain the concept of a message and a channel, and they provide many examples of how and why different configurations of messages and channels might be used.

After that, I would recommend taking a look at this documentation for the Spring Integration Java DSL. If you are already ramping a development team on Java or on Spring, the Java Config for Spring Integration will look and feel more similar to the other code they’re writing than an XML configuration would. It should be noted that not all of Spring Integration’s capabilities have been ported to the Java Config DSL at this time. If you find yourself needing something special that the Java DSL does not cover, the book Spring Integration in Action provides excellent examples of the XML config, as do sections 4 and above in the documentation for Spring Integration.

I found the book Spring Integration in Action extremely helpful for understanding how Spring works, but all of the examples in the book are configured in XML. So I decided to rework an example from one of the book’s introductory chapters using Java Config instead of XML.

Here is an explanation of the business problem:

An airline has a web app through which customers can book their flights. This app also exposes an API that airline comparison sites can use to let customers book flights.
For each booking, the app should charge the credit card and, if that succeeds, proceed to seat selection. Those two steps should happen immediately so we can make sure the airline gets paid and we can make sure that we don’t overbook or book two people in the same seat.
Additionally the customer should get a confirmation email, but that does not have to happen right away if the system is under a lot of load.
Finally, people booking flights from the airline’s website should have priority over the customers of the comparison sites.
Here is my attempt at a Java Config version of a business solution:


@Configuration
@EnableIntegration
public class ExampleConfiguration {
@Bean
IntegrationFlow inboundFlow(
ErrorManager errorManager,
SeatSelector seatSelector,
Prioritizer priority
) {
return IntegrationFlows
.from(Http.inboundGateway("/example/bookmyflight"))
.enrichHeaders(headerEnricherSpec -> headerEnricherSpec.header("original_request", request)
.header("priority", priority.for(request)))
.channel(channel -> channel.priority())
.transform(//build credit card charge request with info from flight booking request)
.enrich(e -> e.requestChannel("paymentChannel")
.header("paymentInfo", this.paymentInfo)
.handle((payload, headers) -> seatSelector.selectSeat(payload))
.publishSubscribeChannel(Executors.newCachedThreadPool(), s -> s
.subscribe(subflow -> subflow
.<String>handle((payload, headers) -> "You have successfully booked your seat.")
.subscribe(subflow -> subflow
.enrichHeaders(headerEnricherSpec -> headerEnricherSpec.header("mail_to", payload.getEmailAddress()))
.handle(Mail.outboundAdapter("smtp.gmail.com")
.credentials(ExampleCiConfig.AUTHENTICATION_EMAIL, ExampleCiConfig.AUTHENTICATION_PASSWORD)
.javaMailProperties(mailProperties()))
.channel(channel -> channel.queue("outboundEmail"))))
.get();
}
@Bean
public Prioritizer prioritizer() {
return new Prioritizer() {
public int for(Object request) {
//Figures out from the request whether the customer came from the airline's website or from a
//comparison website, and assigns a higher priority to the first type of customer
}
}.get();
}
@Bean
public ErrorManager errorManager() {
return new ErrorManager() {
public Object handleError(int statusCode, Object payload) {
//Handle different HTTP status codes and, if call successful, return payload
}
}
}
@Bean
public SeatSelector seatSelector() {
return new SeatSelector() {
public Object selectSeat(Object payload) {
//make call to some external seat selection service
}
}
}
Properties mailProperties() {
Properties properties = new Properties();
properties.put("mail.smtp.ssl.enable", true);
return properties;
}
}

This is a first attempt and may need to be tweaked and clarified. I welcome feedback!

 

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.