VMware offers training and certification to turbo-charge your progress.Learn more
We are pleased to announce the first installment of the 'Loan Broker' Reference Implementation. 'Loan Broker' concept has become a de-facto reference domain for showcasing Enterprise Integration Patterns (EIP) - by Gregor Hohpe ad Bobby Woolf, and this installment of the Loan Broker RI demonstrates how Enterprise Integration Patterns are realized and applied using Spring Integration (SI) framework.
At the core of EIP architecture are the very simple yet powerful concepts of Pipes and Filters and Message. Endpoints (Filters) are connected with one another via Channels (Pipes). The producing endpoint sends Message to the Channel and the Message is retrieved by the Consuming endpoint. This architecture is meant to define various mechanisms that describe How information is exchanged between the endpoints, without any awareness of What those endpoints are or What information they are exchanging, thus providing for a very loosely coupled and flexible collaboration model while also, decoupling Integration concerns from Business concerns. EIP extends this architecture by further defining:
The Spring Integration (SI) messaging framework is designed to provide a POJO-based programming model built on top of Enterprise Integration Patterns.
Submit Loan Quote request - Messaging Gateway
The Messaging Gateway pattern provides a simple mechanism to access messaging systems, including our Loan Broker. In SI you define the Gateway as a Plain Old Java Interface (no need to provide an implementation), configure it via the XML <gateway> element or via annotation and use it as any other Spring bean. SI will take care of delegating and mapping method invocations to the Messaging infrastructure by generating a Message (payload is mapped to an input parameter of the method) and sending it to the designated channel.
LoanBrokerGateway.java is the interface representing the Message Gateway used by the consumer
Gateway XML configuration:
<int:gateway id="loanBrokerGateway" default-request-channel="loanBrokerPreProcessingChannel" service-interface="org.springframework.integration.loanbroker.LoanBrokerGateway"/>
In the above configuration whenever any method is invoked on the 'loanBrokerGateway' bean, a Message will be constructed and sent to the 'loanBrokerPreProcessingChannel'.
Our definition of the Messaging Gateway (LoanBrokerGateway.java) gives the consumer two ways to interact with the Loan Broker. The Consumer might request a single(best) quote by invoking the getLoanQuote(loanRequest) method, or all quotes via a call to getAllLoanQuotes(loanRequest) method. This means that our Loan Broker must be aware of the type of the Loan request. We also know there are some pre-screening steps such as getting and evaluating the consumer's credit score, simply because some premiere Banks will only typically accept quote requests from consumers that meet a minimum credit score requirement.
Essentially, this whole process resembles doctors visit where before seeing the real doctor you meet the nurse who takes your temperature, blood pressure etc., writing up a list of 'meta information' the doctor will need.In EIP a Message is a simple structure which consists of a Message Payload and Message Headers. Message Headers are a great mechanism to store meta information related to the Message. So how can we enrich our Message with the additional information? EIP defines the Content Enricher pattern which describes how to augment a Message with additional information. Spring Integration provides a <header-enricher> element allowing you to quickly enrich a Message in-transit. But since our Loan Broker must perform several tasks before sending out the quote, it would be nice if there were also a mechanism to compose a process from a set of individual and independent tasks.
Loan Request Pre-screeening – Composed Message Processor and Content Enricher
The Composed Message Processor pattern describes rules around building endpoints that maintain control over message flow which consists of multiple message processors. In our case the pre-screening flow consists of 3 steps: a) determine the the type of the loan request; b) obtain consumer's credit history and score; c) determine (based on some criteria) the list of channels (each channel corresponds to an individual bank).
Spring Integration allows you to compose complex processors via the <chain> element
<int:chain id="preScreening" input-channel="loanBrokerPreProcessingChannel" output-channel="banksDistributionChannel"> <int:header-enricher> <int:header name="RESPONSE_TYPE" expression="headers.history.iterator().next().attributes['method'].equals('getLoanQuote') ? 'BEST' : 'ALL'" /> </int:header-enricher> <int:header-enricher> <int:header name="CREDIT_SCORE" ref="creditBureau" method="getCreditScore"/> </int:header-enricher> <int:header-enricher> <int:header name="BANKS" ref="bankSelector" method="selectBankChannels"/> </int:header-enricher> </int:chain>
This will create a bean 'preScreening" as an SI chain endpoint, which also defines an input/output-channel to receive and send messages. The above chain is composed with 3 header-enricher processors. The first one will set RESPONSE_TYPE header by utilizing SpEL while accessing the Message History and determining this header's value based on the Gateway method invoked.
This example illustrates how SpEL can be used to perform simple evaluations while determining the header value, but we do not advocate using SpEL to perform complex business logicNext we have a header-enricher which is mapped to a process responsible for getting the credit score from the Credit Bureau (currently stub CreditBureauStub) and set the CREDIT_SCORE header. The last header-enricher uses the BankChannelSelector (see the bankSelector config).The implementation of the BankChannelSelector.selectBankChannels(..) method takes a Message as input and returns a Set<String> value containing the name of channels, which will be set as the BANKS header. This completes our pre-screening process, our Loan Broker is now ready to send a loan request to each selected Bank via the banksDistributionChannel.
The BANKS header defines the dynamically generated and filtered list of channels each of which acts as a recipient representing a Bank. We need an endpoint that allows us to send the same Message to all recipients.
Distribute Loan Quote Requests to Selected Banks - Recipient ListEIP defines various types of routing patterns which all derive from Message Router. One of them is the Recipient List router, which routes a message to all recipients in the list. SI provides a <router> element that allows us to configure a router which, in our case, will receive a Message from the bankDistributionChannel. This router will get the list of bank channels from the BANKS Message Header via a SpEL expression (this list is set in the pre-screening step), and will distribute the Message to these channels.
The XML Configuration below shows how this router is configured:
<int:router id="bankRecipientListRouter" input-channel="banksDistributionChannel" expression="headers['BANKS']" apply-sequence="true"/>
You can clearly see how we are assembling Loan Broker by connecting various endpoints (Filters) via channels (Pipes), while passing Messages. You can also see that we are accomplishing it through POJO-based programming techniques, barely using the Spring Integration API (only BankChannelSelector.java). SI takes care of interfacing our POJOs with the Messaging infrastructure.One last thing the Loan Broker needs to to is to receive the loan quotes form the banks, aggregate them by consumer (we don't want to show quotes from one consumer to another), assemble the response based on the consumer's selection criteria (single best quote or all quotes) and reply back to the consumer.
Aggregating Loan Quote Responses - Aggregator
An Aggregator pattern describes an endpoint which groups related Messages into a single Message. Criteria and rules can be provided to determine an aggregation and correlation strategy. SI provides several implementations of the Aggregator pattern as well as a convenient name-space based configuration.
Our Loan Broker defines a 'loanQuoteAggregator' bean via the <aggregator> element which provides a default aggregator and correlation strategy. The default correlation strategy correlates messages based on the $corelationId header (see Correlation Identifier pattern). What's interesting is that we never provided the value for this header. It was set earlier by the Recipient List router automatically, when it generated a separate Message for each Bank.
Once the Messages are correlated they are released to the actual Aggregator implementation. Although default Aggregator is provided by SI, its strategy (gather the list of payloads from all Messages and construct a new Message with this List as payload) does not satisfy our requirement. The reason is that our consumer might require a single best quote or all quotes. To communicate the consumer's request, earlier in the process we set the RESPONSE_TYPE header. Now we have to evaluate this header and return either all the quotes (the default aggregation strategy would work) or the best quote (the default aggregation strategy will not work because we have to determine which loan quote is the best).
<int:aggregator id="loanQuoteAggregator" input-channel="quotesAggregationChannel" method="aggregateQuotes"> <bean class="org.springframework.integration.loanbroker.LoanQuoteAggregator"/> </int:aggregator>
Obviously selecting the best quote could be based on complex criteria and would influence the complexity of the aggregator, but for now we are making it simple. If consumer wants the best quote we will select a quote with the lowest interest rate. To accomplish that the LoanQuoteAggregator.java will sort all the quotes and return the first one. The LoanQuote.java implements Comparable which compares quotes based on the rate attribute.
Once the response Message is created it is sent to the default-reply-channel of the Messaging Gateway (thus the consumer) which started the process. Our consumer got the Loan Quote!
One important thing to notice is that we have not defined a default-reply-channel attribute on <gateway> element. In fact we have not defined a single channel explicitly. Similar to other Messaging systems, SI will auto-create input and default reply channels as needed, giving you yet another way to further simplify Spring Application Context configuration.
In the next installment we'll demonstrate various remoting adapters and techniques available in Spring Integration by substituting our stubbed services with these adapters and will introduce an asynchronous style of integration that is relevant to the 'Loan Broker' use case.
The 'Loan Broker' Reference Implementation is available with the release of Spring Integration 2.0.M3 (see the Downloads section). It is distributed as an independent Eclipse/Maven project. You can also check it out as a project from our Subversion repository.
$> svn co https://src.springframework.org/svn/spring-integration/trunk/spring-integration-samples/loan-broker/ loan-broker $> cd loan-broker $> mvn install
Relevant Links: Spring Integration Spring Integration in Action Enterprise Integration Patterns Getting Started With Spring Integration (Joshua Long) Agile SOA - Part 1, 2 and 3 (Tom McCuch)
Thanks to Gary Russel (Spring Source, SI comitter) and Dave Turanski (Spring Source) for helping out with this blog!