Error Handling, Caching, Iterative Processing and Expressions in Mule 3.3
Error Handling, Caching, Iterative Processing and Expressions in Mule 3.3
Join the DZone community and get the full member experience.Join For Free
Learn more about how to Prevent Slow or Broken APIs From Affecting Your Bottom Line.
If you haven't heard already, there are powerful features in Mule 3.3:
Improved Error Handling: New exception strategy patterns fully integrated in Studio. Included are Try/Catch, Rollback processing and Conditional Exception processing in Choice routers based on exception type, or just about any criteria!
Iterative Processing using Foreach Scope: Allows for iterative loop type processing while maintaining the original message context
Mule Expression Language: A new, unified expression language to improve consistency and ease of use when validating, filtering, routing, or transforming messages
Cache: Improve performance with “In memory” caching of messages, such as the results of service calls
Graphical Data Mapper: A new graphical tool to easily transform from one data format to another data format while also mapping specific fields within the message structure. Formats supported include XML, JSON, CSV, POJOs, collections of POJOs, and EXCEL
We'll introduce these features by walking you through the development of a Mule Application which exploits each of these new features. As we work our way through the development process we will describe what we want to achieve and explain how to achieve that in Mule Studio. For the sake of simplicity we will refer you to our online documentation when we touch on features already available in previous releases.
As part of the integration plaform of Stallion Electronics, we wish to develop an Order Processing SOAP based Webservice. The service should accept Order requests and process each item requested inside the order. If a requested item is manufactured by Samsung, then that particular item request should be routed to an external SOAP based Webservice hosted by Samsung. Stallion Electronics should handle all other item requests. Regardless, the result of each item request should be stored (success / failure) together with the price. For non-Samsung requests, the price of the product should be retrieved from an inhouse RESTful webservice. To reduce calls to this service and make the Order processing more efficient, the results of these calls should be cached for a period of time. Finally the results of the Item requests should be amalgamated into a summary reply to be sent back to the client. Before replying, the summary should be audited. The audit of these summaries must be guaranteed in the case of orders with a total value greater than 5000 dollars. Failed invocations of Samsung’s webservice need not be retried, rather the status of the purcaseReceipt should be set to REJECTED.
We expose the Order Processing webservice on the orderService flow. This delegates processing to the other flows and sub-flows.
Orders arrive over HTTP. We immediately dispatch each item to the corresponding Order Item processing service. Once all these Item requests have been processed, the summary of the Order is audited before returning to the client. Exceptions are caught and processed by the defaultErrorHandler sub-flow.
Our first task is to initialise our totalValue variable to cater for our auditing requirements. This should be in Session scope as we’ll need to access it in other flows.
|*||We now have a new Building Block at our disposal, the Session Variable Transformer. Use this both to set and remove variables of Session scope.|
Both the name of the variable and the value can be set using the same expression language. In other words, we can decide the name of the variable and its value at runtime!
Note the literal / expression drop-down for both Name and Value!
Next up, we need to iterate over all of the items in the Order.
|To achieve this we can use the Foreach scope. Any processing needed on a particular Item should be inside the scope.
The scope splits the message up into separate messages for each item on the specified collection.
This flow is not transactional so we are only interested in logging errors, perhaps informing Operations also, so for this common scenario we have the defaultErrorHandler sub-flow. To delegate processing to defaultErrorHandler, let´s just drag a CatchExceptionStrategy to orderService. These Exception Strategies are sub-flows in their own right, so we can do just about anything in there. We can just drag the flow-ref into it. No more configuration needed here.
The expression used in our Choice Message Routing Processor above is #[payload.manufacturer == ‘Samsung’]. See how we have direct access to payload. It’s one of a number of context variables our Expression Language exposes to us. Bearing in mind that our original Order message has been split into the item messages inside it, payload now refers to the Item pertaining to the current iteration.
Hence we arrive in some cases here at samsungOrder:
Items arrive on our Inbound VM endpoint, which we need to transform into OrderRequests for the invocation of Samsung’s webservice. We invoke the service and filter only those responses that have HTTP status 200. We proceed to increment our totalValue variable before transforming the response from Samsung into a PurchaseReceipt instance representing the status (success / failure) of that request together with the total price.
Graphical Data Mapper
Transformations are extremely common to integration solutions. Mule has long provided you with a large array of transformers of both data-type and content. The latter can be pretty expensive exercises when payloads are large or computations are needed to produce the desired value. Now with Studio, we have a simple but very powerful graphical mapping tool which both speeds up the initial transformation effort and acts as a marvellous documentation artifact for the same. At a glance you can see exactly what has been defined.
Here we need to transform our OrderItem instance into an OrderRequest as expected by Samsung’s webservice.
Note how we have each field mapped to its target through the curved arrows. Targets and arrows are highlighted as you click on them for even easier viewing.
We also need to transform the result returned by Samsung´s webservice into our own PurchaseReceipt:
As agreed, failed invocations of Samsung’s Order processing webservice should not be retried. Instead a REJECTED PurchaseReceipt shoud be instantiated. As above we use a Catch Exception Strategy and place a script component inside it.
We need to increment the totalValue Session variable:
The instantiation needed in the Exception Strategy is easy with our new Expression Language:
|*||Things to note here: this is not Groovy so no def for variable declaration! with statement allows for group setter invocation (separated by commas) Semi-colon at the end of each line and finally assignment to payload.|
All items that are not requests for Samsung products arrive here. This flow differs from the samsungOrder in that it is transactional: nothing is saved to the database until we have successfully created our PurchaseReceipt at the end. As soon as the message arrives, we invoke the Price Service to retrieve the price of the requested product. If the product ID is in the cache it is sufficient to use the cache value, otherwise the webservice should be invoked and the price stored in the cache. We subsequently increment the totalValue by that price (times the quantity requested). Save order to database and then create the PurchaseReceipt.
An extremely beneficial new scope has been added to our toolbox: whole service invocation results can be stored for a given time. In plain English: cache the price of a particular product.
|*||Every cache needs a Caching Strategy. This basically decides how to store the value (in this case we use a memory store) and how to generate the key (we use an expression).|
|These values are keyed by any expression. In this case we use the payload.productId (remember the payload is an OrderItem after the Foreach above!).||*|
|*||So, with the store and the stategy setup, we can now create our Cache and refer to the Strategy.|
Now we have a different ball game: this flow needs to have its transactions considered–it’s all or nothing! For that we have the Rollback Exception Strategy. The message will remain on the VM inbound endpoint until we have completed the whole flow. One point to note here. The processing strategy of the flow must be set to Synchronous, otherwise the application will fail. This is because reliable transports do not consume messages, they wait for messages to successfully complete a flow before consuming it; therefore, the flow must process synchronously.
Our auditService is the last point on our flow. All Order Items have been processed and the results stored in the same payload that arrived from the client. We just need to audit these results. However, this is only a strict requirement when the total value of the order is greater than 500 dollars. So our strategy is to attempt always to audit and only retry for such values.
The Choice Exception Strategy can nest any number of Exception Strategies. Each of these has a ‘when’ field which we can use to direct the wrapping Choice strategy.In this case we just specify the criteria which the wrapping Choice Exception Strategy will use to determine which Strategy to delegate the processing. For all other cases (just leave when blank) we can use our familiar Catch Strategy. Note how we specify max redelivery attempts as 3 times.
The priceService is a simple RESTful webservice flow which returns the price of a product. It is blissfully unaware that its results are being cached by our Cache scope above.
Invoked by our Catch Exception Strategies, we simply log the error and let Operations know that trouble is brewing.
Right-click on the Application in Mule Studio and select Run As Mule Application. In just a few seconds you will have an instance of Mule Enterprise Edition Server running with your Application deployed and its Webservice listening for requests over the configured host and port.
First initialise the database (it’s a Derby database which will install itself in the root of your project. Just browse to http://localhost:8091/populate to kick off the init flow
The invocation of the webservice consists in posting a SOAP envelope with the details of the order request inside the Body element. To do this, we can use SoapUI, a well known tool for testing Webservices.
Here, we’ve requested 5 Samsung Galaxies and 3 Nokia N900s
The response should come back looking something like this:
That’s all folks!
We hope you’ve enjoyed these valuable additions to an already rich integration platform! This project is available with Mule Studio Enterprise 3.3 as a template. If you haven’t already, you can download Mule here. Enjoy!
Opinions expressed by DZone contributors are their own.