Contact Us 1-800-596-4880

Flight Reservation Example

Version 3.3.1 only

Enterprise Edition

This example application simulates an online reservation system that allows users to search for the best flights available between two cities. The system processes requests and provides the flight numbers, seating information and price for each leg of a three-leg flight, computing the total price at the end.

This Flight Reservation example application consists of two parts, each fully implemented in Mule ESB:

  1. Client-side Frontend Application
    A client-side application which accepts end user requests from an AJAX-based graphical user interface (GUI). This application enables users to to select between one departure city and two possible destinations.

  2. Server-side Backend Application
    A server-side application that processes end-user requests. The application orchestrates data operations and transfer between four flows. It uses several of the new features added in Mule 3.3, including:

Where is the Client-side Code?

To examine the code of the client-side interface, access the file in the src/main/app/docroot folder in Mule.

  1. In the Package Explorer, click to expand the src folder.

  2. Click to expand folders to navigate to main > app > docroot.

  3. Double-click the following files to open them in news canvas panes in Studio:

    • flight-reservations.css

    • flight-reservations.js

    • index.html

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.

This document describes the details of the example within the context of Mule Studio, Mule ESB’s graphical user interface (GUI). Where appropriate, the XML configuration follows the Studio interface screenshot in an expandable section.

Set Up

As with this Flight Reservation 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 Flight Reservation application in Mule ESB.

  1. Complete the procedure in Examples and Exercises to create, then run the Flight Reservation template in Mule Studio, or the Flight Reservation example in Mule Standalone (Mule ESB without Studio).

  2. Open your Web browser.

  3. Browse to the URL http://localhost:9092/reservation/. The application displays its Web user interface (see below).

    Flight+Reservation+UI

The application includes three cities preloaded in the Origin and Destination combo boxes, and can output the following three results:

  1. Error (if no city was selected in one or both combo boxes)

    Flight+Reservation+Invalid+Error
  2. No flight available (Buenos Aires to Hong Kong)

    Flight+Resrvation+no+flights
  3. Flight information for a three-leg flight (Buenos Aires to Moscow)

    Flight+Reservation+success

When successful, the search results display a table with information for each flight segment and the total price for the flight (see image above).

How it works

The Flight Reservation application consists of four flowsMule Application Architecture. Together, these flows process end-user requests, then return responses which contain flight information.

partA-1

partB-1

View the XML

<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns="http://www.mulesoft.org/schema/mule/core"
      xmlns:json="http://www.mulesoft.org/schema/mule/json"
      xmlns:vm="http://www.mulesoft.org/schema/mule/vm"
      xmlns:http="http://www.mulesoft.org/schema/mule/http"
      xmlns:scripting="http://www.mulesoft.org/schema/mule/scripting"
      xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
      xmlns:core="http://www.mulesoft.org/schema/mule/core"
      xmlns:ajax="http://www.mulesoft.org/schema/mule/ajax"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      version="EE-3.3.0" xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.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/ajax http://www.mulesoft.org/schema/mule/ajax/current/mule-ajax.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/scripting http://www.mulesoft.org/schema/mule/scripting/current/mule-scripting.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd">

    <ajax:connector name="ajaxServer" serverUrl="http://0.0.0.0:9092/reservation" resourceBase="${app.home}/docroot" doc:name="Ajax"/>
    <flow name="makeReservation" doc:name="makeReservation">
        <ajax:inbound-endpoint channel="/searchFlights" responseTimeout="10000" connector-ref="ajaxServer" doc:name="Search Flights"/>
        <object-to-string-transformer doc:name="Object to String"/>
        <flow-ref name="processReservation" doc:name="processReservation"/>
    </flow>
    <flow name="processReservation" doc:name="processReservation">
        <json:json-to-object-transformer returnClass="org.mule.example.ReservationRequest" doc:name="JSON to ReservationRequest"/>
        <set-session-variable variableName="reservationRequest" value="#[payload]" doc:name="Save original request in Session"/>
        <set-payload value="#[new org.mule.example.ReservationResponse()]" doc:name="Set ReservationResponse payload"/>
        <expression-component doc:name="Add request flight to response">payload.setFlights(reservationRequest.flights)</expression-component>
        <set-variable variableName="totalPrice" value="#[0]" doc:name="Initialize totalPrice"/>
        <foreach collection="#[payload.flights]" doc:name="Foreach on flights">
            <logger message="Before throw exception" level="ERROR" doc:name="Logger"/>
            <scripting:transformer doc:name="Search flight availability">
                <scripting:script engine="Groovy">
                    <scripting:text><![CDATA[if (payload.flightNumber.endsWith('3'))
   throw new org.mule.example.FlightUnavailableException()
else
   payload]]></scripting:text>
                </scripting:script>
            </scripting:transformer>
            <vm:outbound-endpoint exchange-pattern="request-response" path="acquireSeatsInfoQueue" doc:name="Acquire Seats Info"/>
            <vm:outbound-endpoint exchange-pattern="request-response" path="acquireFlightPrice" doc:name="Acquire Flight Price"/>
            <set-variable variableName="totalPrice" value="#[totalPrice + payload.ticketPrice]" doc:name="Add price to totalPrice"/>
        </foreach>
        <expression-component doc:name="Add total price to reservation">payload.totalPrice = flowVars['totalPrice']</expression-component>
        <json:object-to-json-transformer doc:name="Object to JSON"/>
        <choice-exception-strategy doc:name="Choice Exception Strategy">
            <catch-exception-strategy when="#[exception.causedBy(org.mule.example.FlightUnavailableException)]" doc:name="Catch Exception Strategy">
                <logger message="In Catch Exception" level="ERROR" doc:name="Logger"/>
                <scripting:transformer doc:name="Add no availability error">
                    <scripting:script engine="Groovy">
                        <scripting:text><![CDATA[def payload = new org.mule.example.ReservationResponse()
payload.addError('There is no availability for the selected flight')
payload]]></scripting:text>
                    </scripting:script>
                </scripting:transformer>
                <json:object-to-json-transformer doc:name="Object to JSON"/>
            </catch-exception-strategy>
            <catch-exception-strategy doc:name="Catch Exception Strategy">
                <scripting:transformer doc:name="Add exception message">
                    <scripting:script engine="Groovy">
                        <scripting:text><![CDATA[def payload = new org.mule.example.ReservationResponse()
payload.addError("Error processing request")
payload]]></scripting:text>
                    </scripting:script>
                </scripting:transformer>
                <set-property propertyName="http.status" value="500" doc:name="Set http status 500"/>
                <json:object-to-json-transformer doc:name="Object to JSON"/>
            </catch-exception-strategy>
        </choice-exception-strategy>
    </flow>
    <flow name="acquireSeatsInfo" doc:name="acquireSeatsInfo">
        <vm:inbound-endpoint exchange-pattern="request-response" path="acquireSeatsInfoQueue" doc:name="acquireSeatsInfo"/>
        <scripting:component doc:name="Aquire seats info service">
            <scripting:script engine="Groovy">
                <scripting:text><![CDATA[if (payload.flightNumber.endsWith('2'))
   payload.seatInfo = '20A'
else
   throw new Exception()
payload]]></scripting:text>
            </scripting:script>
        </scripting:component>
        <catch-exception-strategy doc:name="Catch Exception Strategy">
            <expression-component doc:name="Add no seat info avaiable message">payload.seatInfo = 'No seat info available'</expression-component>
        </catch-exception-strategy>
    </flow>
    <flow name="acquireFlightPrice" doc:name="acquireFlightPrice">
        <vm:inbound-endpoint exchange-pattern="request-response" path="acquireFlightPrice" doc:name="acquireFlightPrice"/>
        <expression-component doc:name="acquireFlightPrice">payload.ticketPrice = Integer.valueOf(payload.flightNumber) * 2</expression-component>
    </flow>
</mule>

The sections below offer flow-by-flow descriptions of the Flight Reservation system’s actions as it processes end-user requests.

For Mule Studio Users

In Mule Studio, double-click a building block to open its Properties Panel, then examine its configuration details. Alternatively, click the Configuration XML tab to examine the application’s XML configuration file.

config_tab

MakeReservation flow

This first flow in the application receives incoming requests and forwards them to the next flow, the processReservation flow.

MakeReservation+Flow

View the XML

<ajax:connector name="ajaxServer" serverUrl="http://0.0.0.0:9092/reservation" resourceBase="${app.home}/docroot" doc:name="Ajax"/>

<flow name="makeReservation" doc:name="makeReservation">
    <ajax:inbound-endpoint channel="/searchFlights" responseTimeout="10000" connector-ref="ajaxServer" doc:name="Search Flights"/>
    <object-to-string-transformer doc:name="Object to String"/>
    <flow-ref name="processReservation" doc:name="processReservation"/>
</flow>

The first building block in the flow is an AJAX Inbound Endpoint configured to listen for incoming requests. The endpoint references the configuration values set in an AJAX Global Element, which defines the parameters for the listening endpoint.

What is an AJAX Global Element?

AJAX building block must reference an AJAX Global Element. The global element contains the basic configuration for the AJAX endpoint, such as server URL and document resource base. Complete the following procedure to see the AJAX global endpoint’s configuration in Studio.

  1. Click the Global Elements tab to the right of the Message Flow tab.

    global.elements.tab
  2. Click the ajaxServer row in the Global Configuration Elements view, then click Edit.

    global.element-edit
  3. Studio displays the Global Element Properties window for the AJAX Global Element.

    global.element-props

The AJAX Endpoint forwards the data to the next building block, an Object to String transformer which converts the data to a readable text string. Then, a Flow Reference Component forwards the data to the the ProcessReservation flow. This second flow contains the application’s main logic.

ProcessReservation Flow

processflow1
processflow2

View the XML

<json:json-to-object-transformer returnClass="org.mule.example.ReservationRequest" doc:name="JSON to ReservationRequest"/>
<set-session-variable variableName="reservationRequest" value="#[payload]" doc:name="Save original request in Session"/>
<set-payload value="#[new org.mule.example.ReservationResponse()]" doc:name="Set ReservationResponse payload"/>
<expression-component doc:name="Add request flight to response">payload.setFlights(reservationRequest.flights)</expression-component>
<set-variable variableName="totalPrice" value="#[0]" doc:name="Initialize totalPrice"/>

This flow receives requests in JSON format from the frontend Web application. The ProcessReservation flow must, therefore, transform the JSON object into a Java object that the rest of the application can use. To do so, this flow uses a JSON to Object Transformer.

The second building block in the flow, the Session Variable Transformer, stores the original request as a variable. Unlike a flow variable which remains active in one specific flow, a session variable remains active and available for the whole application. In this example, the session variable is called reservationRequest.

Then, the Set Payload Transformer creates a response, called ReservationResponse, which Mule populates with information from the reservationRequest session variable. To populate reservationResponse, Mule uses an Expression Component.

Next, the Variable Transformer sets a an empty totalPrice variable on the message. Unlike variables set with the session variable transformer, this variable is only valid in the current flow.

Foreach

The Foreach scope splits the collection into elements, then iteratively processes them through the message processors defined within the scope. Mule extracts the collection from #[payload.flights], which holds the flight segments' information (see image below).

foreach

View the XML

<foreach collection="#[payload.flights]" doc:name="Foreach on flights">
            <logger message="Before throw exception" level="ERROR" doc:name="Logger"/>
            <scripting:transformer doc:name="Search flight availability">
                <scripting:script engine="Groovy">
                    <scripting:text><![CDATA[if (payload.flightNumber.endsWith('3'))
   throw new org.mule.example.FlightUnavailableException()
else
   payload]]></scripting:text>
                </scripting:script>
            </scripting:transformer>
            <vm:outbound-endpoint exchange-pattern="request-response" path="acquireSeatsInfoQueue" doc:name="Acquire Seats Info"/>
            <vm:outbound-endpoint exchange-pattern="request-response" path="acquireFlightPrice" doc:name="Acquire Flight Price"/>
            <set-variable variableName="totalPrice" value="#[totalPrice + payload.ticketPrice]" doc:name="Add price to totalPrice"/>
        </foreach>

The Groovy Transformer in the Foreach scope, Search flight availability, invokes a Groovy script that checks availability for the flight segment. For this example, the script simply throws a FlightUnavailableException if the flight number ends with 3.

If there is availability for the flight segment, the Groovy transformer sends the payload to the next building block, Acquire Seats Info. This is an In Memory (VM) Outbound Endpoint that invokes the AcquireSeatsInfo Flow (visible near the bottom of the Studio canvas). The AcquireSeatsInfo flow returns a seat number or a message that reads, No seat info available.

How does this flow invoke another?

The In Memory (VM) transport allows Mule applications to use intra-Java Virtual Machine communications between Mule flows.

In this case, the Acquire Seats Info building block is an outbound endpoint, configured as a request-response transport, that calls a memory queue named acquireSeatsInfoQueue.

This queue is defined in the first building block of the acquireSeatsInfo flow, which is also an In Memory transport (an inbound endpoint in this case).

Next, the Acquire Flight Price building block invokes the acquireFlightPrice flow, which returns the price of the flight.

The final building block in the Foreach scope is a Variable Transformer that sets the value for the flight segment to the price for the entire flight in the totalPrice variable. (Mule set the an empty totalPrice variable on the message just prior to its entering the Foreach scope.)

After the Foreach scope, an Expression Transformer adds the final price to the response. Finally, Mule converts the response back to JSON for receipt by the client-side Web browser interface.

Error Handling With a Choice Exception Strategy

To handle errors within the ProcessReservation flow, Mule uses a Choice Exception Strategy. This exception strategy routes messages according to the cause of each exception.

choice_ES

View the XML

<choice-exception-strategy doc:name="Choice Exception Strategy">
    <catch-exception-strategy when="#[exception.causedBy(org.mule.example.FlightUnavailableException)]" doc:name="Catch Exception Strategy">
        <scripting:transformer doc:name="Add no availability error">
            <scripting:script engine="Groovy">
                <scripting:text><![CDATA[def payload = new org.mule.example.ReservationResponse()
payload.addError('There is no availability for the selected flight')
payload]]>      </scripting:text>
            </scripting:script>
        </scripting:transformer>
        <json:object-to-json-transformer doc:name="Object to JSON"/>
    </catch-exception-strategy>
    <catch-exception-strategy doc:name="Catch Exception Strategy">
        <scripting:transformer doc:name="Add exception message">
             <scripting:script engine="Groovy">
                 <scripting:text><![CDATA[def payload = new org.mule.example.ReservationResponse()
payload.addError("Error processing request")
payload]]>
                 </scripting:text>
             </scripting:script>
         </scripting:transformer>
         <set-property propertyName="http.status" value="500" doc:name="Set http status 500"/>
         <json:object-to-json-transformer doc:name="Object to JSON"/>
    </catch-exception-strategy>
</choice-exception-strategy>

The first Catch Exception Strategy within the choice exception strategy uses an expression to handle all FlightUnavailableException exceptions (see image below). When this exception occurs, the catch exception strategy uses a Groovy transformer to generate an error message stating the lack of availability for the flight.

catch_ES

The second catch exception strategy handles all other exceptions. Like the first catch exception strategy, it uses a Groovy transformer to generate an error message, in this case, Error processing request. Next, it uses a Property Transformer to set the HTTP status code to 500: Internal Error. Finally, it transforms the response to JSON data-format for the client-side Web browser.

AcquireSeatsInfo Flow

Invoked by the Foreach scope in the ProcessReservation flow, this flow provides a valid response for each request it receives.

Acquire+seat+info+flow

View the XML
<flow name="acquireSeatsInfo" doc:name="acquireSeatsInfo">
    <vm:inbound-endpoint exchange-pattern="request-response" path="acquireSeatsInfoQueue" doc:name="acquireSeatsInfo"/>
    <scripting:component doc:name="Aquire seats info service">
        <scripting:script engine="Groovy">
            <scripting:text><![CDATA[if (payload.flightNumber.endsWith('2'))
   payload.seatInfo = '20A'
else
   throw new Exception()
payload]]>
            </scripting:text>
        </scripting:script>
    </scripting:component>
    <catch-exception-strategy doc:name="Catch Exception Strategy">
        <expression-component doc:name="Add no seat info available message">payload.seatInfo = 'No seat info available'</expression-component>
    </catch-exception-strategy>
</flow>

The first building block in the flow, AcquireSeatsInfo, is an In Memory (VM) Inbound Endpoint. The Acquire Seats Info building block in the Foreach scope in the ProcessReservation flow invokes this building block.

The next building block, a Groovy Component, generates a response that contains flight seat information. For the purposes of this example, it simply returns a seat number if the flight number ends with 2; otherwise, it throws an exception.

The catch exception strategy handles any exceptions that occur in this flow. It uses an Expression Component to add a message to the response that reads, No seat info available.

AcquireFlightPrice flow

Invoked by the Acquire Flight Price building block in the Foreach scope of the ProcessReservation flow, this flow provides a valid response for each request it receives.

Acquire+price+info
View the XML
<flow name="acquireFlightPrice" doc:name="acquireFlightPrice">
    <vm:inbound-endpoint exchange-pattern="request-response" path="acquireFlightPrice" doc:name="acquireFlightPrice"/>
    <expression-component doc:name="acquireFlightPrice">payload.ticketPrice = Integer.valueOf(payload.flightNumber) * 2</expression-component>
</flow>

This flow sets the price for the flight. For the purposes of this example, it uses an Expression Component that simply multiplies the flight number by two, then returns this value to the ProcessReservation flow.