Apache Camel を使って Spring Boot 上でルーティングを構築していると、例外処理の設計が非常に重要になります。
特に Camel の <onException>
と <handled>
の使い方を正しく理解することで、ルートの安定性や柔軟性が大きく向上します。
この記事では、基本構文から split
処理内の例外ハンドリング、そしてユースケース別の使い分けパターンまでを網羅的に解説します。
使用バージョン
- Apache Camel 4.x
- Spring Boot 3.x
- DSL:XML DSL
基本構文(onException + handled)
<onException>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<log message="例外をハンドリングしました"/>
<to uri="mock:error"/>
</onException>
handled の true / false の違い
設定値 | 動作の意味 | Camel の挙動 | ルートの継続 |
---|---|---|---|
<constant>true</constant> |
例外を処理済みにする | 上位に例外をスローしない | 継続する or 終了(明示的制御) |
<constant>false</constant> |
例外を未処理として扱う → 再スローする | Camel が例外として再スローする | 中断される |
-
true
:ログを出して終了したり、エラールートに流したりするのに最適 -
false
:例外を上位に伝播させたいときに使用(テストや REST API 連携など)
サンプルルート:例外を補足して別ルートへ
<routes xmlns="http://camel.apache.org/schema/spring">
<!-- 特定の例外をキャッチして処理済みにする -->
<onException>
<exception>java.lang.IllegalArgumentException</exception>
<handled>
<constant>true</constant>
</handled>
<log message="IllegalArgumentException をハンドリング"/>
<to uri="mock:handled"/>
</onException>
<route id="mainRoute">
<from uri="direct:start"/>
<process>
<bean ref="throwingBean"/>
</process>
<to uri="mock:result"/>
</route>
</routes>
@Component("throwingBean")
public class ThrowingBean implements Processor {
@Override
public void process(Exchange exchange) {
throw new IllegalArgumentException("テスト例外");
}
}
応用:条件付きで handled にする
<handled>
<simple>${exception.message} contains '非致命'</simple>
</handled>
応用:split の中で例外をキャッチしつつ処理を継続する
<route id="splitRoute">
<from uri="direct:start"/>
<split>
<simple>${body}</simple>
<parallelProcessing>false</parallelProcessing>
<onException>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<log message="スプリット要素で例外を補足しスキップ"/>
<to uri="mock:error"/>
</onException>
<process ref="itemProcessor"/>
<to uri="mock:each"/>
</split>
<to uri="mock:done"/>
</route>
@Component("itemProcessor")
public class ItemProcessor implements Processor {
@Override
public void process(Exchange exchange) {
String item = exchange.getIn().getBody(String.class);
if ("NG".equals(item)) {
throw new RuntimeException("NG項目が含まれています");
}
}
}
【用途別】Camel の <handled>
の使い分け早見表
ユースケース | handled の値 | 理由・意図 | 備考 |
---|---|---|---|
バッチ処理中の一部エラーをスキップ | true |
エラーを処理済みにし、次のメッセージに進みたい |
split 内での定型パターン |
Kafka/S3送信失敗時に再送処理に回す | true |
エラーを補足して「再送」ルートへ渡す | 再送トピックや deadLetterChannel へ分岐 |
REST API で 500 を返す | false |
例外を外部(Spring Boot)へ伝播し、HTTPエラーを返すため | Spring MVC / WebFlux との連携想定 |
テストで例外を検知して失敗させたい | false |
Camel テスト内で例外を catch せず、JUnit で検証したい |
mockEndpoint.assertIsSatisfied() 失敗を狙うケースなど |
ログだけ出して処理を終了したい(無視はしない) | true |
ユーザー通知や監視は必要だが、処理は止めたい |
log + stop や to("mock:alert") で終了 |
一部の例外だけスキップ(条件付き) | 条件付き handled
|
メッセージ内容や例外内容で柔軟に制御 | <simple>${exception.message} contains '~'</simple> |
おわりに
Camel の例外処理は「どこで止めるか・伝播させるか」を設計でしっかり決めることが重要です。
<handled>
を正しく使い分けることで、堅牢かつ柔軟なルート構成が可能になります。