このガイドでは、Spring Integrationを使用してRSSフィード(Springブログ)からデータを取得し、データを操作してファイルに書き込む単純なアプリケーションを作成するプロセスを説明します。このガイドでは、従来のSpring Integration XML構成を使用しています。他のガイドでは、JDK 8 Lambda式の有無にかかわらずJava構成とDSLを使用する方法を示しています。

構築するもの

従来のXML構成を使用して、Spring Integrationでフローを作成します。

必要なもの

このガイドを完了する方法

ほとんどのSpring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。

最初から始めるには、Spring Initializrから開始に進みます。

基本スキップするには、次の手順を実行します。

完了したときは、gs-integration/completeのコードに対して結果を確認できます。

Spring Initializrから開始

すべてのSpringアプリケーションの場合、Spring Initializr(英語) から開始する必要があります。Initializrは、アプリケーションに必要なすべての依存関係をすばやく取り込む方法を提供し、多くのセットアップを行います。この例では、Spring Integration依存関係のみが必要です。次のイメージは、このサンプルプロジェクト用に設定されたInitializrを示しています。

initializr
前の図は、Mavenがビルドツールとして選択されたInitializrを示しています。Gradleも使用できます。また、com.example および integration の値をそれぞれグループおよびアーティファクトとして表示します。このサンプルの残りの部分では、これらの値を使用します。

次のリストは、Mavenを選択したときに作成される pom.xml ファイルを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>integration</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>integration</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-integration</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

次のリストは、Gradleを選択したときに作成される build.gradle ファイルを示しています。

plugins {
	id 'org.springframework.boot' version '2.2.0.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-integration'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
	testImplementation 'org.springframework.integration:spring-integration-test'
}

test {
	useJUnitPlatform()
}

ビルドファイルに追加する

この例では、2つの依存関係を追加する必要があります。

  • spring-integration-feed

  • spring-integration-file

次のリストは、最終的な pom.xml ファイルを示しています。

<?xml version="1.0" encoding="UTF-8"?>
<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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>integration</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>integration</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-integration</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-feed</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-file</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

次のリストは、最終的な build.gradle ファイルを示しています。

plugins {
	id 'org.springframework.boot' version '2.2.0.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-integration'
	implementation 'org.springframework.integration:spring-integration-feed'
	implementation 'org.springframework.integration:spring-integration-file'
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
	testImplementation 'org.springframework.integration:spring-integration-test'
}

test {
	useJUnitPlatform()
}

統合Flowを定義する

このガイドのサンプルアプリケーションでは、次のようなSpring Integrationフローを定義します。

  • spring.ioのRSSフィードからブログ投稿を読み取ります。

  • 投稿のタイトルと投稿のURLで構成される、読みやすい String に変換します。

  • その String をファイルの末尾(/tmp/si/SpringBlog)に追加します。

統合フローを定義するには、Spring IntegrationのXML名前空間のいくつかの要素を使用してSpring XML構成を作成できます。具体的には、目的の統合フローのために、これらのSpring Integration名前空間(コア、フィード、およびファイル)の要素を操作します。(最後の2つを取得することが、Spring Initializrによって提供されるビルドファイルを変更する必要があった理由です。)

以下のXML構成ファイル( src/main/resources/integration/integration.xmlから)は、統合フローを定義します。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:int="http://www.springframework.org/schema/integration"
	xmlns:file="http://www.springframework.org/schema/integration/file"
	xmlns:feed="http://www.springframework.org/schema/integration/feed"
	xsi:schemaLocation="http://www.springframework.org/schema/integration/feed https://www.springframework.org/schema/integration/feed/spring-integration-feed.xsd
		http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/integration/file https://www.springframework.org/schema/integration/file/spring-integration-file.xsd
		http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd">

    <feed:inbound-channel-adapter id="news" url="https://spring.io/blog.atom" auto-startup="${auto.startup:true}">
        <int:poller fixed-rate="5000"/>
    </feed:inbound-channel-adapter>

    <int:transformer
            input-channel="news"
            expression="payload.title + ' @ ' + payload.link + '#{systemProperties['line.separator']}'"
            output-channel="file"/>

    <file:outbound-channel-adapter id="file"
            mode="APPEND"
            charset="UTF-8"
            directory="/tmp/si"
            filename-generator-expression="'${feed.file.name:SpringBlog}'"/>

</beans>

ここでは、3つの統合要素が関係しています。

  • <feed:inbound-channel-adapter> : ポーリングごとに1つずつ、投稿を取得する受信アダプター。ここで構成されているように、5秒ごとにポーリングします。投稿は、news (アダプターのIDに対応)という名前のチャネルに配置されます。

  • <int:transformer> : news チャネルのエントリ(com.rometools.rome.feed.synd.SyndEntry)を変換し、エントリのタイトル(payload.title)とリンク(payload.link)を抽出し、読み取り可能な String に連結(および改行を追加)します。 String は、fileという名前の出力チャネルに送信されます。

  • <file:outbound-channel-adapter> : チャネル( fileという名前)からコンテンツをコンテンツに書き込む送信チャネルアダプター。具体的には、ここで構成されているように、file チャネルのすべてを /tmp/si/SpringBlogのファイルに追加します。

次の図は、この単純なフローを示しています。

A flow that reads RSS feed entries

auto-startup 属性は今のところ無視してください。後でテストについて説明するときに、それについて再確認します。今のところ、デフォルトでは trueであることに注意してください。これは、アプリケーションの起動時に投稿が取得されることを意味します。 filename-generator-expressionのプロパティプレースホルダーにも注意してください。これは、デフォルトが SpringBlog ですが、プロパティで上書きできることを意味します。

アプリケーションを実行可能にする

Spring Integrationフローをより大きなアプリケーション(おそらくWebアプリケーション)内で構成することは一般的ですが、より単純なスタンドアロンアプリケーションで定義できない理由はありません。次に行うことは、統合フローを開始し、統合フローをサポートする少数のBeanを宣言するメインクラスを作成することです。また、アプリケーションをスタンドアロンの実行可能JARファイルにビルドします。Spring Bootの @SpringBootApplication アノテーションを使用して、アプリケーションコンテキストを作成します。このガイドでは統合フローにXML名前空間を使用するため、@ImportResource アノテーションを使用してアプリケーションコンテキストにロードする必要があります。次のリスト( src/main/java/com/example/integration/IntegrationApplication.javaから)は、アプリケーションファイルを示しています。

package com.example.integration;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ImportResource;

@SpringBootApplication
@ImportResource("/integration/integration.xml")
public class IntegrationApplication {
  public static void main(String[] args) throws Exception {
    ConfigurableApplicationContext ctx = new SpringApplication(IntegrationApplication.class).run(args);
    System.out.println("Hit Enter to terminate");
    System.in.read();
    ctx.close();
  }

}

実行可能JARを構築する

コマンドラインからGradleまたはMavenを使用してアプリケーションを実行できます。必要なすべての依存関係、クラス、およびリソースを含む単一の実行可能JARファイルを構築して実行することもできます。実行可能なjarを構築すると、開発ライフサイクル全体、さまざまな環境などで、サービスをアプリケーションとして簡単に提供、バージョン管理、およびデプロイできます。

Gradleを使用する場合、./gradlew bootRunを使用してアプリケーションを実行できます。または、次のように、./gradlew build を使用してJARファイルをビルドしてから、JARファイルを実行できます。

java -jar build/libs/gs-integration-0.1.0.jar

Mavenを使用する場合、./mvnw spring-boot:runを使用してアプリケーションを実行できます。または、次のように、./mvnw clean package でJARファイルをビルドしてから、JARファイルを実行できます。

java -jar target/gs-integration-0.1.0.jar
ここで説明する手順は、実行可能なJARを作成します。クラシックWARファイルを作成することもできます。

アプリケーションの実行

これで、次のコマンドを実行して、jarからアプリケーションを実行できます。

java -jar build/libs/{project_id}-0.1.0.jar

... app starts up ...

アプリケーションが起動すると、RSSフィードに接続し、ブログ投稿の取得を開始します。アプリケーションは、ユーザーが定義した統合フローを介してそれらの投稿を処理し、最終的に投稿情報を /tmp/si/SpringBlogのファイルに追加します。

アプリケーションがしばらく実行された後、/tmp/si/SpringBlog でファイルを表示して、少数の投稿からのデータを表示できるはずです。UNIXベースのオペレーティングシステムでは、次のコマンドを実行して、ファイルを tail して、書き込まれた結果を確認することもできます。

tail -f /tmp/si/SpringBlog

次のサンプル出力のようなものが表示されるはずです(ただし、実際のニュースは異なります)。

Spring Integration Java DSL 1.0 GA Released @ https://spring.io/blog/2014/11/24/spring-integration-java-dsl-1-0-ga-released
This Week in Spring - November 25th, 2014 @ https://spring.io/blog/2014/11/25/this-week-in-spring-november-25th-2014
Spring Integration Java DSL: Line by line tutorial @ https://spring.io/blog/2014/11/25/spring-integration-java-dsl-line-by-line-tutorial
Spring for Apache Hadoop 2.1.0.M2 Released @ https://spring.io/blog/2014/11/14/spring-for-apache-hadoop-2-1-0-m2-released

テスト

complete プロジェクトを調べると、src/test/java/com/example/integration/FlowTests.javaにテストケースが表示されます。

package com.example.integration;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.integration.endpoint.SourcePollingChannelAdapter;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;

import com.rometools.rome.feed.synd.SyndEntryImpl;

@SpringBootTest({ "auto.startup=false",   // we don't want to start the real feed
          "feed.file.name=Test" })   // use a different file
public class FlowTests {

  @Autowired
  private SourcePollingChannelAdapter newsAdapter;

  @Autowired
  private MessageChannel news;

  @Test
  public void test() throws Exception {
    assertThat(this.newsAdapter.isRunning()).isFalse();
    SyndEntryImpl syndEntry = new SyndEntryImpl();
    syndEntry.setTitle("Test Title");
    syndEntry.setLink("http://characters/frodo");
    File out = new File("/tmp/si/Test");
    out.delete();
    assertThat(out.exists()).isFalse();
    this.news.send(MessageBuilder.withPayload(syndEntry).build());
    assertThat(out.exists()).isTrue();
    BufferedReader br = new BufferedReader(new FileReader(out));
    String line = br.readLine();
    assertThat(line).isEqualTo("Test Title @ http://characters/frodo");
    br.close();
    out.delete();
  }

}

このテストでは、Spring Bootのテストサポートを使用して、auto.startup という名前のプロパティを falseに設定します。一般的に、特にCI環境では、テストのためにネットワーク接続に依存することはお勧めできません。代わりに、フィードアダプターが起動しないようにし、SyndEntrynews チャネルに挿入して、残りのフローで処理できるようにします。また、テストは feed.file.name を設定して、テストが別のファイルに書き込むようにします。それから:

  • アダプターが停止していることを確認します。

  • テスト SyndEntryを作成します。

  • テスト出力ファイルを削除します(存在する場合)。

  • メッセージを送信します。

  • ファイルが存在することを確認します。

  • ファイルを読み取り、データが期待どおりであることを確認します。

要約

おめでとう! Spring Integrationを使用してspring.ioからブログ投稿を取得し、処理し、ファイルに書き込む単純なアプリケーションを開発しました。

関連事項

次のガイドも役立ちます。

新しいガイドを作成したり、既存のガイドに貢献したいですか? 投稿ガイドラインを参照してください(GitHub)

すべてのガイドは、コード用のASLv2ライセンス、およびドキュメント用のAttribution、NoDerivativesクリエイティブコモンズライセンス(英語) でリリースされています。