Integracja usług sieciowych z użyciem Apache Camel – praktyczny przykład – część 2


Integracja usług sieciowych z użyciem Apache Camel – praktyczny przykład – część 2

Zanim zaczniesz czytać ten wpis koniecznie zobacz pierwszą część – https://javaleader.pl/2020/04/14/integracja-uslug-sieciowych-z-uzyciem-apache-camel-praktyczny-przyklad-czesc-1/. W tej części zajmiemy się integracją usług z użyciem Apache Camel. Warto odświeżyć sobie wiedzę z zakresu XML, XSD, XSLT.

Tworzymy nowy projekt Apache Maven bez konkretnego archetypu – niezbędne zależności:

<properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<cxf.version>2.7.0</cxf.version>
	<spring.version>3.0.7.RELEASE</spring.version>
	<slf4j.version>1.6.2</slf4j.version>
</properties>

zależności do org.apache.camel:

<dependency>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-core</artifactId>
	<version>2.10.2</version>
</dependency>
<dependency>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-spring</artifactId>
	<version>2.10.2</version>
</dependency>
<dependency>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-stream</artifactId>
	<version>2.10.2</version>
</dependency>
<dependency>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-http</artifactId>
	<version>2.10.2</version>
</dependency>
<dependency>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-cxf</artifactId>
	<version>2.10.2</version>
</dependency>
<dependency>
	<groupId>org.apache.camel</groupId>
	<artifactId>camel-soap</artifactId>
	<version>2.3.0</version>
</dependency>

zależności do org.apache.cxf:

<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-frontend-jaxrs</artifactId>
	<version>${cxf.version}</version>
</dependency>
<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-frontend-jaxws</artifactId>
	<version>2.7.0</version>
</dependency>
<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-transports-http</artifactId>
	<version>2.7.0</version>
</dependency>
<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-transports-http-jetty</artifactId>
	<version>2.6.2</version>
</dependency>
<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-rt-bindings-soap</artifactId>
	<version>2.7.0</version>
</dependency>
<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-tools-common</artifactId>
	<version>2.7.0</version>
</dependency>

zależności do org.slf4j oraz do log4j:

<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
	<version>1.6.6</version>
</dependency>
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-log4j12</artifactId>
	<version>1.6.6</version>
</dependency>
<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.17</version>
</dependency>

pozostałe zależności:

<dependency>
	<groupId>jdom</groupId>
	<artifactId>jdom</artifactId>
	<version>1.1</version>
</dependency>
<dependency>
	<groupId>jaxen</groupId>
	<artifactId>jaxen</artifactId>
	<version>1.1.4</version>
</dependency>

Wtyczka niezbędna do wygenerowania odpowiednich klas na podstawie plików WSDL:

<plugin>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-codegen-plugin</artifactId>
	<version>2.7.0</version>
	<executions>
		<execution>
			<id>generate-sources</id>
			<phase>generate-sources</phase>
			<configuration>
				<sourceRoot>${basedir}/src/main/java</sourceRoot>
				<wsdlOptions>
					<wsdlOption>
						<wsdl>${basedir}/src/main/resources/wsdl/ReservationService.wsdl</wsdl>
					</wsdlOption>
					<wsdlOption>
						<wsdl>${basedir}/src/main/resources/wsdl/ReservationServiceAirlineA.wsdl</wsdl>
					</wsdlOption>
					<wsdlOption>
						<wsdl>${basedir}/src/main/resources/wsdl/ReservationServiceAirlineB.wsdl</wsdl>
					</wsdlOption>
				</wsdlOptions>
			</configuration>
			<goals>
				<goal>wsdl2java</goal>
			</goals>
		</execution>
	</executions>
</plugin>

Pliki *.WSDL – zamieszczamy w katalogu ./src/main/resources/wsdl:

  • ReservationService.wsdl
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:tns="http://air.sample.com/quote/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="AirLineQuote"
	targetNamespace="http://air.sample.com/quote/">
	<wsdl:types>
		<xsd:schema targetNamespace="http://air.sample.com/quote/">
 
			<xsd:element name="getQuote">
				<xsd:complexType>
					<xsd:sequence>
						<xsd:element name ="getQuote" type="tns:getQuoteRequest" />
					</xsd:sequence>
				</xsd:complexType>
			</xsd:element>
 
			<xsd:complexType name="getQuoteRequest">
				<xsd:sequence>
					<xsd:element name="source" type="xsd:string"></xsd:element>
					<xsd:element name="destination" type="xsd:string"></xsd:element>
					<xsd:element name="date" type="xsd:string"></xsd:element>
					<xsd:element name="airline" type="xsd:string"
						minOccurs="0"></xsd:element>
				</xsd:sequence>
			</xsd:complexType>
 
			<xsd:element name="getQuoteResponse">
				<xsd:complexType>
					<xsd:sequence>
						<xsd:element name = "getQuoteResponse" type="tns:getQuoteResp" />
					</xsd:sequence>
				</xsd:complexType>
			</xsd:element>
 
			<xsd:complexType name="getQuoteResp">
				<xsd:sequence>
					<xsd:element name="price" minOccurs="0" maxOccurs="unbounded"
						type="tns:Quote"></xsd:element>
				</xsd:sequence>
			</xsd:complexType>
 
			<xsd:complexType name="Quote">
				<xsd:sequence>
					<xsd:element name="airLine" type="xsd:string"
						minOccurs="0" />
					<xsd:element name="price" type="xsd:string" minOccurs="0" />
				</xsd:sequence>
			</xsd:complexType>
 
			<xsd:element name="getQuoteFault">
				<xsd:complexType>
					<xsd:sequence>
						<xsd:element name="code" type="xsd:string"></xsd:element>
						<xsd:element name="msg" type="xsd:string"></xsd:element>
					</xsd:sequence>
				</xsd:complexType>
			</xsd:element>
		</xsd:schema>
	</wsdl:types>
	<wsdl:message name="getQuoteOperationRequest">
		<wsdl:part element="tns:getQuote" name="parameters" />
	</wsdl:message>
	<wsdl:message name="getQuoteOperationResponse">
		<wsdl:part element="tns:getQuoteResponse" name="parameters" />
	</wsdl:message>
	<wsdl:message name="getQuoteOperationFault">
		<wsdl:part name="parameters" element="tns:getQuoteFault"></wsdl:part>
	</wsdl:message>
	<wsdl:portType name="AirLineQuote">
		<wsdl:operation name="getQuoteOperation">
			<wsdl:input message="tns:getQuoteOperationRequest" />
			<wsdl:output message="tns:getQuoteOperationResponse" />
			<wsdl:fault name="fault" message="tns:getQuoteOperationFault"></wsdl:fault>
		</wsdl:operation>
	</wsdl:portType>
	<wsdl:binding name="AirLineQuoteSOAP" type="tns:AirLineQuote">
		<soap:binding style="document"
			transport="http://schemas.xmlsoap.org/soap/http" />
		<wsdl:operation name="getQuoteOperation">
			<soap:operation soapAction="http://air.sample.com/quote/getQuoteOperation" />
			<wsdl:input>
				<soap:body use="literal" />
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal" />
			</wsdl:output>
		</wsdl:operation>
	</wsdl:binding>
	<wsdl:service name="AirLineQuoteService">
		<wsdl:port binding="tns:AirLineQuoteSOAP" name="AirLineQuoteSOAP">
			<soap:address location="http://localhost:8080/QuoteService" />
		</wsdl:port>
	</wsdl:service>
</wsdl:definitions>
  • ReservationServiceAirlineA.wsdl
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:tns="http://aira.sample.com/quote/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="AirLineAQuote"
	targetNamespace="http://aira.sample.com/quote/">
	<wsdl:types>
		<xsd:schema targetNamespace="http://aira.sample.com/quote/">
			<xsd:element name="getQuote">
				<xsd:complexType>
					<xsd:sequence>
						<xsd:element name="getQuote" type="tns:getQuoteRequest" />
					</xsd:sequence>
				</xsd:complexType>
			</xsd:element>
 
			<xsd:complexType name="getQuoteRequest">
				<xsd:sequence>
					<xsd:element name="source" type="xsd:string"></xsd:element>
					<xsd:element name="destination" type="xsd:string"></xsd:element>
					<xsd:element name="date" type="xsd:string"></xsd:element>
					<xsd:element name="airline" type="xsd:string"
						minOccurs="0"></xsd:element>
				</xsd:sequence>
			</xsd:complexType>
 
			<xsd:element name="getQuoteResponse">
				<xsd:complexType>
					<xsd:sequence>
						<xsd:element name="getQuoteResponse" type="tns:getQuoteResp" />
					</xsd:sequence>
				</xsd:complexType>
			</xsd:element>
 
			<xsd:complexType name="getQuoteResp">
				<xsd:sequence>
					<xsd:element name="price" minOccurs="0" 
						type="tns:Quote"></xsd:element>
				</xsd:sequence>
			</xsd:complexType>
 
			<xsd:complexType name="Quote">
				<xsd:sequence>
					<xsd:element name="airLine" type="xsd:string" fixed="AirLineA"/>
					<xsd:element name="price" type="xsd:string" />
				</xsd:sequence>
			</xsd:complexType>
 
			<xsd:element name="getQuoteFault">
				<xsd:complexType>
					<xsd:sequence>
						<xsd:element name="code" type="xsd:string"></xsd:element>
						<xsd:element name="msg" type="xsd:string"></xsd:element>
					</xsd:sequence>
				</xsd:complexType>
			</xsd:element>
		</xsd:schema>
	</wsdl:types>
	<wsdl:message name="getQuoteOperationRequest">
		<wsdl:part element="tns:getQuote" name="parameters" />
	</wsdl:message>
	<wsdl:message name="getQuoteOperationResponse">
		<wsdl:part element="tns:getQuoteResponse" name="parameters" />
	</wsdl:message>
	<wsdl:message name="getQuoteOperationFault">
		<wsdl:part name="parameters" element="tns:getQuoteFault"></wsdl:part>
	</wsdl:message>
	<wsdl:portType name="AirLineAQuote">
		<wsdl:operation name="getQuoteOperation">
			<wsdl:input message="tns:getQuoteOperationRequest" />
			<wsdl:output message="tns:getQuoteOperationResponse" />
			<wsdl:fault name="fault" message="tns:getQuoteOperationFault"></wsdl:fault>
		</wsdl:operation>
	</wsdl:portType>
	<wsdl:binding name="AirLineQuoteSOAP" type="tns:AirLineAQuote">
		<soap:binding style="document"
			transport="http://schemas.xmlsoap.org/soap/http" />
		<wsdl:operation name="getQuoteOperation">
			<soap:operation soapAction="http://aira.sample.com/quote/getQuoteOperation" />
			<wsdl:input>
				<soap:body use="literal" />
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal" />
			</wsdl:output>
		</wsdl:operation>
	</wsdl:binding>
	<wsdl:service name="AirLineAQuoteService">
		<wsdl:port binding="tns:AirLineQuoteSOAP" name="AirLineQuoteSOAP">
			<soap:address location="http://localhost:8080/QuoteOperation" />
		</wsdl:port>
	</wsdl:service>
</wsdl:definitions>
  • ReservationServiceAirlineB.wsdl
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
	xmlns:tns="http://airb.sample.com/quote/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="AirLineBQuote"
	targetNamespace="http://airb.sample.com/quote/">
	<wsdl:types>
		<xsd:schema targetNamespace="http://airb.sample.com/quote/">
			<xsd:element name="getQuote">
				<xsd:complexType>
					<xsd:sequence>
						<xsd:element name="getQuote" type="tns:getQuoteRequest" />
					</xsd:sequence>
				</xsd:complexType>
			</xsd:element>
 
			<xsd:complexType name="getQuoteRequest">
				<xsd:sequence>
					<xsd:element name="source" type="xsd:string"></xsd:element>
					<xsd:element name="destination" type="xsd:string"></xsd:element>
					<xsd:element name="date" type="xsd:string"></xsd:element>
					<xsd:element name="airline" type="xsd:string"
						minOccurs="0"></xsd:element>
				</xsd:sequence>
			</xsd:complexType>
 
			<xsd:element name="getQuoteResponse">
				<xsd:complexType>
					<xsd:sequence>
						<xsd:element name="getQuoteResponse" type="tns:getQuoteResp" />
					</xsd:sequence>
				</xsd:complexType>
			</xsd:element>
 
			<xsd:complexType name="getQuoteResp">
				<xsd:sequence>
					<xsd:element name="price" minOccurs="0" type="tns:Quote"></xsd:element>
				</xsd:sequence>
			</xsd:complexType>
 
			<xsd:complexType name="Quote">
				<xsd:sequence>
					<xsd:element name="airLine" type="xsd:string" fixed="AirLineB"/>
					<xsd:element name="price" type="xsd:string" />
				</xsd:sequence>
			</xsd:complexType>
 
			<xsd:element name="getQuoteFault">
				<xsd:complexType>
					<xsd:sequence>
						<xsd:element name="code" type="xsd:string"></xsd:element>
						<xsd:element name="msg" type="xsd:string"></xsd:element>
					</xsd:sequence>
				</xsd:complexType>
			</xsd:element>
		</xsd:schema>
	</wsdl:types>
	<wsdl:message name="getQuoteOperationRequest">
		<wsdl:part element="tns:getQuote" name="parameters" />
	</wsdl:message>
	<wsdl:message name="getQuoteOperationResponse">
		<wsdl:part element="tns:getQuoteResponse" name="parameters" />
	</wsdl:message>
	<wsdl:message name="getQuoteOperationFault">
		<wsdl:part name="parameters" element="tns:getQuoteFault"></wsdl:part>
	</wsdl:message>
	<wsdl:portType name="AirLineBQuote">
		<wsdl:operation name="getQuoteOperation">
			<wsdl:input message="tns:getQuoteOperationRequest" />
			<wsdl:output message="tns:getQuoteOperationResponse" />
			<wsdl:fault name="fault" message="tns:getQuoteOperationFault"></wsdl:fault>
		</wsdl:operation>
	</wsdl:portType>
	<wsdl:binding name="AirLineQuoteSOAP" type="tns:AirLineBQuote">
		<soap:binding style="document"
			transport="http://schemas.xmlsoap.org/soap/http" />
		<wsdl:operation name="getQuoteOperation">
			<soap:operation soapAction="http://airb.sample.com/quote/getQuoteOperation" />
			<wsdl:input>
				<soap:body use="literal" />
			</wsdl:input>
			<wsdl:output>
				<soap:body use="literal" />
			</wsdl:output>
		</wsdl:operation>
	</wsdl:binding>
	<wsdl:service name="AirLineBQuoteService">
		<wsdl:port binding="tns:AirLineQuoteSOAP" name="AirLineQuoteSOAP">
			<soap:address location="http://localhost:8080/QuoteOperation" />
		</wsdl:port>
	</wsdl:service>
</wsdl:definitions>

Generujemy narzędziem wsdl2java niezbędne klasy na podstawie plików WSDL:

mvn generate:sources

Apache Camel Context

Plik ./webapp/WEB-INF/camel-context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:broker="http://activemq.apache.org/schema/core"
	xmlns:cxf="http://camel.apache.org/schema/cxf" xmlns:camel="http://camel.apache.org/schema/spring"
	xsi:schemaLocation="
       http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://activemq.apache.org/schema/core 
       http://activemq.apache.org/schema/core/activemq-core.xsd
       http://camel.apache.org/schema/spring 
       http://camel.apache.org/schema/spring/camel-spring.xsd
	   http://camel.apache.org/schema/cxf 
	   http://camel.apache.org/schema/cxf/camel-cxf.xsd">
 
</beans>

za pomocą cxf-component defniujemy Camel CXF Endpoint dla zdefiniowanych usług:

<!-- Endpoint of MockService for Airline A -->
<cxf:cxfEndpoint id="airlineAEndpoint"
	address="http://localhost:8080/cxf-sample-airlineA-1.0/services/AirLineAQuote" serviceClass="com.sample.aira.quote.AirLineAQuote"
	wsdlURL="wsdl/ReservationServiceAirlineA.wsdl">
	<cxf:properties>
		<entry key="dataFormat" value="PAYLOAD" />
	</cxf:properties>
</cxf:cxfEndpoint>
 
<!-- Endpoint of MockService for Airline B -->
<cxf:cxfEndpoint id="airlineBEndpoint"
	address="http://localhost:8080/cxf-sample-airlineB-1.0/services/AirLineBQuote" serviceClass="com.sample.airb.quote.AirLineBQuote"
	wsdlURL="wsdl/ReservationServiceAirlineB.wsdl">
	<cxf:properties>
		<entry key="dataFormat" value="PAYLOAD" />
	</cxf:properties>
</cxf:cxfEndpoint>
 
<!-- Endpoint of Reservation Service -->
<cxf:cxfEndpoint id="reservationService" address="/QuoteService"
	serviceClass="com.sample.air.quote.AirLineQuote" wsdlURL="wsdl/ReservationService.wsdl">
	<cxf:properties>
		<entry key="dataFormat" value="PAYLOAD" />
		<entry key="schema-validation-enabled" value="true" />
	</cxf:properties>
</cxf:cxfEndpoint>

domyślnie camel-cxf korzysta z dataFormat=POJO. Potrzebujemy XMLa aby przekazywać go miedzy wywołaniami w konfiguracji Apache Camel. Aby to zrealizować należy dodać:

<entry key="dataFormat" value="PAYLOAD" />

Jeśli zależy nam na walidacji requesta na podstawie schematu zdefiniowanege w danym pliku WSDL to należy dodać:

<entry key="schema-validation-enabled" value="true" />

W tagach:

<camel:camelContext id="camelContext">...</camel:camelContext>

definiujemy routing:

<camel:route id="airLineQuoteRoute">
 
	<camel:from uri="cxf:bean:reservationService" />
	<camel:to uri="bean:requestValidator" />
 
	<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:onException>
</camel:route>

Przychodzi request który jest przetwarzany za pomoca beana requestValidator:

<bean id="requestValidator" class="com.sample.processor.RequestValidator" />

Klasa RequestValidator sprawdza czy wysłany request posiada prawidłowe dane i ustawia dodatkowo nagłówek header:

public class RequestValidator implements Processor {
 
	private static final Logger LOGGER = Logger
			.getLogger(RequestValidator.class);
 
	public static final String XPATH_ORIGIN      = "//*[local-name() = 'source']";
	public static final String XPATH_DESTINATION = "//*[local-name() = 'destination']";
	public static final String XPATH_DATE        = "//*[local-name() = 'date']";
	public static final String XPATH_AIRLINES    = "//*[local-name() = 'airline']";
 
	@Override
	public void process(Exchange exchange) throws Exception {
 
		String requestXML = exchange.getIn().getBody(String.class);
 
		XPathReaderUtil xpathReader = new XPathReaderUtil(requestXML);
 
		getNodeValue(exchange, xpathReader, XPATH_ORIGIN,      "source");
		getNodeValue(exchange, xpathReader, XPATH_DESTINATION, "destination");
		getNodeValue(exchange, xpathReader, XPATH_DATE,        "date");
		getNodeValue(exchange, xpathReader, XPATH_AIRLINES,    "airline");
	}
 
	private void getNodeValue(Exchange exchange, XPathReaderUtil xpathReader,
			String xpathName, String nodeName) throws Exception {
 
		String nodeValue = null;
 
		try {
			nodeValue = xpathReader.getNodeValue(xpathName);
 
			if ("".equals(nodeValue)) {
				throw (new Exception());
			}
			if ("airline".equals(nodeName)) {
				setAirLine(exchange, nodeValue);
			}
 
		} catch (Exception exception) {
			if ("source".equals(nodeName)) {
				throw (new Exception("Please provide Source - Starting place!"));
			} else if ("destination".equals(nodeName)) {
				throw (new Exception("Please provide Destination!"));
			} else if ("date".equals(nodeName)) {
				throw (new Exception("Please provide Travel date"));
			} else if ("airline".equals(nodeName)) {
				setAirLine(exchange, "All");
			}
		}
	}
 
	private void setAirLine(Exchange exchange, String airLines)
			throws Exception {
		if (airLines != null) {
			if ("AirLineA".equals(airLines) || "AirLineB".equals(airLines)
					|| "All".equals(airLines)) {
				exchange.getIn().setHeader("airLines", airLines);
			} else {
				exchange.getIn().setHeader("airLines", "NONE");
				throw (new Exception("Please provide valid Airline name or All"));
			}
		}
	}
}

Przykładowo, jeśli zostanie wysłany request – bez podanego źródła:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:quot="http://air.sample.com/quote/">
   <soapenv:Header/>
   <soapenv:Body>
      <quot:getQuote>
         <getQuote>
            <source></source>
            <destination>CHN</destination>
            <date>25/09/2013</date>
            <airline>All</airline>
         </getQuote>
      </quot:getQuote>
   </soapenv:Body>
</soapenv:Envelope>

W odpowiedzi otrzymamy:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <soap:Fault>
         <faultcode>soap:Server</faultcode>
         <faultstring>Please provide Source - Starting place!</faultstring>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>

na podstawie wartości ustawionego nagłówka przekazujemy sterowanie dalej, do kolejnych faz routingu.

AirLineA -> direct:getQuoteA

AirLineB -> direct:getQuoteB

All -> direct:getQuoteMulticast

Routing dla serwisu AirLineA:

Definiujemy kolejny routing:

<camel:route id="getQuoteAirLineARoute">
	<camel:from uri="direct:getQuoteA" />
 
	<camel:to uri="bean:getXMLStreamSource" />
 
	<camel:to uri="xslt://airlineA_req_transform.xsl" />
 
	<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>
 
	<camel:to uri="cxf:bean:airlineAEndpoint" />
 
	<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:log message="Route getQuoteAirLineARoute Ends .... " />
</camel:route>

i beana:

<bean id="getXMLStreamSource" class="com.sample.processor.PrepareXMLStreamProcessor" />
public class PrepareXMLStreamProcessor implements Processor {
 
	@Override
	public void process(Exchange exchange) throws Exception {
 
		String requestXML = exchange.getIn().getBody(String.class);
 
		StringReader stringReader = new StringReader(requestXML);
 
		StreamSource streamSource = new StreamSource(stringReader);
 
		exchange.getIn().setBody(streamSource);
 
	}
}

oraz beana do obsługi błędów:

<bean id="exceptionHandler" class="com.sample.processor.ExceptionHandler" />
public class ExceptionHandler implements Processor {
 
	@Override
	public void process(Exchange exchange) throws Exception {
 
		Exception exception = (Exception) exchange.getProperty(Exchange.EXCEPTION_CAUGHT);
 
		if (exception instanceof org.apache.cxf.transport.http.HTTPException) {
			throw (new Exception("TargetService Not Available!"));
		} if (exception instanceof org.apache.cxf.interceptor.Fault){
			throw (new Exception("Unable to communicate with the Service!"));
		} else {
			throw (new Exception("unknown exception occured!"));
		}
	}
}

żadanie zostanie przeprocesowane i poddane transformacji XSLT:

Plik \src\main\resources\airlineA_req_transform.xsl:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:res="http://air.sample.com/quote/"
	xmlns:quot="http://aira.sample.com/quote/">
 
	<xsl:output method="xml" indent="yes" encoding="UTF-8" />
 
	<xsl:template match="text()|@*" />
 
	<xsl:template match="//res:getQuote//getQuote">
		<xsl:element name="quot:getQuote">
			<xsl:element name="getQuote">
				<xsl:element name="source">
					<xsl:value-of select="source"></xsl:value-of>
				</xsl:element>
				<xsl:element name="destination">
					<xsl:value-of select="destination"></xsl:value-of>
				</xsl:element>
				<xsl:element name="date">
					<xsl:value-of select="date"></xsl:value-of>
				</xsl:element>
			</xsl:element>
		</xsl:element>
	</xsl:template>
</xsl:stylesheet>

dodajemy nagłówki które są niezbędne w komunikacji SOAP:

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

żadanie wysyłane jest do endpointa:

airlineAEndpoint

następnie odpowiedź jest poddawana transformacji XSLT:

Plik src\main\resources\airline_resp_transform.xsl:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:resa="http://aira.sample.com/quote/"
	xmlns:resb="http://airb.sample.com/quote/" xmlns:quot="http://air.sample.com/quote/">
 
	<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"
		encoding="UTF-8" />
 
	<xsl:template match="text()|@*" />
 
	<xsl:template match="//resa:getQuoteResponse//getQuoteResponse//price">
		<xsl:element name="quot:getQuoteResponse">
			<xsl:element name="getQuoteResponse">
				<xsl:element name="price">
					<xsl:element name="airLine">
						<xsl:value-of select="//price//airLine"></xsl:value-of>
					</xsl:element>
					<xsl:element name="price">
						<xsl:value-of select="//price//price"></xsl:value-of>
					</xsl:element>
				</xsl:element>
			</xsl:element>
		</xsl:element>
	</xsl:template>
 
	<xsl:template match="//resb:getQuoteResponse//getQuoteResponse//price">
		<xsl:element name="quot:getQuoteResponse">
			<xsl:element name="getQuoteResponse">
				<xsl:element name="price">
					<xsl:element name="airLine">
						<xsl:value-of select="//price//airLine"></xsl:value-of>
					</xsl:element>
					<xsl:element name="price">
						<xsl:value-of select="//price//price"></xsl:value-of>
					</xsl:element>
				</xsl:element>
			</xsl:element>
		</xsl:element>
	</xsl:template>
</xsl:stylesheet>

Routing dla serwisu AirLineB:

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

analogicznie jak poprzednio:

Plik \src\main\resources\airlineB_req_transform.xsl:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:res="http://air.sample.com/quote/"
	xmlns:quot="http://airb.sample.com/quote/">
 
	<xsl:output method="xml" indent="yes" encoding="UTF-8" />
 
	<xsl:template match="text()|@*" />
 
	<xsl:template match="//res:getQuote//getQuote">
		<xsl:element name="quot:getQuote">
			<xsl:element name="getQuote">
				<xsl:element name="source">
					<xsl:value-of select="source"></xsl:value-of>
				</xsl:element>
				<xsl:element name="destination">
					<xsl:value-of select="destination"></xsl:value-of>
				</xsl:element>
				<xsl:element name="date">
					<xsl:value-of select="date"></xsl:value-of>
				</xsl:element>
			</xsl:element>
		</xsl:element>
	</xsl:template>
</xsl:stylesheet>

Routing dla przetwarzania równlogełego:

Jeśli klient chciałby dokonać agregacji wyników z dwóch różnych serwisów czyli podał w zapytaniu:

<airline>All</airline>

należy pobrać dane z dwóch serwisów (najlepiej równolegle) i dokonać agregacji wyników. W tym celu definiujemy kolejny routing:

<camel:route id="getQuoteMultiCastRoute">
	<camel:from uri="direct:getQuoteMulticast" />
 
	<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:to uri="bean:exceptionHandler" />
	</camel:onException>
 
</camel:route>

definicja beana – aggregateData:

<bean id="aggregateData" class="com.sample.aggregate.AggregateResponse" />
public class AggregateResponse implements AggregationStrategy {
 
	@Override
	public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
 
		Exception exception = (Exception) newExchange.getProperty(Exchange.EXCEPTION_CAUGHT);
		if (exception == null) {
			if (oldExchange == null && newExchange != null) {
				return newExchange;
 
			} else if (oldExchange != null && newExchange != null) {
				newExchange.getIn().setBody(
						oldExchange.getIn().getBody(String.class)
								+ newExchange.getIn().getBody(String.class));
			}
			return newExchange;
		}
		return oldExchange;
	}
}

definicja beana – processResponse:

<bean id="processResponse" class="com.sample.processor.ProcessResponse" />
public class ProcessResponse implements Processor {
	@Override
	public void process(Exchange exchange) throws Exception {
 
		StringBuffer multicastResponseXML = new StringBuffer("<aggregatedResponse>\n");
 
		multicastResponseXML.append(exchange.getIn().getBody(String.class));
 
		multicastResponseXML.append("\n </aggregatedResponse>");
 
		StringReader stringReader = new StringReader(multicastResponseXML.toString());
 
		StreamSource streamSource = new StreamSource(stringReader);
 
		exchange.getIn().setBody(streamSource);
 
	}
}

zagregowana odpowiedź poddawana jest transformacie XSLT:

Plik \src\main\resources\process_response.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:res="http://air.sample.com/quote/">
 
	<xsl:output method="xml" indent="yes" encoding="UTF-8" />
 
	<xsl:template match="text()|@*" />
 
	<xsl:template match="//aggregatedResponse">
		<xsl:element name="res:getQuoteResponse">
			<xsl:element name="getQuoteResponse">
				<xsl:for-each select="//res:getQuoteResponse">
					<xsl:element name="price">
						<xsl:element name="airLine">
							<xsl:value-of select="getQuoteResponse//price//airLine"></xsl:value-of>
						</xsl:element>
						<xsl:element name="price">
							<xsl:value-of select="getQuoteResponse//price//price"></xsl:value-of>
						</xsl:element>
					</xsl:element>
				</xsl:for-each>
			</xsl:element>
		</xsl:element>
	</xsl:template>
</xsl:stylesheet>

Przykładowy request:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:quot="http://air.sample.com/quote/">
   <soapenv:Header/>
   <soapenv:Body>
      <quot:getQuote>
         <getQuote>
            <source>HYD</source>
            <destination>CHN</destination>
            <date>25/09/2013</date>
            <airline>All</airline>
         </getQuote>
      </quot:getQuote>
   </soapenv:Body>
</soapenv:Envelope>

zagregowana odpowiedź:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <res:getQuoteResponse xmlns:res="http://air.sample.com/quote/">
         <getQuoteResponse>
            <price>
               <airLine>INDIGO</airLine>
               <price>4000</price>
            </price>
            <price>
               <airLine>SPICEJET</airLine>
               <price>4500</price>
            </price>
         </getQuoteResponse>
      </res:getQuoteResponse>
   </soap:Body>
</soap:Envelope>

W projekcie użyliśmy następujących komponentów:

  • Camel CXF Endpoint,
  • Camel Bean,
  • Camel Direct,
  • Camel XSLT,
  • Camel Multicast EIP,
  • Camel Aggregator EIP.

W ostatniej części projektu zobaczysz w jaki sposób za pomoca modułu Camel WireTap EIP dokonać logowania akcji występujących w projekcie.


Leave a comment

Your email address will not be published.


*