Loan-broker-sync Flow
As the main flow in the application, this flow coordinates data collection among flows to produce an end user response.
Studio Visual Editor
Studio XML Editor
<flow name="loan-broker-sync" doc:name="loan-broker-sync" doc:description="The main loanbroker flow that:
i) Receives a customer request
ii) Performs a lookup of the customer credit profile using a component binding
iii) Determines the bank that should be used to request quotes
iv) Sends the request to the selected banks and aggregates responses
v) Selects the lowest quote from the list of quotes
vi) Returns the response to the client
">
<description>
The main loanbroker flow that:
i) Receives a customer request
ii) Performs a lookup of the customer credit profile using a component binding
iii) Determines the bank that should be used to request quotes
iv) Sends the request to the selected banks and aggregates responses
v) Selects the lowest quote from the list of quotes
vi) Returns the response to the client
</description>
<http:inbound-endpoint address="http://0.0.0.0:11081" exchange-pattern="request-response" doc:name="HTTP"/>
<http:body-to-parameter-map-transformer doc:name="Body to Parameter Map"/>
<choice doc:name="Choice">
<when expression="!(payload['name'] == null || payload['ssn'] == null || payload['amount'] == null || payload['term']==null)">
<expression-component doc:name="create customer request">
import org.mule.example.loanbroker.message.CustomerQuoteRequest;
import org.mule.example.loanbroker.model.Customer;
payload = new CustomerQuoteRequest(new Customer(payload['name'], Integer.parseInt(payload['ssn'])), Integer.parseInt(payload['amount']), Integer.parseInt(payload['term']));
</expression-component>
<enricher source="#[payload]" target="#[flowVars['creditProfile']]" doc:name="Enrich with creditProfile">
<flow-ref name="lookupCustomerCreditProfile" doc:name="lookupCustomerCreditProfile"/>
</enricher>
<enricher source="#[payload]" target="#[flowVars['banks']]" doc:name="Enrich with banks">
<flow-ref name="lookupBanks" doc:name="lookupBanks"/>
</enricher>
<set-variable variableName="quotes" value="#[new java.util.LinkedList()]" doc:name="create empty quotes"/>
<foreach collection="#[flowVars['banks']]" doc:name="Foreach">
<enricher target="#[quotes.add($)]" doc:name="Message Enricher">
<flow-ref name="lookupLoanQuote" doc:name="lookupLoanQuote"/>
</enricher>
</foreach>
<flow-ref name="findLowestLoanQuote" doc:name="findLowestLoanQuote"/>
<object-to-string-transformer doc:name="Object to String"/>
</when>
<otherwise>
<expression-component doc:name="set error message">payload="Error: incomplete request"</expression-component>
</otherwise>
</choice>
<catch-exception-strategy doc:name="Catch Exception Strategy">
<set-payload value="Error processing loan request" doc:name="Set error message"/>
</catch-exception-strategy>
</flow>
The request-response HTTP Inbound Endpoint in this flow receives an end user request. Because it has a two-way message exchange pattern, this HTTP endpoint is responsible for both receiving and returning messages.
Next, the Body to Parameter Map Transformer converts the data format of the message payload from HTTP body data to a Java map. The Loan Broker application only processes Java message payloads.
Then, Mule employs a content-based router to direct the message for further processing. The Choice Flow Control routes each message to one of two processing pathways according to its payload contents (see image and code below).
-
If the message payload contains a complete request (i.e. the borrower’s name and SSN, and the amount and the term of the loan), the choice flow control passes the message to the create customer request
Expression Component.
-
If the message payload is an incomplete request, the choice flow control passes the message to the set error message
expression component. This component sets the payload of the message to read Error: incomplete request
. Mule processes the message no further. Instead, it responds to the end user with the error message.
Studio Visual Editor
Studio XML Editor
<choice doc:name="Choice">
<when expression="!(payload['name'] == null || payload['ssn'] == null || payload['amount'] == null || payload['term']==null)">
<expression-component doc:name="create customer request">
import org.mule.example.loanbroker.message.CustomerQuoteRequest;
import org.mule.example.loanbroker.model.Customer;
payload = new CustomerQuoteRequest(new Customer(payload['name'], Integer.parseInt(payload['ssn'])), Integer.parseInt(payload['amount']), Integer.parseInt(payload['term']));
</expression-component>
<enricher source="#[payload]" target="#[flowVars['creditProfile']]" doc:name="Enrich with creditProfile">
<flow-ref name="lookupCustomerCreditProfile" doc:name="lookupCustomerCreditProfile"/>
</enricher>
<enricher source="#[payload]" target="#[flowVars['banks']]" doc:name="Enrich with banks">
<flow-ref name="lookupBanks" doc:name="lookupBanks"/>
</enricher>
<set-variable variableName="quotes" value="#[new java.util.LinkedList()]" doc:name="create empty quotes"/>
<foreach collection="#[flowVars['banks']]" doc:name="Foreach">
<enricher target="#[quotes.add($)]" doc:name="Message Enricher">
<flow-ref name="lookupLoanQuote" doc:name="lookupLoanQuote"/>
</enricher>
</foreach>
<flow-ref name="findLowestLoanQuote" doc:name="findLowestLoanQuote"/>
<object-to-string-transformer doc:name="Object to String"/>
</when>
<otherwise>
<expression-component doc:name="set error message">payload="Error: incomplete request"</expression-component>
</otherwise>
</choice>
The create customer request
component uses expressions to extract data from the message payload. It uses the data to create a new Java object with three values:
-
the Customer
, which identifies both the borrower’s name and SSN
-
one Integer
, which identifies the amount of the loan
-
a second Integer
, which identifies the loan repayment term
<expression-component doc:name="create customer request">import org.mule.example.loanbroker.message.CustomerQuoteRequest; import org.mule.example.loanbroker.model.Customer; payload = new CustomerQuoteRequest(new Customer(payload['name'], Integer.parseInt(payload['ssn'])), Integer.parseInt(payload['amount']), Integer.parseInt(payload['term']));</expression-component>
With a new CustomerQuoteRequest
object in its payload, the message encounters its first Message Enricher. Throughout this flow, Mule enriches messages with data rather than changing the payload contents. By enriching a message, Mule preserves the payload content so that other elements in the application can access the original data.
The Enrich with creditProfile
enricher contains only a Flow Reference Component. This type of component invokes other flows, or subflows, in the application to acquire, then add data to the message. In this case, the lookupCustomerCreditProfile
component demands that the LookupCustomerCreditProfile subflow access an external Web service to acquire the borrower’s credit score. Mule enriches the message with the credit score, then passes the message to the next enricher in the flow.
As with its predecessor, the Enrich with Banks
enricher uses a flow reference component to invoke a subflow and acquire data. In this case, instead of adding a credit score, Mule uses the result of the LookupBanks subflow to add a list of banks to the message payload.
Mule then uses a Variable Transformer to create an empty list variable. Mule will fill this empty quotes
list variable with the quotes it fetches from banks. With an empty list to fill, the message next encounters a Foreach scope. One by one, this iterative processor fetches data to populate each item on the list.
To fetch these data, the flow reference component first invokes the LookupLoanQuote subflow to acquire a quote from a bank. Then, the message enricher adds the quote to the list variable. Foreach continues to invoke, then enrich, until it has acquired a quote from each bank on the list of banks. Foreach then passes the message to the next message processor in the flow.
To illustrate foreach’s behavior with an example, imagine a message payload with the following contents:
-
an empty quotes
list variable
-
a banks
list variable naming two banks from which Mule must request a quote: MuliNational Bank, and IndustrialGrowth Bank
Foreach processes the message payload as follows:
-
Foreach consults the banks
list variable to learn that it should send its first request to MuliNational.
-
Foreach invokes the LookupLoanQuote subflow.
-
The LookupLoanQuote subflow calls the getLoanQuote
Web service to obtain an interest rate quote from MultiNational.
-
The LookupLoanQuote subflow provides the Web service response to the Loan-Broker-Sync flow.
-
The message enricher inserts the interest rate quote from MultiNational into the quotes
list variable.
-
Foreach consults the banks
list variable to learn that it should send its second request to IndustrialGrowth.
-
Foreach invokes the LookupLoanQuote subflow.
-
The LookupLoanQuote subflow calls the getLoanQuote
Web service to obtain an interest rate quote from IndustrialGrowth.
-
The LookupLoanQuote subflow provides the Web service response to the Loan-Broker-Sync flow.
-
The message enricher inserts the interest rate quote from IndustrialGrown into the quotes
list variable.
-
Foreach consults the banks
list variable to find no more items on the list. It passes the message — now with a list containing two interest rate quotes — to the next message processor. Refer to the table below for a before-and-after comparison of message contents.
Message Contents Before
Iterative Processing |
Message Contents After
Iterative Processing |
banks list variable:
• www.multinational.com/loans/quotes
• www.industrialgrowth.com/loans/quotes
|
banks list variable:
• www.multinational.com/loans/quotes
• www.industrialgrowth.com/loans/quotes
|
quote list variable:
|
quote list variable:
• 6.99
• 6.84
|
The penultimate message processor in this flow references yet another subflow in the application. The findLowestLoanQuote
subflow determines which quote in the list is the lowest, then logs the result in the message payload.
Finally, the Object to String Transformer converts the message payload’s data format from Java to a string. The HTTP endpoint sends the response to the end user.
Notice that the Loan-broker-sync flow also contains a Catch Exception Strategy. Rather than use Mule’s default exception strategy this flow uses a customized exception strategy to handle errors. If an error occurs in the flow, the exception strategy’s Set Payload Transformer sets an error message on the payload. The application sends this error message, which reads, Error processing loan request
, as a response to the end user.
LookupCustomerCreditProfile Subflow
Invoked upon demand by the Loan-broker-sync flow, this subflow acquires and logs the borrower’s credit score on the message payload.
Studio Visual Editor
Studio XML Editor
<sub-flow name="lookupCustomerCreditProfile" doc:name="lookupCustomerCreditProfile" doc:description="
Returns the customer credit profile obtained form the Credit Agency
">
<description>
Returns the customer credit profile obtained form the Credit Agency
</description>
<set-payload value="#[payload.customer]" doc:name="customer"/>
<processor-chain doc:name="Processor Chain">
<cxf:jaxws-client serviceClass="org.mule.example.loanbroker.creditagency.CreditAgencyService" operation="getCreditProfile" doc:name="getCreditProfile"/>
<http:outbound-endpoint address="http://localhost:18080/mule/TheCreditAgencyService" doc:name="HTTP"/>
</processor-chain>
<logger level="INFO" message="Credit profile: #[payload]" doc:name="creditProfile"/>
</sub-flow>
To acquire the credit score, the customer
transformer sets the payload to Customer
, as defined by the create customer request
expression transformer. (Recall that the Customer
variable contains the borrower’s name and SSN.) Mule sends a request to the getCreditProfile
SOAP Web service. The HTTP endpoint inserts the Web service’s response into the subflow.
Mule leverages Apache’s CXF framework to build Web services. The Processor Chain that wraps the SOAP Component and HTTP outbound endpoint is a CXF requirement. It ensures that Mule completes all processing activities prior to logging the processing result.
Last in this flow, the Logger Component logs the payload of the Web service’s response on the message payload as the Credit Profile
.
LookupBanks Subflow
The application prevents exposing all banks to all loan quote requests. A bank that caters to premiere clients, for example, would be irked to receive a request for a quote for a small loan from a borrower with poor credit. To prevent such irksome calls to banks’ Web services, the Loan Broker application employs the LookupBanks subflow.
Studio Visual Editor
Studio XML Editor
<sub-flow name="lookupBanks" doc:name="lookupBanks" doc:description="
Returns the list of banks to contact and returns it as a flow variable 'banks'
">
<description>
Returns the list of banks to contact and returns it as a flow variable 'banks'
</description>
<choice doc:name="Choice">
<when expression="payload.getLoanAmount() >= 20000">
<expression-component doc:name="Bank1, Bank2">payload=[new java.net.URI('http://localhost:10080/mule/TheBank1'), new java.net.URI('http://localhost:20080/mule/TheBank2')]</expression-component>
</when>
<when expression="payload.getLoanAmount() >= 10000 || payload.getLoanAmount() <= 19999">
<expression-component doc:name="Bank3, Bank4">payload=[new java.net.URI('http://localhost:30080/mule/TheBank3'), new java.net.URI('http://localhost:40080/mule/TheBank4')]</expression-component>
</when>
<otherwise>
<expression-component doc:name="Bank5">payload=[new java.net.URI('http://localhost:50080/mule/TheBank5')]</expression-component>
</otherwise>
</choice>
<logger level="INFO" message="Banks to contact: #[payload]" doc:name="banks"/>
</sub-flow>
Mule first uses a choice flow control to examine the amount
in the payload, then routes the message according to the size of the loan.
-
If the loan is more that $20,000, the flow control routes the message to the first expression component, labeled Bank 1, Bank 2
.
-
If the loan is more than $10,000, the flow control routes the message to the second expression component, labeled Bank 3, Bank 4
.
-
if otherwise (i.e. if the loan is $10,000 or less), the flow control routes the message to the third expression component, labeled Bank 5
.
Studio Visual Editor
Studio XML Editor
<choice doc:name="Choice">
<when expression="payload.getLoanAmount() >= 20000">
<expression-component doc:name="Bank1, Bank2">payload=[new java.net.URI('http://localhost:10080/mule/TheBank1'), new java.net.URI('http://localhost:20080/mule/TheBank2')]</expression-component>
</when>
<when expression="payload.getLoanAmount() >= 10000 || payload.getLoanAmount() <= 19999">
<expression-component doc:name="Bank3, Bank4">payload=[new java.net.URI('http://localhost:30080/mule/TheBank3'), new java.net.URI('http://localhost:40080/mule/TheBank4')]</expression-component>
</when>
<otherwise>
<expression-component doc:name="Bank5">payload=[new java.net.URI('http://localhost:50080/mule/TheBank5')]</expression-component>
</otherwise>
</choice>
Note that the choice flow control directs the message to the first expression that evaluates to true. For example, it directs a quote request for a loan of $30,000 only to the Bank 1, Bank 2
component.
Each expression component in this subflow contains the URIs of the banks willing to provide an interest rate quote. For example, messages that pass into the Bank 3, Bank 4
component earn, as a payload addition, the URIs for Banks 3 and 4. The banks
Logger component records the list of appropriate banks to which to send a request.
LookupLoanQuote Subflow
This sends a quote request to banks' Web services.
Studio Visual Editor
Studio XML Editor
<sub-flow name="lookupLoanQuote" doc:name="lookupLoanQuote" doc:description="
Returns a loanQuote from a given bank's URI
">
<description>
Returns a loanQuote from a given bank's URI
</description>
<set-variable variableName="bankUri" value="#[payload]" doc:name="bankUri"/>
<expression-component doc:name="create LoanBrokerLoanRequest">
import org.mule.example.loanbroker.message.LoanBrokerQuoteRequest;
LoanBrokerQuoteRequest bqr = new LoanBrokerQuoteRequest();
bqr.setCreditProfile(flowVars['creditProfile']);
payload = bqr;
</expression-component>
<processor-chain doc:name="Processor Chain">
<cxf:jaxws-client serviceClass="org.mule.example.loanbroker.bank.BankService" operation="getLoanQuote" doc:name="getLoanQuote"/>
<http:outbound-endpoint address="http://#[flowVars['bankUri'].getHost()]:#[flowVars['bankUri'].getPort()]#[flowVars['bankUri'].getPath()]" doc:name="HTTP"/>
</processor-chain>
<logger message="LoanQuote from #[flowVars['bankUri']]: #[payload]" level="INFO" doc:name="quote"/>
</sub-flow>
First, the variable transformer stores the Mule message payload — the bank’s URI — as a variable named bankUri
. (Recall that this subflow receives requests one at a time from foreach in the Loan-broker-sync flow. Each request’s payload a the URI of a bank.)
The create LoanBrokerLoanRequest
component uses expressions to extract the borrower’s credit profile (logged by the creditProfile
logger in the LookupCustomerCreditProfile flow) from the message payload. It uses the data to create a request to send to the getLoanQuote
Web service.
Mule uses a SOAP component — configured as a JAXWS-client — to send the request to a bank’s Web service. The HTTP outbound endpoint dynamically determines where to send the request based on the bank’s URI in the message payload. It receives the response from the banks’ Web service and pushes the response payload to the quote
logger to record.