Java Tip: Write an SOA integration layer with Apache Camel

Web services integration with Spring and Apache Camel

1 2 3 4 5 6 Page 4
Page 4 of 6

 Step 3: Configure the Camel route(s) to use multicasting

Apache Camel allows us to use the Multicast pattern to send a single message to more than one endpoint. Here, we use Multicast to send the request from our portal ReservationService to the airlineARoute and airlineBRoute. Setting the parallelProcessing=true attribute means that we can use multicasting in parallel, thereby optimizing the response time. Both routes in turn transform the received request into the destination-request format and invoke the Reservation Services from Airline A and Airline B in parallel.

As shown in Listing 3, we use the processor in Apache Camel to set the header of the exchange and to specify a Consumer's choice of airline (AirlineA, AirlineB, or All). This allows us to configure the choice when construct in the Camel route to direct the message to the user's choice of airline, with easy XML configuration. Camel processor is a Java class that implements Camel's Processor interface and is used to transform the message (exchange) flowing through the Processor or add important information in the header, which can be used in the Camel route to branch out the flow, based on business inputs.

Because we'll receive a response from more than one web service, we also need to aggregate the responses before we send them back to the original service. For this we use Camel's Aggregator pattern implementation. Camel's support for aggregation is provided by the strategyRef attribute of the Multicast tag, which is shown in Listing 3 below.

The strategyRef attribute points to an implementation of the AggregationStrategy class, which you can view in the article source. The overridden aggregator method gets responses from airlineARoute and airlineBRoute. The responses can be aggregated in this method and set in the Camel Exchange.

Listing 3. Camel route to conditionally invoke the target web services

<camel:route id="airLineQuoteRoute">
            // Web service Consumer endpoint representing the ReservationService exposed to outer world
            <camel:from uri="cxf:bean:reservationService" />

            //Use the Camel wireTap component to take a snapshot of the message 
            <wireTap uri="direct:logInfo"/>

            //Camel processor that does business validations, extracts the information from incoming XML and sets the Exchange header
            <camel:to uri="bean:requestValidator" />
            
            //Conditional Route Flow
            <camel:choice>
                <camel:when>
                    <camel:simple>${header.airLines} == 'AirLineA'</camel:simple>
                    <camel:to uri="direct:getQuoteA"></camel:to>
                </camel:when>
                <camel:when>
                    <camel:simple>${header.airLines} == 'AirLineB'</camel:simple>
                    <camel:to uri="direct:getQuoteB"></camel:to>
                </camel:when>
                <camel:when>
                    <camel:simple>${header.airLines} == 'All'</camel:simple>
                    <camel:to uri="direct:getQuoteMulticast"></camel:to>
                </camel:when>
            </camel:choice>

            //To handle exceptions occurred
            <camel:onException>
                <camel:exception>java.lang.Exception</camel:exception>
                <camel:log message="Exception occurred ${exception.message}" />
            </camel:onException>
        </camel:route>
        
        // Routing logic using the Camel Multicast component
        <camel:route id="getQuoteMultiCastRoute">
            <camel:from uri="direct:getQuoteMulticast" />
            
            // Mulitcast call for both Airline A and Airline B 
            <camel:multicast strategyRef="aggregateData" parallelProcessing="true">
                <camel:to uri="direct:getQuoteA" />
                <camel:to uri="direct:getQuoteB" />
            </camel:multicast>

            <camel:to uri="bean:processResponse" />

            <camel:to uri="xslt://process_response.xsl" />
            <camel:onException>
                <camel:exception>java.lang.Exception</camel:exception>
                <camel:log message="Exception occurred ${exception.message}" />
                <camel:to uri="bean:exceptionHandler" />
            </camel:onException>
        </camel:route>


 Step 4: Configure the Camel route for message transformation

In Step 3 we used multicasting to invoke the two routes for the Airline A and Airline B ReservationServices. Each of these routes transforms the incoming XML to the target format, invokes our two airline reservation services, and transforms the response XML. Apache Camel allows the use of XSLT sheets via the Camel XSLT component. We use XSLT sheets to transform the request-and-response message from the source format (that is, the portal ReservationService) to the target formats required by the airline ReservationServices. As stated in Step 2, the ReservationService endpoints use a PAYLOAD dataFormat, which allows us to process and transform the raw XML using XSLT sheets.

Listing 4. A Camel route for message transformation and invoking target web services

//  Routing logic for Airline A Web Service
        <camel:route id="getQuoteAirLineARoute">
            <camel:from uri="direct:getQuoteA" />
            // To create "StreamSource" (Stream of XML) of the request, and send the message to Camel XSLT component
            <camel:to uri="bean:getXMLStreamSource" />
            
            //loads pre-defined XSLT sheet
            <camel:to uri="xslt://airlineA_req_transform.xsl" />
            
            //set the necessary headers to set the namespace and operation to be invoked on the target Web Service 
            <camel:setHeader headerName="operationNamespace">
                <camel:simple>http://aira.sample.com/quote/</camel:simple>
            </camel:setHeader>
            
            <camel:setHeader headerName="SOAPAction">
                <camel:simple>http://aira.sample.com/quote/getQuoteOperation</camel:simple>
            </camel:setHeader>
            
            //Use wiretap component to take a snapshot of the request message (optional)
            <wireTap uri="direct:logInfo"/>
            // Invoke Airline A Web Service with the Transformed request
            <camel:to uri="cxf:bean:airlineAEndpoint" />

            //Use wiretap component to take a snapshot of the response message (optional)
            <wireTap uri="direct:logInfo"/>
            //do response transformation
            <camel:to uri="xslt://airline_resp_transform.xsl" />

            //Handle  Exceptions 
            <camel:onException>
                <camel:exception>java.lang.Exception</camel:exception>
                <camel:to uri="bean:exceptionHandler" />
            </camel:onException>
        </camel:route>
        
        //do similarly for Airline B Route
        <camel:route id="getQuoteAirLineBRoute">
            <camel:from uri="direct:getQuoteB" />

            <camel:to uri="bean:getXMLStreamSource" />

            <camel:to uri="xslt://airlineB_req_transform.xsl" />

            <camel:setHeader headerName="operationNamespace">
                <camel:simple>http://airb.sample.com/quote/</camel:simple>
            </camel:setHeader>
            
            <camel:setHeader headerName="SOAPAction">
                <camel:simple>http://airb.sample.com/quote/getQuoteOperation</camel:simple>
            </camel:setHeader>

            <camel:to uri="cxf:bean:airlineBEndpoint" />

            <camel:to uri="xslt://airline_resp_transform.xsl" />

            <camel:onException>
                <camel:exception>java.lang.Exception</camel:exception>
                <camel:to uri="bean:exceptionHandler" />
            </camel:onException>
        </camel:route>


1 2 3 4 5 6 Page 4
Page 4 of 6