【Java】Apache CamelとApache CXFを組み合わせSOAP受信アプリを作成する

# はじめに

本記事では、Apache CamelとApache CXFを組み合わせて、Javaコードファーストで作成したSOAP受信アプリを扱う。
下記の背景があり、情報共有として記事を作成した。
- Java、Apache Camel、Apache CXFの使用経験がなく、学習内容の定着を目的のアウトプット
- GitHub上の[Apache Camelの公式サンプル](https://github.com/apache/camel-examples/tree/main#welcome-to-the-apache-camel-examples) に、Apache CamelとApache CXFを組み合わせたサンプルが存在しない
- ネットでApache CamelとApache CXFについて検索したとき、ヒットする情報が少ないうえに、古い(リンク切れもある)
- ネットでApache Camel&Apache CXF& Springboot(あるいはSpring)を組み合わせたサンプルはヒットするが、組み合わせが増えると複雑化するので、今回はApache Camel&Apache CXFの組み合わせで振る舞いの理解を深めたい。


# 実行環境
- Eclipse (4.32.0)
- Maven (3.9.7)
- Java 17(執筆時点の最新バージョンのApache Camelを使用する際に、このバージョンが必要)
- SoapUI (5.7.2)



# 実装の流れ

## 依存関係を設定する

<details open><summary>pom.xml</summary>

~~~xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>java.camel.soap.example</groupId>
	<artifactId>camel-example-cxf-soap</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>soap example</name>
	<description>An example which demonstrates the use of the Camel CXF
		component</description>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<maven.compiler.source>17</maven.compiler.source>
		<maven.compiler.target>17</maven.compiler.target>
		<camel.version>4.7.0</camel.version>
	</properties>
	<dependencies>
		<!-- https://mvnrepository.com/artifact/org.apache.camel/camel-core -->
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-core</artifactId>
			<version>${camel.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-cxf-soap</artifactId>
			<!-- use the same version as your Camel core version -->
			<version>${camel.version}</version>
		</dependency>
		<dependency>
			<groupId>org.apache.cxf</groupId>
			<artifactId>cxf-rt-transports-http-jetty</artifactId>
			<version>4.0.4</version>
		</dependency>
	</dependencies>
</project>

~~~

</details>

<details ><summary>propertiesタグについて</summary>

- EclipseのデフォルトのランタイムがJava 8のため、Java 17を使用するようにコンパイラ設定を追加[(参考)](https://web-dev.hatenablog.com/entry/maven/java-version-setting)

</details >


<details ><summary>dependenciesタグについて</summary>

-  Apache Camelを適用するために、[Maven Reposityよりcamel-core](https://mvnrepository.com/artifact/org.apache.camel/camel-core/4.7.0) を指定した(2024/08/14時点の最新)
-  [Apache CamelのCXFコンポーネント](https://camel.apache.org/components/4.4.x/cxf-component.html)を適用する
-  「cxf-rt-transports-http-jetty」を依存に追加する。追加しない場合、実行時エラーになる（※）

※ [Apache CXF>Maven POM Information](https://cxf.apache.org/docs/using-cxf-with-maven.html)によれば、「Jetty is needed if you're are not using the CXFServlet」とのこと。「cxf-rt-transports-http-jetty」のバージョンは「cxf-rt-frontend-jaxws」、「cxf-rt-transports-http」に合わせた。

</details>


##  service endpoint interface (SEI)を作成する

以下のようにJava標準のインターフェースを定義すれば良い。

~~~Java

package example;

import jakarta.jws.WebParam;
import jakarta.jws.WebService;

@WebService
public interface HelloWorld {

    String sayHi(@WebParam(name = "text") String text);
}

~~~

サービス定義のマッピングの際にメタデータとして活用するため、アノテーションを指定する必要がある。
[Apache CXF>Developing a Service using JAX-WS#Java First Development](https://cxf.apache.org/docs/developing-a-service.html#DevelopingaService-JavaFirstDevelopment)に、詳細な記載がある。

##  サービスの実装クラスを作成する

~~~Java
package example;

import jakarta.jws.WebService;

@WebService
public class HelloWorldImpl implements HelloWorld{

	@Override
	public String sayHi(String text) {
		return "Hello " + text;
	}
}
~~~

##  ルーティングの設定を実装する

~~~Java

package example;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;

public class SoapRouteBuilder extends RouteBuilder{
	private static final String SOAP_ENDPOINT_URI = "cxf://http://localhost:9006/soap"
	        + "?serviceClass=example.HelloWorld";
	private Processor proc;
	
	SoapRouteBuilder(Processor proc){
		this.proc = proc;
	}
	
	@Override
	public void configure() throws Exception {
		System.out.println("Starting Server");
		from(SOAP_ENDPOINT_URI)
		.process(this.proc);
	}
}

~~~

以下の部分が、Apache CamelとApache CXFの紐づけを行っている箇所。

~~~
cxf://http://localhost:9006/soap?serviceClass=example.HelloWorld
~~~

「cxf://http\://localhost:9006/soap」の部分は、Apache CamelのCXFコンポーネントのURIフォーマット(下記)に従って記述した。

>cxf://someAddress[?options]
Where someAddress specifies the CXF endpoint’s address. With this URI format, >most of the endpoint details are specified using options.

続いて、クエリパラメータであるserviceClassを用いて、AddressとSEIを紐づけした。


##  メッセージ受信時の処理を実装する

~~~Java

package example;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;

public class SoapProcessor implements Processor {
	private HelloWorld instance;

	SoapProcessor(HelloWorld obj) {
		instance = obj;
	}

	@Override
	public void process(Exchange exchange) throws Exception {
		System.out.println("Start Process...");
		String inputText = exchange.getIn().getBody(String.class);
		String response = instance.sayHi(inputText);
		exchange.getMessage().setBody(response);
		System.out.println("End Process...");
	}
}

~~~

本クラスの中で、サービス実装クラス HelloWorldImplを呼び出す。



##  Mainを実装する


~~~Java

package example;

import org.apache.camel.CamelContext;
import org.apache.camel.impl.DefaultCamelContext;

public class Main {

	public static void main(String[] args) throws Exception {
        System.out.println("Server ready...");
		try (CamelContext context = new DefaultCamelContext()) {
			context.addRoutes(new SoapRouteBuilder(new SoapProcessor(new HelloWorldImpl())));
			context.start();
			System.out.println("Sleep...");
			// コンソールで実行した時にすぐ終了するため、スリープを入れる
			Thread.sleep(5 * 60 * 1000);
		}
        System.out.println("Server exiting");
	}
}

~~~



# 動作確認

## エンドポイントへの接続確認　兼　WSDLファイルを取得

コンソールアプリケーションを起動後に、Webブラウザに以下URLを入力する。
http://localhost:9006/soap?wsdl

以下のようにWSDLが表示されたら、接続確認はOK。ブラウザ表示内容をWSDL形式で名前を付けて、保存する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/1a076888-aa82-7c06-05e6-1baf0157ece1.png)


## SoapUIからSOAPメッセージを送信する
前の手順で取得したWSDLファイルを取り込み、SoapUIからSOAPメッセージを送信する。

実行結果は以下の通り。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/233cb89c-10f1-061b-6222-a0e474799d8c.png)

画面の左側が、送信元の情報（要求パラメータを設定する画面）である。
画面の右側が、実行結果(Raw形式表示)である。
実行結果がHTTPステータス　200となっており、SOAP通信に成功したことを確認した。

実行結果をXML形式表示に切り替えて、応答情報が期待通りかを確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156594/3503164c-24d4-eb16-b768-4256384baa0e.png)

応答情報の<return>タグに、期待通りの文字列(※)が格納されていることを確認した。
※サービス実装クラス　HelloWorldImplの戻り値

# ソースコードの格納場所
https://github.com/SY81517/Quiita/tree/master/Java/camel-example-cxf-soap

# 参考サイト

| No | リンク | 備考 |
|:--|:--|:--|
|1  | [Camel-Core>Working with Camel Core](https://camel.apache.org/camel-core/working-with-camel-core/index.html) | Apache Camel Coreの機能全体を俯瞰するときに、利用した。 |
|2  | [10分で基礎をマスターするApache Camel](https://qiita.com/dydy/items/2125c399d721200e8bac)| Apache Camelの使い方をわかりやすく解説しているQiitaの記事。  |
|3   | [Apache Camelの公式サンプル](https://github.com/apache/camel-examples/tree/main#welcome-to-the-apache-camel-examples)  | Apache Camelの公式サンプル。 |
|4   | [Camel>Components>CXF](https://camel.apache.org/components/4.4.x/cxf-component.html)  | Apache CamelのCXFコンポーネントに関するページ。  |
|5   | [Apache CXF>A simple JAX-WS service](https://cxf.apache.org/docs/a-simple-jax-ws-service.html)  | Apache CXFのみでSOAP通信を行うクライアント、サーバーの実装例として参考にした。 |
|6   | [Apache CXF>JAX-WS Developing a Service#JavaFirstDevelopment](https://cxf.apache.org/docs/developing-a-service.html#DevelopingaService-JavaFirstDevelopment)  | Apache CXFでJAX-WSを使用したサービス開発の方法がまとめられたページ。|
|7   | [Apache CXF>Example Projects](https://cxf.apache.org/docs/sample-projects.html)  | Apache CXFのサンプルプロジェクトの一覧。  |
|8   | [Apache CXF>Maven POM Information](https://cxf.apache.org/docs/using-cxf-with-maven.html)  | Apache CXFの依存関係に関する説明。  |
|9   |[examples>camel-example-cxf](https://github.com/apache/camel/tree/camel-2.19.5/examples/camel-example-cxf)  | camel-2.19.5版におけるCamel CXF のサンプル。GitHubからクローンしたが、ビルドに失敗した。camel-2.19.5版のサンプルの実装を、本記事の実装の参考にした。  |
|10 |[Apache Maven>POM Reference](https://maven.apache.org/pom.html)   | POMのリファレンス |
|11   |[Apache Camel CXF Example](https://examples.javacodegeeks.com/java-development/enterprise-java/apache-camel/apache-camel-cxf-example/)   | Apache Camel&Apache CXF& Springを組み合わせたサンプル。  |
|12 | [SOAP Webservices using Apache Camel, Springboot and CXF](https://medium.com/@mouli2k5/soap-webservices-using-apache-camel-springboot-and-cxf-201ca25b4e2)  | Apache Camel&Apache CXF& Springbootを組み合わせたサンプル。  |
|13 |[CXF example : SOAP(JAX-WS), REST(JAX-RS), Spring](https://exampledriven.wordpress.com/2012/10/03/cxf-example-soapjax-ws-restjax-rs-spring/) |Apache Camel&Apache CXF& Springを組み合わせたサンプル。 |
|14 |[camel-spring-boot-examples>soap-cxf](https://github.com/apache/camel-spring-boot-examples/tree/main/soap-cxf)   | Apache Camel&Apache CXF& Springbootを組み合わせたサンプル。  |