👋
Apache Camel 入門 2
本記事はApache Camel 入門の続編です。
Spring Boot連携機能で、簡単な連携機能を作成してみます。
Spring Boot のCamelサポート
- 2023年1月の段階では、CamelはjakartaEE対応がまだなので、Spring Boot 3.xは未サポート。
- Camel 4.0でSpring Boot 3はサポートされる。ロードマップ
- CamelはRedhatが製品に組み込んでいるが、基本は伝統あるOSSなのでSpring サポートも頑張ってくれそう。
Getting Started
- Spring Initializerで、Spring Bootのバージョンを2.x系、依存関係にApache Camelを選択
- Camelのバージョン管理をするために、build.gradleに、implementation platformを追加し、個別のバージョン番号を削除
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.8'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
repositories {
mavenCentral()
}
dependencies {
implementation platform('org.apache.camel:camel-bom:3.20.1')
implementation platform( 'org.apache.camel.springboot:camel-spring-boot-bom:3.20.1')
implementation 'org.apache.camel.springboot:camel-spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
Timerログルートの作成
- 数秒おきにログ出力する簡単なルートを作成する
- Timerコンポーネントを利用するので、下記の依存関係を追加する。どんな依存関係かはドキュメントを参照する
implementation 'org.apache.camel.springboot:camel-timer-starter'
ルート定義
- RoutBuilderを継承して、configureメソッドをオーバーライドする。SpringのBeanとして登録したら自動で起動される
- URIの構造はドキュメントを参照
package com.example.springcamel.timer;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
@Component
public class TimerRoute extends RouteBuilder {
@Override
public void configure() throws Exception {
//3秒ごとにメッセージ生成,3回で終了
from("timer:foo?fixedRate=true&period=3000&repeatCount=3")
.log("hello");
}
}
- 一応自動生成されているSpring Bootのメインクラス
package com.example.springcamel;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringCamelApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCamelApplication.class, args);
}
}
- 実行
./gradlew bootRun
こんな感じで3回出て止まるはず。(Spring Boot自体は止まってない)
2023-02-01 00:10:50.829 INFO 87572 --- [1 - timer://foo] route2 : hello
2023-02-01 00:10:53.822 INFO 87572 --- [1 - timer://foo] route2 : hello
2023-02-01 00:10:56.822 INFO 87572 --- [1 - timer://foo] route2 : hello
EndpointDSLを利用する
- uriが間違えやすいので、タイプセーフなエンドポイントDSLを利用する
- 依存関係
implementation "org.apache.camel:camel-endpointdsl"
- ルート定義の修正。EndpointRouteBuilderを継承する
package com.example.springcamel.timer;
import org.apache.camel.builder.endpoint.EndpointRouteBuilder;
import org.springframework.stereotype.Component;
@Component
public class TimerRoute extends EndpointRouteBuilder {
@Override
public void configure() throws Exception {
//3秒ごとにメッセージ生成,3回で終了
from(timer("foo")
.fixedRate(true)
.period(3000)
.repeatCount(3))
.log("hello");
}
}
SpringのBeanを利用する
- beanコンポーネントを使ってSpringで管理されているBeanを使う。beanコンポーネントは依存関係追加は不要
SpringのBean定義
- ルートでメッセージが伝播されるのを確認するため、Beanを2つ定義する
package com.example.springcamel.bean;
import org.apache.camel.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class HelloService {
Logger logger= LoggerFactory.getLogger(HelloService.class);
public String called(Message message){
//timerはbodyを設定しないのでnull
logger.info("body is {}",message.getBody());
return "hello";
}
}
package com.example.springcamel.bean;
import org.apache.camel.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class WorldService {
Logger logger= LoggerFactory.getLogger(WorldService.class);
public String called(Message message){
logger.info("body is {}",message.getBody());
//Stringだと信じる
return String.join(" ",message.getBody(String.class),"World");
}
}
ルート定義
package com.example.springcamel.bean;
import org.apache.camel.builder.endpoint.EndpointRouteBuilder;
import org.springframework.stereotype.Component;
@Component
public class BeanRoute extends EndpointRouteBuilder {
@Override
public void configure() throws Exception {
from(timer("foo2").repeatCount(3).fixedRate(true).period(3000))
.bean(HelloService.class) //実行後、HelloServiceのメソッドの戻り値がbodyに設定
.bean(WorldService.class) //実行後、Hメソッドの戻り値がbodyに設定
//expression言語を使ってbody部をログ出力
.log("end body is ${body}");
}
}
実行結果
2023-02-01 00:29:50.253 INFO 87778 --- [ - timer://foo2] c.example.springcamel.bean.HelloService : body is null
2023-02-01 00:29:50.254 INFO 87778 --- [ - timer://foo2] c.example.springcamel.bean.WorldService : body is hello
2023-02-01 00:29:50.255 INFO 87778 --- [ - timer://foo2] route1 : end body is hello World
Beanの修正
- Beanは引数にMessageだけでなく、Body部ももらえる
package com.example.springcamel.bean;
import org.apache.camel.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class WorldService {
Logger logger= LoggerFactory.getLogger(WorldService.class);
public String called(String hello){
logger.info("body is {}",hello);
return String.join(" ",hello,"World");
}
}
- 実行結果
2023-02-01 00:32:42.496 INFO 87800 --- [ - timer://foo2] c.example.springcamel.bean.HelloService : body is null
2023-02-01 00:32:42.496 INFO 87800 --- [ - timer://foo2] c.example.springcamel.bean.WorldService : body is hello
2023-02-01 00:32:42.496 INFO 87800 --- [ - timer://foo2] route1 : end body is hello World
Discussion