Nav
You are viewing an older version of this section. Click here to navigate to the latest version.

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

iteration2An 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

content_routingMule 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

orchestrateThis 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

cache_iconCaching 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.

  1. Create, then run the example application in Mule Studio.

  2. 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.

  3. 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:

    
                
             
    1
    2
    3
    4
    
    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
  4. The browser displays the following page.

    init.page

  5. Enter order data in the fields, then click Add.

  6. The interface adds your order to the table structure on the right (see below). Click Submit to send your order to the Mule application.

    order_Processing_table2

  7. Mule processes the order and returns a response which your browser displays in the Confirmation tab (see below).

    order_Processing_confirmation

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.  

orderServiceFlow


    
             
          
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<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 http://en.wikipedia.org/wiki/JAXB[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:

    
                 
              
    1
    
    #[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.

samsungOrder


    
             
          
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<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.

inhouseOrder


    
             
          
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<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 the maxRedeliveryAttempts 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.

priceServiceFlow


    
             
          
1
2
3
4
5
6
<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. 

samsungServiceFlow


    
             
          
1
2
3
4
5
<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.

auditServiceFlow


    
             
          
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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.

    defaultErrorHandler

    
        
                    
                 
    1
    2
    3
    4
    5
    6
    
    <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.

dbInitializeFlow


    
             
          
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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

order_processing


    
             
          
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
<?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