INFO 2012-06-12 22:33:01,285 [main] org.mule.lifecycle.AbstractLifecycleManager: Starting connector: ajaxServer
INFO 2012-06-12 22:33:01,285 [main] org.mortbay.jetty: jetty-6.1.26
INFO 2012-06-12 22:33:01,331 [main] org.mortbay.jetty: JSONCommented is deprecated
INFO 2012-06-12 22:33:01,348 [main] org.mortbay.jetty: Started SelectChannelConnector@0.0.0.0:8090
Order Processing Example
Enterprise Edition
The Mule Order Processing application simulates a Web application for an electronics store. Customers can place purchase orders through the Web interface; the application classifies these orders, then processes them according to their classification.
The application acquires the price for each order, sets a status for the order’s result (success or failure), then amalgamates the results into a summary reply to be sent back to the customer. If the purchase price is above $5000, the application audits the reply before sending it.
Assumptions
This document assumes that you are familiar with Mule ESB. For an overview of basic operations, such as starting the server and locating Mule files, consult the Quick Start Guide to Mule ESB Server and the Management Console.
Set Up
As with this Order Processing example, you can create template applications straight out of the box in Mule Studio or Mule Standalone (Mule ESB without Studio). You can tweak the configurations of these use case-based templates to create your own customized applications in Mule.
Follow the procedure below to create, then run the Order Processing application in Mule ESB.
-
Complete the procedure in Examples and Exercises to create, then run the Order Processing template in Mule Studio, or the Order Processing example in Mule Standalone (Mule ESB without Studio).
-
Open a Web browser and navigate to the following URL: http://localhost:8090/orders. Note that sometimes the AJAX server may be delayed for a few seconds while starting up. Before browsing, check for the following output in the console:
Browsing to the application’s URL, you should see the following page:
Before you try placing an order, open a new window or tab in the Web browser, then navigate to the following URL:
This causes the application to initialize a local database to store the orders you place. Your browser should display a message that reads: db populated
.
How It Works
The Mule Order Processing application exposes a SOAP-based Web service, which accepts order requests and processes each item requested inside the order. If a requested item is manufactured by Samsung, then the application routes that particular item request to an external SOAP-based Web service hosted by Samsung. The application sends all other item requests to an in-house order processor, defined in the inhouseOrder Flow flow.
Regardless of who processes the request, the application stores the result for the request (success or failure) with the price, which it retrieves from an in-house RESTful Web service. To reduce calls to this service and improve order processing efficiency, the application caches orders' results for a period of time.
Finally, the Order Processing application amalgamates the results of item requests into a summary reply to be sent back to the client. If the total price for the order is over $5000, Order Processing audits the reply before sending it.
If an invocation to Samsung’s Web service fails, the application does not retry to invoke the service, but sets the purchase receipt status for the order to REJECTED
.
The application stores the files for the Web page in src/main/app/docroot/
. The index.html
file in this directory contains the click handler for the Submit button ($(#submitButton)
), which uses RPC (via mule.rpc
) to call the orderService flow. This is the main flow in the application, explained in the following section.
orderService Flow
The first building block in the orderService flow, HTTP Inbound Endpoint, receives orders entered by the user in the Web page served by the application. A SOAP Component converts the incoming XML into the JAXB annotated classes referenced in the Web service interface. The Choice Flow Control in the flow parses the message payload; if the payload defines the manufacturer as Samsung
, the Choice Strategy routes the message to a VM Outbound Endpoint which calls the samsungOrder flow. If the payload defines the manufacturer as Default
, the Choice Strategy routes the message to a VM Outbound Endpoint which calls the inhouseOrder flow.
When either the samsungOrder flow or the inhouseOrder flow replies, the orderService flow enriches the item with the purchase receipt provided by the replying flow. Then, the orderService flow uses another VM Outbound Endpoint to asynchronously dispatch the enriched message to the auditService flow.
Notes:
-
This flow uses a Session Variable Transformer to initialize the
totalValue
variable with the price of the item, in order to enable the auditService flow to use this value for auditing -
Each iteration replaces the payload variable with the result of inhouseOrder or samsungOrder. So in order to acess the original payload as it was before it entered the loop, we use the special for-each variable
rootMessage
:#[rootMessage.payload.orderItems[counter - 1].purchaseReceipt]
samsungOrder Flow
The samsungOrder flow delegates processing of Samsung order item requests to an external, SOAP-based Web service at Samsung.
The first building block is a VM Inbound Endpoint, which provides the flow with the information from the orderService flow. The second building block, an Anypoint DataMapper Transformer, transforms the message into one suitable for the samsungService flow. After successfully invoking the Samsung Web service, a Session Variable Transformer increments the session variable totalValue
with the price returned by Samsung. Then, a new DataMapper building block transforms the response again for processing by the orderService flow. In case of error, the flow creates a purchase receipt marked REJECTED
. A VM Outbound Endpoint sends the information back to the orderService flow.
Notes:
-
We chose to place this processing in a separate flow rather than a sub-flow in order to limit the scope of our exception handling (it is not possible to have an Exception Strategy on a sub-flow)
-
We use a Message Filter to throw an exception if the HTTP response code is anything other than 200 (success). Without it, the application would consider any HTTP response as successful, including errors such as a SOAP fault
inhouseOrder Flow
The inhouseOrder flow processes requests for all non-Samsung items.
The message source for this flow is a JMS Inbound Endpoint. The flow immediately initializes the variable Price
, then assigns to it the value returned by the in-house priceService flow. The inhouseOrder flow then saves this value to the company database. The session variable totalValue
holds the total price of this item. The last building block in the flow, a Groovy Component, creates a purchase receipt with the relevant information.
Notes:
-
This flow is transactional. It must not save data in the database if any errors occur in the life of the flow
-
Since the transaction must span a JMS endpoint and a JDBC Endpoint, an XA-Transaction is needed
-
The JMS Endpoint is configured to "ALWAYS-BEGIN" the transaction, and the JDBC Endpoint to "ALWAYS-JOIN" it
-
The Rollback Exception Strategy allows us to reinsert the message in the JMS queue in the event of an error
-
The
Redelivery exhausted
configuration allows us to determine what to do when the number of retries has reached the maximum specified in themaxRedeliveryAttempts
attribute of the Exception Strategy -
We cache the price returned by the priceService flow in an in-memory Object Store. The key to the store is the ID of the product requested. The first time that a given product ID appears, the
Enrich with price
Message Enricher invokes the priceService to obtain the price for the product. After that, the flow uses the cached value for the product -
A timeout can be configured on the object store used by the cache
priceService Flow
The inhouse RESTful priceService flow returns the price of non-Samsung products. The HTTP Inbound Endpoint Message Source passes the request to our Jersey backend REST Message Processor.
It’s important to note that the JAX-RS annotated Java implementation is one way of implementing your Web service. A whole flow can serve as the implementation of a Web service, whether it’s RESTful or SOAP-based.
samsungService Flow
The samsungService flow mocks the supposedly external Samsung Web service. It is sourced by the HTTP Inbound Endpoint followed by a SOAP Component configured as a JAX-WS Service. The service implementation is in the Samsung Service Impl
, a Java Component.
auditService Flow
The auditService flow, which is invoked asynchronously by the orderService flow, audits the item requests, which have been enriched with the responses from the inhouseOrder flow and the samsungOrder flow. The auditService flow’s transactional configuration is again XA due to the disparity between the VM Inbound Endpoint and the JDBC Endpoint.
Notes:
-
The source for the flow is a VM Inbound Endpoint, in contrast to the JMS Endpoint on the inhouseOrder flow. The reason is that the auditService flow invocation does not need to be synchronous, as is the case with the invocation for inhouseOrder. All transactional flows must be started by a one-way exchange pattern on the Inbound Endpoint, which can be defined by using a
request-response
exchange pattern on the invoking service -
In order to ensure reliable messaging (i.e., that messages are not lost in case processing stops due to an error), we wrap our Rollback Exception Strategy together with a sibling Catch Exception Strategy. These are both contained in a Choice Exception Strategy which defines which of them to use (whether Rollback or Catch Exception). If the Catch Exception Strategy is used, then the message is lost. In this case the defaultErrorHandler sub-flow emails the error to Operations
databaseIntialisation Flow
The databaseInitialisation flow initializes a local database to store any orders you place. As explained in Set Up and Run the Example, you invoke this flow by pointing your Web browser to http://localhost:8091/populate/. Invoke this flow the first time you run the application; it is not necessary to do so in subsequent runs.