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

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

Z tego wpisu dowiesz się na czym w praktyce polega integracja usług sieciowych z użyciem Apache Camel. Jest to wpis oparty na artykule – https://www.javaworld.com/article/2078883/java-tip-write-an-soa-integration-layer-with-apache-camel.html. Ukłony dla autorów! 😉 Integracja polegać będzie na spięciu ze sobą 3 serwisów. Pierwszy serwis na podstawie danych z dwóch pozostałych agreguje wyniki dotyczące cen biletów linii lotniczych. Drugi i trzeci serwis odpowiada za dostarczanie informacji na temat biletów należących do konkretnej linii lotniczej. Tego typu agregatory danych pozwalają na wyszukanie i zaproponowanie klientowi najlepszej oferty cenowej! Usługi sieciowe będą oparte o architekturę SOA i protokół SOAP.

Serwisy które zostaną utworzone:

1. ReservationService – serwis agregujące dane biletów z dwóch linii lotniczych,

2. ResServiceAirlineA – serwis udostępniający bilety linii lotniczych A,

3. ResServiceAirlineB – serwis udostępniający bilety linii lotniczych B.

Implementacja serwisu – ResServiceAirlineA:

Tworzymy nowy projekt Apache Maven bez konkretnego archetypu:

  • Plik WSDL opisujący usługi sieciowe – plik ten zamieszczamy w katalogu projektu:
<?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>
  • Plik pom.xml – niezbędne zależności:
<dependencies>
    <dependency>
        <groupId>org.springframework.ws</groupId>
        <artifactId>spring-ws-core</artifactId>
        <version>2.0.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-oxm</artifactId>
        <version>3.1.0.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</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>
</dependencies>

Plugin – maven-war-plugin:

Plugin ten podczas budowania wynikowej paczki dodaje niezbędne pliki do zdefiniowanych katalogów:

<plugin>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.1</version>
    <configuration>
        <webXml>src/main/resources/web.xml</webXml>
        <webResources>
            <resource>
                <directory>${basedir}/wsdl</directory>
                <targetPath>WEB-INF</targetPath>
                <includes>
                    <include>*.wsdl</include>
                    <include>*.xsd</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <targetPath>WEB-INF</targetPath>
                <includes>
                    <include>**.*</include>
                </includes>
            </resource>
        </webResources>
    </configuration>
</plugin>
  • Plugin – org.apache.cxf:

Plugin ten pozwala na podstawie pliku WSDL wygenerować niezbędne klasy usług w procesie budowania aplikacji dla fazy generate-sources :

<plugin>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-codegen-plugin</artifactId>
    <version>2.2.3</version>
    <executions>
        <execution>
            <id>generate-sources</id>
            <phase>generate-sources</phase>
            <configuration>
                <sourceRoot>${project.basedir}/src/main/java/ws</sourceRoot>
                <wsdlOptions>
                    <wsdlOption>
                        <wsdl>${project.basedir}/wsdl/ReservationServiceAirlineA.wsdl</wsdl>
                        <serviceName>AirLineAQuoteService</serviceName>
                        <extraargs>
                            <extraarg>-verbose</extraarg>
                        </extraargs>
                    </wsdlOption>
                </wsdlOptions>
            </configuration>
        <goals>
            <goal>wsdl2java</goal>
        </goals>
        </execution>
    </executions>
</plugin>

uruchamiamy plugin:

mvn generate-sources

w wyniku otrzymujemy wygenerowane na podstawie pliku WSDL niezbędne klasy javy.

  • Implementacja serwisu zwracającego przykładowe dane:
@WebService(targetNamespace = "http://aira.sample.com/quote/", name = "AirLineAQuoteService", serviceName="AirLineAQuoteService", portName="AirLineQuoteSOAP")
public class ReservationServiceImpl implements AirLineAQuote{
 
	 public GetQuoteResponse getQuoteOperation(GetQuote parameters){
		 GetQuoteResponse getQuoteResponse = new GetQuoteResponse();
		 GetQuoteResp getQuoteResp = new GetQuoteResp();
		 Quote quote = new Quote();
		 quote.setAirLine("INDIGO");
		 quote.setPrice("4000");
		 getQuoteResp.setPrice(quote);
		 getQuoteResponse.setGetQuoteResponse(getQuoteResp);
		 return getQuoteResponse;
	 }
}
  • Definicja usługi (wyswtawienie usługi na świat):
<?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:jaxws="http://cxf.apache.org/jaxws"
      xmlns:cxf="http://cxf.apache.org/core"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
 
    <jaxws:endpoint xmlns:tns="http://aira.sample.com/quote/"
		id="reservationService"
		address="/AirLineAQuote"
		serviceName="tns:AirLineAQuoteService"
		endpointName="tns:AirLineQuoteSOAP"
		implementor="#reservationServiceImpl">
    </jaxws:endpoint>
 
   <bean id="reservationServiceImpl" class="com.sample.impl.ReservationServiceImpl"/>
 
</beans>
  • Definicja implementacji dla servletu oraz mapowanie adresu:
<?xml version="1.0" encoding="UTF-8"?>
 
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
         http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <display-name>cxf</display-name>
 
    <servlet>
        <servlet-name>cxf</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>cxf</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>
 
    <session-config>
        <session-timeout>60</session-timeout>
    </session-config>
 
</web-app>

udało się z powodzeniem wystawić usługę która dostarcza podstawowych informacji na temat biletów dla linii lotniczych ResServiceAirlineA.

Testy za pomocą narzędzia SoapUI:

Generujemy wynikową paczkę *.war i wgrywamy sa Apache Tomcat. Po tej operacji pod adresem:

http://localhost:8080/cxf-sample-airlineA-1.0/services/AirLineAQuote?wsdl

znajdziesz plik WSDL który należy wykorzystać przy testach z użyciem narzędzia SoapUI:

  • Przykładowy request:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:quot="http://aira.sample.com/quote/">
   <soapenv:Header/>
   <soapenv:Body>
      <quot:getQuote>
         <getQuote>
            <source>CHN</source>
            <destination>HYD</destination>
            <date>25/09/2013</date>
            <!--Optional:-->
            <airline>All</airline>
         </getQuote>
      </quot:getQuote>
   </soapenv:Body>
</soapenv:Envelope>
  • odpowiedź (zaszyta w kodzie na sztywno, bez pobierania danych z bazy danych):
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <ns2:getQuoteResponse xmlns:ns2="http://aira.sample.com/quote/">
         <getQuoteResponse>
            <price>
               <airLine>INDIGO</airLine>
               <price>4000</price>
            </price>
         </getQuoteResponse>
      </ns2:getQuoteResponse>
   </soap:Body>
</soap:Envelope>

Implementacja serwisu – ResServiceAirlineB:

Analogicznie postępujemy dla drugiej aplikacji zmieniając nazwę projektu na ResServiceAirlineB, przykładowe dane które są zwracane z usługi zmieniamy w taki sposób aby dotyczyły one drugiej linii lotniczej:

public GetQuoteResponse getQuoteOperation(GetQuote parameters){
	 GetQuoteResponse getQuoteResponse = new GetQuoteResponse();
	 GetQuoteResp getQuoteResp = new GetQuoteResp();
	 Quote quote = new Quote();
	 quote.setAirLine("SPICEJET");
	 quote.setPrice("4500");
	 getQuoteResp.setPrice(quote);
	 getQuoteResponse.setGetQuoteResponse(getQuoteResp);
	 return getQuoteResponse;
}

analogicznie wgrywamy aplikację na Apache Tomcat i również testujemy za pomocą narzędzia SoapUI:

http://localhost:8080/cxf-sample-airlineB-1.0/services/AirLineBQuote?wsdl
  • Przykładowy request:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:quot="http://airb.sample.com/quote/">
   <soapenv:Header/>
   <soapenv:Body>
      <quot:getQuote>
         <getQuote>
            <source>HYD</source>
            <destination>CHN</destination>
            <date>25/09/2013</date>
            <!--Optional:-->
            <airline>All</airline>
         </getQuote>
      </quot:getQuote>
   </soapenv:Body>
</soapenv:Envelope>
  • odpowiedź (zaszyta w kodzie na sztywno, bez pobierania danych z bazy danych):
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <ns2:getQuoteResponse xmlns:ns2="http://airb.sample.com/quote/">
         <getQuoteResponse>
            <price>
               <airLine>SPICEJET</airLine>
               <price>4500</price>
            </price>
         </getQuoteResponse>
      </ns2:getQuoteResponse>
   </soap:Body>
</soap:Envelope>

W tej części udało się zaimplementować dwie usługi:

  • ResServiceAirlineA
  • ResServiceAirlineB

Każda z nich zwraca z użyciem protokołu SOAP na podstawie przykładowego requestu, przykładową, spreparowaną odpowiedź. Nie korzystamy z bazy danych. W kolejnej części zobaczysz w jaki sposób zintegrować te dwa serwisy za pomocą świetnego narzędzia integracji Apache Camel.

Leave a comment

Your email address will not be published.


*