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

Apache Camel: Ein Web Service für externe Zugriffe mit Apache CXF

Apache Camel: Ein Web Service für externe Zugriffe 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.

Apache Camel, Apache CXF und Oracle WebLogic – ein Beispiel

Ein Web-Service, der als Endpoint in einer Apache Camel Anwendung von außen von einem Client erreichbar ist, kann mit Apache CXF realisiert werden, einem weit verbreiteten Open Source Framework u.a. zum Entwickeln von JAX-WS Diensten. 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 wsProvider skizzenhaft gezeigt, wie ein SOAP Web Service mit Apache CXF in einer Apache Camel-Anwendung für den Oracle WebLogic Server realisiert werden kann. Ausgangspunkt ist die Beschreibung des Web Services mit einer WSDL-Datei (wsdl first). 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.

  • wsProvider: Maven-Root-Projekt
  • wsProvider-ear: Erzeugen der deploybaren .ear-Datei wsProvider-ear.ear
  • wsProvider-jar: Enthält WSDL- und XSD-Datei des Web-Services. Zugehörige JAXB-Klassen werden anhand dieser Definitionen automatisch generiert.
  • wsProvider-war: Definition der Apache Camel-Route, Konfiguration für Apache CXF, Klassen zur Verarbeitung eines eingehenden SOAP Requests. Verwendet die wsProvider-jar generierten Klassen.

Definition des Web Services

Die Definition erfolgt mit einer WSDL- und zugehörigen XSD-Datei im Projekt wsProvider-jar. Der Web Service ExampleRequest enthält nur eine Operation create, an die die Beispieldaten MessageId, Timestamp, RequestId, Title und Description übergeben werden können.

WSDL

<wsdl:message name="inputExampleRequest">
  <wsdl:part element="tns:ExampleRequest" name="ExampleRequest" />
</wsdl:message>
<wsdl:message name="outputExampleRequest">
  <wsdl:part element="tns:SyncResponse" name="SyncResponse" />
</wsdl:message>
 
<wsdl:message name="requestFault">
  <wsdl:part element="tns:RequestFault" name="RequestFault" />
</wsdl:message>
 
<wsdl:portType name="ExampleRequestEndpoint">
  <wsdl:operation name="create">
    <wsdl:documentation>Create new example</wsdl:documentation>
    <wsdl:input message="tns:inputExampleRequest" />
    <wsdl:output message="tns:outputExampleRequest" />
    <wsdl:fault message="tns:requestFault" name="requestFault" />
  </wsdl:operation>
</wsdl:portType>
 
<wsdl:binding name="ExampleRequestBinding" type="tns:ExampleRequestEndpoint">
  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
  <wsdl:operation name="create">
    <soap:operation soapAction="http://example.camel.softceed.de/Example" style="document" />
    <wsdl:input>
      <soap:body use="literal" />
    </wsdl:input>
    <wsdl:output>
      <soap:body use="literal" />
    </wsdl:output>
  </wsdl:operation>
</wsdl:binding>
<wsdl:service name="ExampleRequestEndpointService">
  <wsdl:port name="ExampleRequestService" binding="tns:ExampleRequestBinding">
    <soap:address location="http://localhost:7500/wsprovider/requests" />
  </wsdl:port>
</wsdl:service>

XSD

<xsd:element name="ExampleRequest">
  <xsd:annotation>
    <xsd:documentation>Type for "example request"</xsd:documentation>
  </xsd:annotation>
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="ExampleRequestOperationData" type="eb:ExampleRequestOperationData" />
      <xsd:element name="ExampleRequestInfo" type="eb:ExampleRequestInfo" />
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>
 
<xsd:element name="SyncResponse">
  <xsd:annotation>
    <xsd:documentation>Type for synchronous response information</xsd:documentation>
  </xsd:annotation>
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="Result" type="xsd:string" />
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>
 
<xsd:element name="RequestFault">
  <xsd:annotation>
    <xsd:documentation>Type for information about failed request</xsd:documentation>
  </xsd:annotation>
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="ErrorCode" type="xsd:string" />
      <xsd:element name="ErrorMessage" type="xsd:string" />
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>
 
<xsd:complexType name="ExampleRequestOperationData">
  <xsd:annotation>
    <xsd:documentation>Common information about requested operation</xsd:documentation>
  </xsd:annotation>
  <xsd:sequence>
    <xsd:element name="MessageId" type="xsd:string" />
    <xsd:element name="Timestamp" type="xsd:dateTime" minOccurs="0" />
  </xsd:sequence>
</xsd:complexType>
 
<xsd:complexType name="ExampleRequestInfo">
  <xsd:annotation>
    <xsd:documentation>Special request information</xsd:documentation>
  </xsd:annotation>
  <xsd:sequence>
    <xsd:element name="RequestId" type="xsd:string" />
    <xsd:element name="Title" type="xsd:string" minOccurs="0" />
    <xsd:element name="Description" type="xsd:string" minOccurs="0" />
  </xsd:sequence>
</xsd:complexType>

Maven Dependencies

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

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>wsdl4j</groupId>
      <artifactId>wsdl4j</artifactId>
      <version>1.6.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-context</artifactId>
      <version>${camel-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-core</artifactId>
      <version>${camel-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-spring</artifactId>
      <version>${camel-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-spring-ws</artifactId>
      <version>${camel-version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.camel</groupId>
      <artifactId>camel-cxf</artifactId>
      <version>${camel-version}</version>
    </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>
  </dependencies>
</dependencyManagement>

Verarbeitung einer eingehenden Nachricht

Route

Die Apache Camel Route wird in applicationContext.xml in wsProvider-war definiert. Der Web Service wird als CXF-Service bereitgestellt (wsInputEndpoint). Nach einer ersten Verarbeitung werden die Request-Daten in Objekte umgewandelt (unmarshal) und gegen die XSD-Datei des Web Services validiert (xsdValidatorEndpoint). Nach einer weiteren detaillierteren semantischen Validierung werden die Daten asynchron weiterverarbeitet (asynchProcessingEndpoint).

<!-- Apache Camel context and routes -->
<camelContext xmlns="http://camel.apache.org/schema/spring">
 
  <!-- Endpoints for routes -->
  <endpoint id="wsInputEndpoint" uri="cxf:bean:exampleRequestEndpoint" />
  <endpoint id="xsdValidatorEndpoint" uri="validator:wsdl/wsProvider.xsd" />
  <endpoint id="asynchProcessingEndpoint" uri="seda:asynchProcessing?waitForTaskToComplete=Never" />
 
  <!-- Incoming external web service requests -->
  <route id="fromExternalRoute">
    <!-- Route starts from CXF webservice, see wsProvider-camel-cxf.xml for details -->
    <from uri="wsInputEndpoint" />
 
    <!-- Get request data for validation and further processing -->
    <bean ref="messageProcessor" method="getSoapRequest" />
    <unmarshal ref="requestMessage" />
 
    <!-- Schema validation, no extra checking of message type necessary because only operations defined in web service are possible -->
    <to uri="xsdValidatorEndpoint" />
 
    <!-- validate request data -->
    <bean ref="messageProcessor" method="validateSoapRequest" />
 
    <!-- Further, asynchronous processing -->
    <to uri="asynchProcessingEndpoint" />
 
    <!-- synchronous acknowledgment -->
    <bean ref="messageProcessor" method="getSyncSoapResponse" />
  </route>
 
 
  <route id="asynchronousFromExternalRoute">
    <!-- Asynchronous web service request processing -->
    <from uri="asynchProcessingEndpoint" />
    <setExchangePattern pattern="InOnly" />
 
    <log message="Do further processing, send message to other endpoint..." />
  </route>
 
</camelContext>

CXF-Konfiguration

CXF-spezifische Konfigurationen werden in einer eigenen XML-Datei vorgenommen. Sie enthält u.a. die Angabe des Web Service-Namens (exampleRequestEndpoint), der in der Routendefinition verwendet werden kann (s.o.) und den Namen der Service-Klasse, sowie Interceptors, die z.B. das Logging von ein- und ausgehenden Requests erlauben.

<bean id="logInboundSoapRequest" class="de.softceed.camel.example.mgfromexternal.cxf.SoapInboundLoggingInterceptor" />
<bean id="logOutboundSoapRequest" class="de.softceed.camel.example.mgfromexternal.cxf.SoapOutboundLoggingInterceptor" />
 
<bean id="logInbound" class="org.apache.cxf.interceptor.LoggingInInterceptor" />
<bean id="logOutbound" class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
<bean id="soapFaultInterceptor" class="de.softceed.camel.example.mgfromexternal.cxf.SoapFaultInterceptor" />
 
<!-- Web service endpoint for use by external client -->
<cxf:cxfEndpoint id="exampleRequestEndpoint"
                 address="requests"
                 serviceClass="de.softceed.camel.example.fromexternal.ExampleRequestEndpoint"
                 wsdlURL="wsdl/wsProvider.wsdl">
  <cxf:inInterceptors>
     <!-- Logging request in server log -->
     <ref bean="logInbound" />
     <!-- Logging SOAP request to route processing log mechanism -->                
     <ref bean="logInboundSoapRequest" />
     <!-- Setting SOAP fault for invalid xml request data -->
     <ref bean="soapFaultInterceptor" />
  </cxf:inInterceptors>
  <cxf:outInterceptors>
     <!-- Logging response in server log -->
     <ref bean="logOutbound" />               
  </cxf:outInterceptors>
  <cxf:properties>
    <!-- No schema validation to enable backup of invalid requests in camel route -->
    <entry key="schema-validation-enabled" value="false" />
    <entry key="mtom-enabled" value="true"/>
  </cxf:properties>
</cxf:cxfEndpoint>

LoggingInInterceptor und LoggingOutInterceptor sind Standard-CXF-Interceptors. Aber auch eigene Interceptors können verwendet werden. In der Definition der Interceptors ist die korrekte Platzierung in der Verarbeitungskette („Phase“) zu beachten.

Eigentliche Verarbeitung

Die Verarbeitung von eingehenden Nachrichten kann in eigenen Java-Klassen nach Belieben erfolgen, in unserem Beispiel ist das die Bean messageProcessor, und an andere Module oder Systeme über die Mittel von Apache Camel weitergegeben werden.

Fazit

Das Zusammenspiel von Apache Camel und Apache CXF für die Bereitstellung eines Web Services, der von externen Clients aufgerufen werden kann, ist nicht für jeden Application Server ganz trivial. Leider 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


Autor

Ähnliche Beiträge