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
Service Orchestration and Choice Routing Example
Enterprise, CloudHub
This application illustrates the orchestration of Web service calls and message queue submissions in order to fulfill an HTTP request for order fulfillment. Routing messages according to the content of their payload, the application efficiently coordinates order fulfillment with different vendors and initiates auditing of orders.
Foreach Processing
An iterative processor, Foreach splits collections into elements, then processes them iteratively without losing any of the message payload. After Foreach splits a message collection and processes the individual elements, it returns a reference to the original message thus resulting in "Java in, Java out" processing.
Content-Based Routing
Mule has the ability to intelligently route a message through different processing pathways according to its content. Using a Choice Router (or Choice Flow Control in Studio), Mule uses an expression to evaluate a message’s properties, or part of its payload, then routes it to a specific pathway (i.e. series of message processors). Content-based routing, as this activity is called, dynamically applies routing criteria to a message at runtime.
Service Orchestration
This term applies to the activity of coordinating calls to several different Web services in order to process a single Web service request. As the name implies, an application, such as this example, can orchestrate the sequence of calls to services. Like the conductor of an orchestra, a single Mule flow signals when to submit calls to services, ensuring that all the moving pieces work together to produce a single response.
Cache
Caching message content during processing saves on time and processing load by storing and reusing frequently called data. The cache wrapper (or cache scope in Studio) processes the message, delivers the output, and saves the output (i.e. caches the response). The next time the flow encounters the same kind of request, Mule may offer a cached response rather than invoking, again, a potentially time-consuming process.
Assumptions
This document assumes that you are familiar with Mule ESB and the Mule Studio interface. To increase your familiarity with Studio, consider completing one or more Mule Studio Tutorials. Further, this example assumes you are familiar with XML coding and that you have a basic understanding of Mule flows and SOAP as a Web service paradigm and the practice of WSDL-first Web service development.
This document describes the details of the example within the context of Mule Studio, Mule ESB’s graphical user interface (GUI), and includes configuration details for both the visual and XML editors.
Example Use Case
This example application simulates a Web application for an electronics store. Customers can place purchase orders through a Web interface. The backend 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 audits the order and amalgamates the results into a summary reply to be sent back to the customer. If the order fails and the purchase price is above $5000, the application initiates the rollback exception strategy.
Set Up and Run the Example
As with this example, you can create template applications straight out of the box in Mule 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 this example application in Mule ESB.
-
Create, then run the example application in Mule Studio.
-
Open a 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 displays a message that reads:
db populated
. -
In your Web browser, 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: -
The browser displays the following page.
-
Enter order data in the fields, then click Add.
-
The interface adds your order to the table structure on the right (see below). Click Submit to send your order to the Mule application.
-
Mule processes the order and returns a response which your browser displays in the Confirmation tab (see below).
How It Works
The Service Orchestration and Choice Routing application exposes a SOAP-based Web service, which accepts order requests and processes each item 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 application audits the order andamalgamates the results of item requests into a summary reply to be sent back to the client. If the order fails and the purchase price is above $5000, the application initiates the rollback exception strategy.
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.
orderService Flow
The main flow of the application, the orderService flow accepts orders via a SOAP Web service, routes the order to the appropriate order queue according to the content of the message, and dispatches a request to initiate an audit of the order. This flow is also responsible for returning a response to the caller to confirm that the order was processed.
Studio Visual Editor
Studio XML Editor
<flow name="orderService" doc:name="orderService">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="1080" path="orders" doc:name="/orders" doc:description="Process HTTP reqests or responses." connector-ref="HttpConnector"/>
<cxf:jaxws-service serviceClass="com.mulesoft.se.orders.IProcessOrder" doc:name="Order WebService" doc:description="Make a web service available via CXF"/>
<set-session-variable variableName="totalValue" value="0" doc:name="totalValue = 0"/>
<foreach collection="#[payload.orderItems]" doc:name="For each Order Item">
<enricher target="#[rootMessage.payload.orderItems[counter - 1].purchaseReceipt]" doc:name="Enrich with purchase receipt">
<choice doc:name="Choice">
<when expression="#[payload.manufacturer == 'Samsung']">
<processor-chain>
<vm:outbound-endpoint exchange-pattern="request-response" path="samsungOrder" doc:name="Dispatch to samsungOrder"/>
</processor-chain>
</when>
<otherwise>
<processor-chain>
<jms:outbound-endpoint exchange-pattern="request-response" queue="inhouseOrder" connector-ref="Active_MQ" doc:name="Dispatch to inhouseOrder"/>
</processor-chain>
</otherwise>
</choice>
</enricher>
</foreach>
<vm:outbound-endpoint exchange-pattern="one-way" path="audit" responseTimeout="10000" mimeType="text/plain" doc:name="Dispatch to audit"/>
<catch-exception-strategy doc:name="Catch Exception Strategy">
<flow-ref name="defaultErrorHandler" doc:name="Invoke defaultErrorHandler"/>
</catch-exception-strategy>
</flow>
The first building block in the orderService flow, an 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.
Studio Visual Editor
Studio XML Editor or Standalone
<flow name="samsungOrder" doc:name="samsungOrder">
<vm:inbound-endpoint exchange-pattern="request-response" path="samsungOrder" doc:name="samsungOrder"/>
<data-mapper:transform config-ref="OrderItemToOrderRequest" doc:name="OrderItem to OrderRequest"/>
<flow-ref name="samsungWebServiceClient" doc:name="Invoke Samsung WebService"/>
<message-filter throwOnUnaccepted="true" doc:name="Filter on 200 OK">
<message-property-filter pattern="http.status=200" caseSensitive="true" scope="inbound"/>
</message-filter>
<set-session-variable variableName="totalValue" value="#[totalValue + payload.price]" doc:name="totalValue += price"/>
<data-mapper:transform config-ref="OrderResponseToPurchaseReceipt" doc:name="OrderResponse to PurchaseReceipt"/>
<catch-exception-strategy doc:name="Catch Exception Strategy">
<scripting:transformer doc:name="Create REJECTED PurchaseReceipt">
<scripting:script engine="groovy">
<scripting:text><![CDATA[def receipt = new com.mulesoft.se.orders.PurchaseReceipt();
receipt.setStatus(com.mulesoft.se.orders.Status.REJECTED); receipt.setTotalPrice(0);
return receipt;]]></scripting:text>
</scripting:script>
</scripting:transformer>
</catch-exception-strategy>
</flow>
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.
Studio Visual Editor
Studio XML Editor or Standalone
<flow name="inhouseOrder" doc:name="inhouseOrder">
<jms:inbound-endpoint queue="inhouseOrder" connector-ref="Active_MQ" doc:name="inhouseOrder">
<xa-transaction action="ALWAYS_BEGIN"/>
</jms:inbound-endpoint>
<set-variable variableName="price" value="0" doc:name="Initialise Price"/>
<enricher target="#[price]" doc:name="Enrich with price">
<ee:cache cachingStrategy-ref="Caching_Strategy" doc:name="Cache the Price">
<http:outbound-endpoint exchange-pattern="request-response" host="localhost" port="9999" path="api/prices/#[payload.productId]" method="GET" disableTransportTransformer="true" doc:name="Invoke Price Service"/>
<core:object-to-string-transformer doc:name="Object to String"/>
</ee:cache>
</enricher>
<jdbc-ee:outbound-endpoint exchange-pattern="one-way" queryKey="insertOrder" queryTimeout="-1" connector-ref="JDBCConnector" doc:name="Save Order Item">
<xa-transaction action="ALWAYS_JOIN"/>
<jdbc-ee:query key="insertOrder" value="insert into orders (product_id, name, manufacturer, quantity, price) values (#[payload.productId], #[payload.name], #[payload.manufacturer], #[payload.quantity], #[price])"/>
</jdbc-ee:outbound-endpoint>
<set-variable variableName="totalPrice" value="#[price * payload.quantity]" doc:name="totalPrice = price * payload.quantity"/>
<set-session-variable variableName="totalValue" value="#[totalValue + totalPrice]" doc:name="totalValue += totalPrice"/>
<scripting:transformer doc:name="Groovy">
<scripting:script engine="Groovy">
<scripting:text><![CDATA[receipt = new com.mulesoft.se.orders.PurchaseReceipt(); receipt.setStatus(com.mulesoft.se.orders.Status.ACCEPTED); receipt.setTotalPrice(Float.valueOf(message.getInvocationProperty('totalPrice')));
return receipt;]]></scripting:text>
</scripting:script>
</scripting:transformer>
<rollback-exception-strategy maxRedeliveryAttempts="3" doc:name="Rollback Exception Strategy">
<logger message="#[payload:]" level="INFO" doc:name="Logger"/>
<on-redelivery-attempts-exceeded>
<flow-ref name="defaultErrorHandler" doc:name="Invoke defaultErrorHandler"/>
</on-redelivery-attempts-exceeded>
</rollback-exception-strategy>
</flow>
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.
Studio Visual Editor
Studio XML Editor or Standalone
<flow name="priceService" doc:name="priceService">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="9999" path="api" doc:name="/prices" connector-ref="HttpConnector"/>
<jersey:resources doc:name="Price Service">
<component class="com.mulesoft.se.orders.ProductPrice"/>
</jersey:resources>
</flow>
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.
Studio Visual Editor
Studio XML Editor or Standalone
<flow name="samsungService" doc:name="samsungService">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="9090" path="samsung/orders" doc:name="/samsung/orders" doc:description="Process HTTP reqests or responses." connector-ref="HttpConnector"/>
<cxf:jaxws-service serviceClass="com.mulesoft.se.samsung.SamsungService" doc:name="Order WebService" doc:description="Make a web service available via CXF"/>
<component class="com.mulesoft.se.samsung.SamsungServiceImpl" doc:name="Samsung Service Impl"/>
</flow>
This flow 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.
Studio Visual Editor
Studio XML Editor
<flow name="auditService" doc:name="auditService">
<vm:inbound-endpoint exchange-pattern="one-way" path="audit" responseTimeout="10000" mimeType="text/plain" doc:name="audit">
<xa-transaction action="ALWAYS_BEGIN"/>
</vm:inbound-endpoint>
<jdbc-ee:outbound-endpoint exchange-pattern="one-way" queryKey="insertOrderSummary" responseTimeout="10000" mimeType="text/plain" queryTimeout="-1" connector-ref="JDBCConnector" doc:name="Save OrderSummary">
<xa-transaction action="ALWAYS_JOIN"/>
<jdbc-ee:query key="insertOrderSummary" value="insert into order_audits values(default, #[payload.orderId], #[totalValue])"/>
</jdbc-ee:outbound-endpoint>
<choice-exception-strategy doc:name="Choice Exception Strategy">
<rollback-exception-strategy when="#[sessionVars['totalValue'] > 5000" doc:name="Rollback Exception Strategy"/>
<catch-exception-strategy doc:name="Catch Exception Strategy">
<flow-ref name="defaultErrorHandler" doc:name="Invoke defaultErrorHandler"/>
</catch-exception-strategy>
</choice-exception-strategy>
</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.
Studio Visual Editor
Studio XML Editor or Standalone
<sub-flow name="defaultErrorHandler" doc:name="defaultErrorHandler"> <logger message="Error occurred: #[payload]" level="INFO" doc:name="Log Error" /> <smtp:outbound-endpoint host="localhost" responseTimeout="10000" doc:name="Send Email to Operations" /> </sub-flow>
databaseInitialisation Flow
The databaseInitialisation flow initializes a local database to store any orders you place.
Studio Visual Editor
Studio XML Editor or Standalone
<flow name="databaseInitialisation" doc:name="databaseInitialisation">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8091" path="populate" doc:name="HTTP" connector-ref="HttpConnector"/>
<scripting:component doc:name="Create Tables">
<scripting:script engine="Groovy">
<scripting:text><![CDATA[jdbcConnector = muleContext.getRegistry().lookupConnector("JDBCConnector");
qr = jdbcConnector.getQueryRunner();
conn = jdbcConnector.getConnection();
qr.update(conn, "CREATE TABLE orders (i int generated always as identity, product_id varchar(256), name varchar(256), manufacturer varchar(256), quantity integer, price integer)");
qr.update(conn, "CREATE TABLE order_audits (i int generated always as identity, order_id varchar(256), total_value integer)");
return "db populated";]]></scripting:text>
</scripting:script>
</scripting:component>
<catch-exception-strategy doc:name="Catch Exception Strategy">
<set-payload value="table already populated" doc:name="'table already populated'"/>
</catch-exception-strategy>
</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.
Complete Code
Studio Visual Editor
Studio XML Editor or Standalone
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:ee="http://www.mulesoft.org/schema/mule/ee/core"
xmlns:cxf="http://www.mulesoft.org/schema/mule/cxf" xmlns:jms="http://www.mulesoft.org/schema/mule/jms"
xmlns:smtp="http://www.mulesoft.org/schema/mule/smtp" xmlns:jersey="http://www.mulesoft.org/schema/mule/jersey"
xmlns:data-mapper="http://www.mulesoft.org/schema/mule/ee/data-mapper"
xmlns:scripting="http://www.mulesoft.org/schema/mule/scripting"
xmlns:vm="http://www.mulesoft.org/schema/mule/vm" xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns:jbossts="http://www.mulesoft.org/schema/mule/jbossts"
xmlns:jdbc-ee="http://www.mulesoft.org/schema/mule/ee/jdbc" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
xmlns:core="http://www.mulesoft.org/schema/mule/core" xmlns:spring="http://www.springframework.org/schema/beans"
xmlns:mulexml="http://www.mulesoft.org/schema/mule/xml" xmlns:jdbc="http://www.mulesoft.org/schema/mule/ee/jdbc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="EE-3.4.0"
xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/ee/core http://www.mulesoft.org/schema/mule/ee/core/current/mule-ee.xsd
http://www.mulesoft.org/schema/mule/cxf http://www.mulesoft.org/schema/mule/cxf/current/mule-cxf.xsd
http://www.mulesoft.org/schema/mule/jms http://www.mulesoft.org/schema/mule/jms/current/mule-jms.xsd
http://www.mulesoft.org/schema/mule/smtp http://www.mulesoft.org/schema/mule/smtp/current/mule-smtp.xsd
http://www.mulesoft.org/schema/mule/jersey http://www.mulesoft.org/schema/mule/jersey/current/mule-jersey.xsd
http://www.mulesoft.org/schema/mule/ee/data-mapper http://www.mulesoft.org/schema/mule/ee/data-mapper/current/mule-data-mapper.xsd
http://www.mulesoft.org/schema/mule/scripting http://www.mulesoft.org/schema/mule/scripting/current/mule-scripting.xsd
http://www.mulesoft.org/schema/mule/vm http://www.mulesoft.org/schema/mule/vm/current/mule-vm.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/jbossts http://www.mulesoft.org/schema/mule/jbossts/current/mule-jbossts.xsd
http://www.mulesoft.org/schema/mule/ee/jdbc http://www.mulesoft.org/schema/mule/ee/jdbc/current/mule-jdbc-ee.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/xml http://www.mulesoft.org/schema/mule/xml/current/mule-xml.xsd ">
<http:connector name="HttpConnector" doc:name="HTTP\HTTPS" />
<jms:activemq-xa-connector name="Active_MQ"
brokerURL="vm://localhost" validateConnections="true" doc:name="Active MQ" />
<cxf:configuration initializeStaticBusInstance="false"
doc:name="CXF Configuration" doc:description="Global CXF Configuration" />
<data-mapper:config name="OrderItemToOrderRequest"
transformationGraphPath="orderitemtoorderrequest_1.grf" doc:name="DataMapper" />
<data-mapper:config name="OrderResponseToPurchaseReceipt"
transformationGraphPath="orderresponsetopurchasereceipt.grf" doc:name="DataMapper" />
<spring:beans>
<spring:bean id="Derby_Data_Source"
class="org.enhydra.jdbc.standard.StandardXADataSource"
destroy-method="shutdown">
<spring:property name="driverName"
value="org.apache.derby.jdbc.EmbeddedDriver" />
<spring:property name="url"
value="jdbc:derby:muleEmbeddedDB;create=true" />
</spring:bean>
</spring:beans>
<jdbc-ee:connector name="JDBCConnector"
dataSource-ref="Derby_Data_Source" validateConnections="true"
queryTimeout="-1" pollingFrequency="0" doc:name="Database" />
<jbossts:transaction-manager doc:name="Transaction Manager">
<property key="com.arjuna.ats.arjuna.coordinator.defaultTimeout"
value="600"></property>
<property key="com.arjuna.ats.arjuna.coordinator.txReaperTimeout"
value="1000000"></property>
</jbossts:transaction-manager>
<ee:object-store-caching-strategy
name="Caching_Strategy" keyGenerationExpression="#[payload.productId]"
doc:name="Caching Strategy">
<in-memory-store />
</ee:object-store-caching-strategy>
<mulexml:namespace-manager
includeConfigNamespaces="true">
<mulexml:namespace prefix="soap"
uri="http://schemas.xmlsoap.org/soap/envelope/" />
<mulexml:namespace prefix="ord"
uri="http://orders.se.mulesoft.com/" />
</mulexml:namespace-manager>
<flow name="orderService" doc:name="orderService">
<http:inbound-endpoint exchange-pattern="request-response"
host="localhost" port="1080" path="orders" doc:name="/orders"
doc:description="Process HTTP reqests or responses." connector-ref="HttpConnector" />
<cxf:jaxws-service serviceClass="com.mulesoft.se.orders.IProcessOrder"
doc:name="Order WebService" doc:description="Make a web service available via CXF" />
<set-session-variable variableName="totalValue"
value="0" doc:name="totalValue=0" />
<foreach collection="#[payload.orderItems]" doc:name="For each Order Item">
<enricher
target="#[rootMessage.payload.orderItems[counter - 1].purchaseReceipt]"
doc:name="Enrich with purchase receipt">
<choice doc:name="Choice">
<when expression="#[payload.manufacturer == 'Samsung']">
<vm:outbound-endpoint exchange-pattern="request-response"
path="samsungOrder" doc:name="Dispatch to samsungOrder" />
</when>
<otherwise>
<jms:outbound-endpoint exchange-pattern="request-response"
queue="inhouseOrder" connector-ref="Active_MQ" doc:name="Dispatch to inhouseOrder" />
</otherwise>
</choice>
</enricher>
</foreach>
<vm:outbound-endpoint exchange-pattern="one-way"
path="audit" responseTimeout="10000" mimeType="text/plain" doc:name="Dispatch to audit" />
<catch-exception-strategy doc:name="Catch Exception Strategy">
<flow-ref name="defaultErrorHandler" doc:name="Invoke defaultErrorHandler" />
</catch-exception-strategy>
</flow>
<flow name="samsungOrder" doc:name="samsungOrder">
<vm:inbound-endpoint exchange-pattern="request-response"
path="samsungOrder" doc:name="samsungOrder" />
<data-mapper:transform config-ref="OrderItemToOrderRequest"
doc:name="OrderItem to OrderRequest" />
<flow-ref name="samsungWebServiceClient" doc:name="Invoke Samsung WebService" />
<message-filter throwOnUnaccepted="true" doc:name="Filter on 200 OK">
<message-property-filter pattern="http.status=200"
caseSensitive="true" scope="inbound" />
</message-filter>
<set-session-variable variableName="totalValue"
value="#[totalValue + payload.price]" doc:name="totalValue += price" />
<data-mapper:transform config-ref="OrderResponseToPurchaseReceipt"
doc:name="OrderResponse to PurchaseReceipt" />
<catch-exception-strategy doc:name="Catch Exception Strategy">
<scripting:transformer doc:name="Create REJECTED PurchaseReceipt">
<scripting:script engine="groovy">
<scripting:text><![CDATA[def receipt = new com.mulesoft.se.orders.PurchaseReceipt();
receipt.setStatus(com.mulesoft.se.orders.Status.REJECTED); receipt.setTotalPrice(0);
return receipt;]]></scripting:text>
</scripting:script>
</scripting:transformer>
</catch-exception-strategy>
</flow>
<sub-flow name="samsungWebServiceClient" doc:name="samsungWebServiceClient">
<cxf:jaxws-client operation="purchase"
clientClass="com.mulesoft.se.samsung.SamsungServiceService" port="SamsungServicePort"
doc:name="Samsung Webservice Client" />
<http:outbound-endpoint exchange-pattern="request-response"
host="localhost" port="9090" path="samsung/orders" doc:name="/samsung/orders" />
</sub-flow>
<flow name="inhouseOrder" doc:name="inhouseOrder">
<jms:inbound-endpoint queue="inhouseOrder"
connector-ref="Active_MQ" doc:name="inhouseOrder">
<xa-transaction action="ALWAYS_BEGIN" />
</jms:inbound-endpoint>
<set-variable variableName="price" value="0"
doc:name="Initialise Price" />
<enricher target="#[price]" doc:name="Enrich with price">
<ee:cache cachingStrategy-ref="Caching_Strategy" doc:name="Cache the Price">
<http:outbound-endpoint exchange-pattern="request-response"
host="localhost" port="9999" path="api/prices/#[payload.productId]"
method="GET" disableTransportTransformer="true" doc:name="Invoke Price Service" />
<object-to-string-transformer doc:name="Object to String" />
</ee:cache>
</enricher>
<jdbc-ee:outbound-endpoint exchange-pattern="one-way"
queryKey="insertOrder" queryTimeout="-1" connector-ref="JDBCConnector"
doc:name="Save Order Item">
<xa-transaction action="ALWAYS_JOIN" />
<jdbc-ee:query key="insertOrder"
value="insert into orders (product_id, name, manufacturer, quantity, price) values (#[payload.productId], #[payload.name], #[payload.manufacturer], #[payload.quantity], #[price])" />
</jdbc-ee:outbound-endpoint>
<set-variable variableName="totalPrice" value="#[price * payload.quantity]"
doc:name="totalPrice = price * payload.quantity" />
<set-session-variable variableName="totalValue"
value="#[totalValue + totalPrice]" doc:name="totalValue += totalPrice" />
<scripting:transformer doc:name="Groovy">
<scripting:script engine="Groovy">
<scripting:text><![CDATA[receipt = new com.mulesoft.se.orders.PurchaseReceipt(); receipt.setStatus(com.mulesoft.se.orders.Status.ACCEPTED); receipt.setTotalPrice(Float.valueOf(message.getInvocationProperty('totalPrice')));
return receipt;]]></scripting:text>
</scripting:script>
</scripting:transformer>
<rollback-exception-strategy
maxRedeliveryAttempts="3" doc:name="Rollback Exception Strategy">
<logger message="#[payload:]" level="INFO" doc:name="Logger" />
<on-redelivery-attempts-exceeded
doc:name="Redelivery exhausted">
<flow-ref name="defaultErrorHandler" doc:name="Invoke defaultErrorHandler" />
</on-redelivery-attempts-exceeded>
</rollback-exception-strategy>
</flow>
<flow name="auditService" doc:name="auditService">
<vm:inbound-endpoint exchange-pattern="one-way"
path="audit" responseTimeout="10000" mimeType="text/plain" doc:name="audit">
<xa-transaction action="ALWAYS_BEGIN" />
</vm:inbound-endpoint>
<jdbc-ee:outbound-endpoint exchange-pattern="one-way"
queryKey="insertOrderSummary" responseTimeout="10000" mimeType="text/plain"
queryTimeout="-1" connector-ref="JDBCConnector" doc:name="Save OrderSummary">
<xa-transaction action="ALWAYS_JOIN" />
<jdbc-ee:query key="insertOrderSummary"
value="insert into order_audits values(default, #[payload.orderId], #[totalValue])" />
</jdbc-ee:outbound-endpoint>
<choice-exception-strategy doc:name="Choice Exception Strategy">
<rollback-exception-strategy when="#[sessionVars['totalValue'] > 5000"
doc:name="Rollback Exception Strategy" />
<catch-exception-strategy doc:name="Catch Exception Strategy">
<flow-ref name="defaultErrorHandler" doc:name="Invoke defaultErrorHandler" />
</catch-exception-strategy>
</choice-exception-strategy>
</flow>
<flow name="priceService" doc:name="priceService">
<http:inbound-endpoint exchange-pattern="request-response"
host="localhost" port="9999" path="api" doc:name="/prices"
connector-ref="HttpConnector" />
<jersey:resources doc:name="Price Service">
<component class="com.mulesoft.se.orders.ProductPrice" />
</jersey:resources>
</flow>
<flow name="samsungService" doc:name="samsungService">
<http:inbound-endpoint exchange-pattern="request-response"
host="localhost" port="9090" path="samsung/orders" doc:name="/samsung/orders"
doc:description="Process HTTP reqests or responses." connector-ref="HttpConnector" />
<cxf:jaxws-service serviceClass="com.mulesoft.se.samsung.SamsungService"
doc:name="Order WebService" doc:description="Make a web service available via CXF" />
<component class="com.mulesoft.se.samsung.SamsungServiceImpl"
doc:name="Samsung Service Impl" />
</flow>
<sub-flow name="defaultErrorHandler" doc:name="defaultErrorHandler">
<logger message="Error occurred: #[payload]" level="INFO"
doc:name="Log Error" />
<smtp:outbound-endpoint host="localhost"
responseTimeout="10000" doc:name="Send Email to Operations" />
</sub-flow>
<flow name="databaseInitialisation" doc:name="databaseInitialisation">
<http:inbound-endpoint exchange-pattern="request-response"
host="localhost" port="8091" path="populate" doc:name="HTTP"
connector-ref="HttpConnector" />
<scripting:component doc:name="Create Tables">
<scripting:script engine="Groovy">
<scripting:text><![CDATA[jdbcConnector = muleContext.getRegistry().lookupConnector("JDBCConnector");
qr = jdbcConnector.getQueryRunner();
conn = jdbcConnector.getConnection();
qr.update(conn, "CREATE TABLE orders (i int generated always as identity, product_id varchar(256), name varchar(256), manufacturer varchar(256), quantity integer, price integer)");
qr.update(conn, "CREATE TABLE order_audits (i int generated always as identity, order_id varchar(256), total_value integer)");
return "db populated";]]></scripting:text>
</scripting:script>
</scripting:component>
<catch-exception-strategy doc:name="Catch Exception Strategy">
<set-payload value="table already populated" doc:name="'table already populated'" />
</catch-exception-strategy>
</flow>
</mule>
Documentation
Studio includes a feature that enables you to easily export all the documentation you have recorded for your project. Whenever you want to easily share your project with others outside the Studio environment, you can export the project’s documentation to print, email or share online. Studio’s auto-generated documentation includes:
-
a visual diagram of the flows in your application
-
the XML configuration which corresponds to each flow in your application
-
the text you entered in the Documentation tab of any building block in your flow
See Also
-
Learn more about about the SOAP Component.
-
Learn more about the Choice Router/Choice Flow Control.
-
Learn more about Anypoint DataMapper.