Diese Website nutzt Cookies, um bestmögliche Funktionalität bieten zu können. Weitere Informationen siehe Datenschutzerklärung.

Apache Camel: Anbindung eines externen Web Services mit Apache CXF

Apache Camel: Anbindung eines externen Web Services mit Apache CXF

Apache Camel ist ein Open Source Framework auf Java-Basis, das die einfache Verwendung bewährter Enterprise Integration Patterns ermöglicht, wie sie unter www.enterpriseintegrationpatterns.com beschrieben sind. Damit ist es möglich, den Nachrichtenaustausch zwischen verschiedenen Modulen und Systemen, die unterschiedliche Datenformate und Protokolle verwenden, flexibel und erweiterbar zu steuern.

Nachrichten werden zwischen Systemen und Modulen über Endpoints ausgetauscht, Routen definieren dabei die Wege von Nachrichten und können abhängig von Bedingungen sein, die z.B. anhand des Inhalts einer Nachricht überprüft werden. Auf einem Weg kann eine Nachricht in beliebige andere Formate umgewandelt werden.

Apache Camel unterstützt von Haus aus diverse, gängige Datenformate wie XML und Protokolle, mit denen Endpoints angesprochen werden können wie FTP und HTTP. Endpoints können dabei auch die Eingangskanäle von Web-Services sein. Eine Apache Camel Anwendung kann sowohl für externe Clients nach Außen Web Services bereitstellen als selbst externe Web Services ansprechen.

Apache Camel, Apache CXF und Oracle WebLogic – ein Beispiel

Ein Client, der innerhalb einer Apache Camel Anwendung mit einem externen Web-Service kommuniziert, kann mit Apache CXF realisiert werden, einem weit verbreiteten Open Source Framework für Web Services. Bei der Kombination mit Apache Camel müssen für Apache CXF Anwendungen einige Besonderheiten beachtet werden, die oft auch spezifisch sind für den Application Server, unter dem die Anwendung laufen soll.

Im Nachfolgenden wird anhand eines einfachen Beispiels wsConsumer skizzenhaft gezeigt, wie mit Apache CXF aus einer Apache Camel Anwendung heraus ein SOAP Web Service über SSL/TLS kontaktiert werden kann. Die Apache Camel Anwendung läuft dabei in einer Oracle WebLogic Server Umgebung. Ausgangspunkt ist die Beschreibung des externen Web Services in einer WSDL-Datei. Die Konfiguration für Apache Camel erfolgt mit Spring in einer XML-Konfigurationsdatei (applicationContext.xml).

Dabei werden verwendet:

  • Java 1.7
  • Oracle WebLogic Server 10.3.3.0
  • Maven 3.3.9
  • Apache Camel 2.8.0
  • Spring Framework 3.0.5.RELEASE

Für aktuellere Versionen erfolgt das Vorgehen weitestgehend analog.

Das Deployment der Anwendung erfolgt als .ear-Datei.

Maven-Projektstruktur

Die Projektstruktur berücksichtigt die Verwendung von Maven als Build-Tool.

  • wsConsumer: Maven-Root-Projekt
  • wsProvider-ear: Erzeugen der deploybaren .ear-Datei wsConsumer-ear.ear
  • webservice-beans: Enthält WSDL-Datei des externen Web-Services. Mit JAXB werden zugehörige Java-Klassen automatisch generiert.
  • wsConsumer-war: Definition der Apache Camel-Route, Konfiguration für Apache CXF, Klassen zum Erstellen eines ausgehenden SOAP Requests. Verwendet die in webservice-beans generierten Klassen.

Definition des Web Services

Die Definition erfolgt mit einer WSDL-Datei im Projekt webservice-beans. Der Web Service ExampleWebService enthält nur eine Operation create, an die die Beispieldaten SendTime, Id und Title übergeben werden können.

WSDL

  <wsdl:types>
    <xsd:schema targetNamespace="tns2" xmlns:tns2="tns2" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <xsd:element name="CreateResponse" nillable="true" type="tns2:CreateResponse" />
      <xsd:complexType name="CreateResponse">
        <xsd:sequence>
          <xsd:element name="success" nillable="true" type="xsd:boolean" />
          <xsd:element minOccurs="0" name="ErrorMessage" nillable="true" type="xsd:string" />
        </xsd:sequence>
      </xsd:complexType>
    </xsd:schema>
    <xsd:schema targetNamespace="http://ws.softceed.de/ExampleWebService"
      xmlns:tns="http://ws.softceed.de/ExampleWebService"
      xmlns:tns2="tns2" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <xsd:import namespace="tns2" />
      <xsd:element name="create" type="tns:create" />
      <xsd:element name="createResponse" type="tns:createResponse" />
      <xsd:complexType name="create">
        <xsd:sequence>
          <xsd:element name="CreateRequest" nillable="true" type="tns:CreateRequest" />
        </xsd:sequence>
      </xsd:complexType>
      <xsd:complexType name="CreateRequest">
        <xsd:sequence>
          <xsd:element name="SendTime" nillable="true" type="xsd:dateTime" />
          <xsd:element name="Id" nillable="true" type="xsd:string" />
          <xsd:element name="Title" minOccurs="0" nillable="true" type="xsd:string" />
        </xsd:sequence>
      </xsd:complexType>
      <xsd:complexType name="createResponse">
        <xsd:sequence>
          <xsd:element ref="tns2:CreateResponse" />
        </xsd:sequence>
      </xsd:complexType>
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="ExampleWebService_PortType_create">
    <wsdl:part name="parameters" element="tns1:create">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="ExampleWebService_PortType_createResponse">
    <wsdl:part name="parameters" element="tns1:createResponse">
    </wsdl:part>
  </wsdl:message>
  <wsdl:portType name="ExampleWebService_PortType">
    <wsdl:operation name="create">
      <wsdl:input message="tns1:ExampleWebService_PortType_create">
      </wsdl:input>
      <wsdl:output message="tns1:ExampleWebService_PortType_createResponse">
      </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="Interface_ExampleWebService_Binder" type="tns1:ExampleWebService_PortType">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="create">
      <soap:operation soapAction="Interface_ExampleWebService_Binder_create" style="document" />
      <wsdl:input>
        <soap:body parts="parameters" use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body parts="parameters" use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="ExampleWebService.Interface.ExampleWebService">
    <wsdl:port name="Interface_ExampleWebService_Port" binding="tns1:Interface_ExampleWebService_Binder">
      <soap:address
location="http://ws.softceed.de/ExampleWebService:5511/ws/ExampleWebService.Interface.ExampleWebService/Interface_ExampleWebService_Port" />
    </wsdl:port>
  </wsdl:service>

Maven Dependencies

In den Projekten sind folgende Abhängigkeiten für Apache Camel und Apache CXF einzubinden:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-cxf</artifactId>
      <version>${camel-version}</version>
      <exclusions>
        <exclusion>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-api</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-jaxb</artifactId>
      <version>${camel-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-ws-metadata_2.0_spec</artifactId>
      <version>${geronimo-version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${springframework-version}</version>
    </dependency>
  </dependencies>
</dependencyManagement>

Erzeugen einer ausgehenden Nachricht

Route

Die Apache Camel Route wird in applicationContext.xml in wsConsumer-war definiert. Dabei wird der “Eingangs”-Endpoint der Route (toExternalInputEndpoint) von einer anderen (Camel-)Komponente angesprochen, die die zu versendenden Nachricht bereitstellt. Nach dem Setzen der SOAP-Parameter operationName und SOAPAction im Header der Nachricht (prepareSoapRequest) – und einer möglichen weiteren Verarbeitung – wird die Nachricht an den Web Service verschickt (wsOutputEndpoint). Nach einem Fehler wird bis zu dreimal erneut das Versenden versucht.

<!-- Apache Camel context and routes -->
<camelContext id="wsCamelContext" xmlns="http://camel.apache.org/schema/spring">

  <!-- Endpoints for routes -->
  <endpoint id="toExternalInputEndpoint" 
    uri="<uri to use by component that provides request message to send to external web service" />
  <endpoint id="wsOutputEndpoint" uri="cxf:bean:externalWsEndpoint" />
  <endpoint id="sendMessageToOutputEndpoint" uri="direct:sendMessage" />

  <!-- Global exception handling -->
  <onException>
    <exception>java.lang.Exception</exception>
    <handled>
      <constant>true</constant>
    </handled>
  </onException>

  <!-- Sending requests to external web service -->
  <route id="toExternalWsRoute">
    <!-- Route starts if there is a new request for external web service -->
    <from uri="toExternalInputEndpoint" />
    <onException>
      <exception>java.lang.Exception</exception>
      <handled>
        <constant>true</constant>
      </handled>
    </onException>
    <!-- build SOAP request -->
    <bean ref="messageProcessor" method="prepareSoapRequest" />
    <to uri="sendMessageToOutputEndpoint" />
  </route>

  <route id="sendMessageToExternalWs">
    <from uri="sendMessageToOutputEndpoint" />
    <onException>
      <exception>java.lang.Exception</exception>
      <redeliveryPolicy maximumRedeliveries="3" redeliveryDelay="5000" asyncDelayedRedelivery="true"
        retryAttemptedLogLevel="WARN" logRetryStackTrace="true" logHandled="true" />
      <handled>
        <constant>true</constant>
      </handled>
    </onException>

    <!-- Send request to external web service -->
    <to uri="wsOutputEndpoint" />
    <bean ref="messageProcessor" method="checkSyncResponseForHttpError" />
  </route>

</camelContext>

Konfiguration der Kommunikation mit dem Web Service (CXF-Konfiguration)

CXF-spezifische Konfigurationen werden in einer eigenen XML-Datei external-camel-cxf.xml im Projekt webservice-beans vorgenommen. Sie enthält u.a. die Angabe des externen Web Service-Namens (externalWsEndpoint), der in der Routendefinition verwendet wird (s.o.) und den Namen der Service-Klasse, sowie Interceptors, die z.B. das Logging von ein- und ausgehenden Requests erlauben.

<!-- External web service endpoint -->
<!-- ID is used in Camel endpoint definition: cxf:bean:<id> -->
<cxf:cxfEndpoint id="externalWsEndpoint" address="<url of external web service>"
  serviceClass="de.softceed.ws.examplewebservice.ExampleWebServicePortType" wsdlURL="wsdl/external.wsdl"
  serviceName="s:ExampleWebService.Interface.ExampleWebService" endpointName="s:Interface_ExampleWebService_Port"
  xmlns:s="http://ws.softceed.de/ExampleWebService">
  <cxf:properties>
    <entry key="loggingFeatureEnabled" value="true" />
    <entry key="dataFormat" value="POJO" />
    <entry key="schema-validation-enabled" value="true" />
    <entry key="headerFilterStrategy" value="#headerFilterStrategy" />
  </cxf:properties>
  <cxf:inInterceptors>
    <!-- Logging response in server log -->
    <ref bean="logInbound" />
    <!-- Logging SOAP response to route processing log mechanism -->
    <ref bean="logInboundSoapRequest" />
  </cxf:inInterceptors>
  <cxf:outInterceptors>
    <ref bean="logOutbound" />
    <!-- Logging SOAP request to route processing log mechanism -->
    <ref bean="logOutboundSoapRequest" />
  </cxf:outInterceptors>
  <cxf:outFaultInterceptors>
    <ref bean="logOutbound" />
  </cxf:outFaultInterceptors>
</cxf:cxfEndpoint>

SSL

Soll der Web Service über SSL genutzt werden, können zugehörige Konfigurationen ebenfalls in external-camel-cxf.xml angegeben werden. Sie erfolgen in einem zugehörigen http-conduit-Abschnitt:

<http:conduit name="*.http-conduit">
  <!-- CXF http conduit definition, e.g. to configure TLS connection -->
  <http:client ContentType="text/xml;charset=UTF-8" AcceptEncoding="identity" MaxRetransmits="1"
    Connection="close" AllowChunking="false" />
  <!-- If no SSL is used don't use following tlsClientParameters configuration -->
  <http:tlsClientParameters disableCNCheck="true" secureSocketProtocol="TLS">
    <sec:keyManagers keyPassword="keyPassword">
      <sec:keyStore type="JKS" password="keystorePassword" file="keystore.jks" />
    </sec:keyManagers>
    <sec:trustManagers>
      <sec:keyStore type="JKS" password="truststorePassword" file="truststore.jks" />
    </sec:trustManagers>
  </http:tlsClientParameters>
</http:conduit>

Dabei werden ein Truststore und Keystore mit der Verwaltung der SSL-Zertifikate eingebunden.

Fazit

Das Zusammenspiel von Apache Camel und Apache CXF für Anbindung eines Web Services, der von externen Clients aufgerufen werden kann, ist nicht für jeden Application Server ganz trivial, da die “richtigen” Dependencies verwendet werden müssen. Zudem sind immer spezifische Konfigurationen notwendig, da unterschiedliche Application Server unterschiedliche Konfigurations-Dateien verwenden und auch teilweise zu berücksichtigen ist, dass sie von Haus aus bereits eine Apache CXF-Unterstützung und somit die Einbindung zugehöriger Java-Archive mitbringen. Hat man diese Hürden aber überwunden, kann man die volle Flexibilität von Camel und CXF nutzen.

Links

Projektsourcen auf GitHub – Softceed GmbH
Apache Camel: Ein Web Service für externe Zugriffe mit Apache CXF


Autor

Ähnliche Beiträge