メールサポート

このセクションでは、Spring Integration でメールメッセージを操作する方法について説明します。

この依存関係をプロジェクトに含める必要があります。

Maven
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-mail</artifactId>
    <version>5.5.8</version>
</dependency>
Gradle
compile "org.springframework.integration:spring-integration-mail:5.5.8"

javax.mail:javax.mail-api は、ベンダー固有の実装を介して含める必要があります。

メール送信チャネルアダプター

Spring Integration は、MailSendingMessageHandler を使用した送信メールのサポートを提供します。次の例に示すように、Spring の JavaMailSender の構成済みインスタンスに委譲します。

 JavaMailSender mailSender = context.getBean("mailSender", JavaMailSender.class);

 MailSendingMessageHandler mailSendingHandler = new MailSendingMessageHandler(mailSender);

MailSendingMessageHandler には、Spring の MailMessage 抽象化を使用するさまざまなマッピング戦略があります。受信したメッセージのペイロードがすでに MailMessage インスタンスである場合、直接送信されます。一般的に、このコンシューマーの前に、自明ではない MailMessage 構成要件用のトランスフォーマを使用することをお勧めします。ただし、Spring Integration はいくつかの単純なメッセージマッピング戦略をサポートしています。例: メッセージペイロードがバイト配列の場合、添付ファイルにマップされます。単純なテキストベースのメールの場合、文字列ベースのメッセージペイロードを提供できます。その場合、String をテキストコンテンツとして使用して MailMessage が作成されます。toString() メソッドが適切なメールテキストコンテンツを返すメッセージペイロード型を使用する場合は、送信メールアダプターの前に Spring Integration の ObjectToStringTransformer を追加することを検討してください(詳細については、XML を使用した Transformer の構成の例を参照してください)。

MessageHeaders の特定の値を使用して、送信 MailMessage を構成することもできます。可能な場合、値は、受信者(To、Cc、BCc)、from、reply-to、subject などの送信メールのプロパティにマップされます。ヘッダー名は、次の定数によって定義されます。

 MailHeaders.SUBJECT
 MailHeaders.TO
 MailHeaders.CC
 MailHeaders.BCC
 MailHeaders.FROM
 MailHeaders.REPLY_TO
MailHeaders では、対応する MailMessage 値をオーバーライドすることもできます。例: MailMessage.to が '[ メール保護 ] (英語) ' に設定され、MailHeaders.TO メッセージヘッダーが提供される場合、それが優先され、MailMessage の対応する値をオーバーライドします。

メール受信チャネルアダプター

Spring Integration は、MailReceivingMessageSource を使用した受信メールのサポートも提供します。Spring Integration 自身の MailReceiver インターフェースの構成済みインスタンスに委譲します。Pop3MailReceiver と ImapMailReceiver の 2 つの実装があります。これらのいずれかをインスタンス化する最も簡単な方法は、次の例に示すように、メールストアの "uri" を受信者のコンストラクターに渡すことです。

MailReceiver receiver = new Pop3MailReceiver("pop3://usr:pwd@localhost/INBOX");

メールを受信する別のオプションは、IMAP idle コマンドです(メールサーバーでサポートされている場合)。Spring Integration は、それ自体がメッセージ生成エンドポイントである ImapIdleChannelAdapter を提供します。ImapMailReceiver のインスタンスに委譲しますが、メールメッセージの非同期受信を有効にします。次のセクションでは、「メール」スキーマで Spring Integration のネームスペースサポートを使用して両方の型の受信チャネルアダプターを構成する例を示します。

通常、IMAPMessage.getContent() メソッドが呼び出されると、次の例に示すように、特定のヘッダーと本文がレンダリングされます(単純なテキストメールの場合)。

To: [email protected] (英語)  
From: [email protected] (英語)  
Subject: Test Email

something

単純な MimeMessage の場合、getContent() はメール本文(前の例では something)を返します。

バージョン 2.2 以降、フレームワークは IMAP メッセージを積極的にフェッチし、MimeMessage の内部サブクラスとして公開します。これには、getContent() の動作を変更するという望ましくない副作用がありました。この不一致は、バージョン 4.3 で導入されたメールマッピングの機能強化によってさらに悪化しました。これは、ヘッダーマッパーが提供されたときに、ペイロードが IMAPMessage.getContent() メソッドによってレンダリングされたためです。これは、ヘッダーマッパーが提供されたかどうかによって、IMAP コンテンツが異なることを意味しました。

バージョン 5.0 以降、IMAP ソースから発信されたメッセージは、ヘッダーマッパーが提供されているかどうかに関係なく、IMAPMessage.getContent() の動作に従ってコンテンツをレンダリングします。ヘッダーマッパーを使用せず、本文のみをレンダリングする以前の動作に戻したい場合は、メールレシーバーの simpleContent ブールプロパティを true に設定します。このプロパティは、ヘッダーマッパーが使用されているかどうかに関係なくレンダリングを制御するようになりました。ヘッダーマッパーが提供されている場合、ボディのみのレンダリングが可能になりました。

バージョン 5.2 以降、autoCloseFolder オプションがメール受信者に提供されています。false に設定しても、フェッチ後にフォルダーが自動的に閉じられることはありませんが、代わりに IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE ヘッダー(詳細については MessageHeaderAccessor API を参照)がチャネルアダプターからプロデューサーへのすべてのメッセージに入力されます。これは、新しいメッセージを取得するためにフォルダーを開いたり閉じたりすることに依存しているため、Pop3MailReceiver では機能しません。ダウンストリームフローで必要な場合は常に、このヘッダーで close() を呼び出すのはターゲットアプリケーションの責任です。

Closeable closeableResource = StaticMessageHeaderAccessor.getCloseableResource(mailMessage);
if (closeableResource != null) {
    closeableResource.close();
}

フォルダーを開いたままにしておくと、添付ファイル付きのメールのマルチパートコンテンツの解析中にサーバーとの通信が必要な場合に役立ちます。shouldDeleteMessages が AbstractMailReceiver でそれぞれ構成されている場合、IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE ヘッダーの close() は AbstractMailReceiver に委譲して、expunge オプションでフォルダーを閉じます。

バージョン 5.4 以降、変換や先行したコンテンツの読み込みを行わずに、MimeMessage をそのまま返すことができるようになりました。この機能は、次のオプションの組み合わせで有効になります。headerMapper は提供されず、simpleContent プロパティは false であり、autoCloseFolder プロパティは false です。MimeMessage は、生成された Spring メッセージのペイロードとして存在します。この場合、入力されるヘッダーは、MimeMessage の処理が完了したときに閉じる必要があるフォルダーの上記の IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE のみです。

受信メールメッセージマッピング

デフォルトでは、受信アダプターによって生成されるメッセージのペイロードは生の MimeMessage です。そのオブジェクトを使用して、ヘッダーとコンテンツを調べることができます。バージョン 4.3 から、HeaderMapper<MimeMessage> を提供してヘッダーを MessageHeaders にマップできます。便宜上、Spring Integration はこの目的のために DefaultMailHeaderMapper を提供します。次のヘッダーをマップします。

  • mail_fromfrom アドレスの String 表現。

  • mail_bccbcc アドレスを含む String 配列。

  • mail_cccc アドレスを含む String 配列。

  • mail_toto アドレスを含む String 配列。

  • mail_replyToreplyTo アドレスの String 表現。

  • mail_subject: メールの件名。

  • mail_lineCount: 行数(使用可能な場合)。

  • mail_receivedDate: 受信日(利用可能な場合)。

  • mail_size: メールのサイズ(利用可能な場合)。

  • mail_expunged: メッセージが削除されたかどうかを示すブール値。

  • mail_raw: すべてのメールヘッダーとその値を含む MultiValueMap

  • mail_contentType: 元のメールメッセージのコンテンツ型。

  • contentType: ペイロードコンテンツ型(以下を参照)。

メッセージマッピングが有効な場合、ペイロードはメールメッセージとその実装に依存します。通常、メールの内容は、MimeMessage 内の DataHandler によってレンダリングされます。

text/* メールの場合、ペイロードは String であり、contentType ヘッダーは mail_contentType と同じです。

javax.mail.Part インスタンスが埋め込まれたメッセージの場合、DataHandler は通常 Part オブジェクトをレンダリングします。これらのオブジェクトは Serializable ではなく、Kryo などの代替技術による直列化には適していません。このため、デフォルトでは、マッピングが有効になっている場合、そのようなペイロードは Part データを含む生の byte[] としてレンダリングされます。Part の例は、Message および Multipart です。この場合、contentType ヘッダーは application/octet-stream です。この動作を変更して Multipart オブジェクトペイロードを受信するには、MailReceiver で embeddedPartsAsBytes を false に設定します。DataHandler にとって未知のコンテンツ型の場合、コンテンツは application/octet-stream の contentType ヘッダーを持つ byte[] としてレンダリングされます。

ヘッダーマッパーを指定しない場合、メッセージペイロードは javax.mail によって提示される MimeMessage です。フレームワークは、メールの内容を String に変換する戦略を使用してメッセージを変換するために使用できる MailToStringTransformer を提供します。

Java DSL
   ...
   .transform(Mail.toStringTransformer())
   ...
Java
@Bean
@Transformer(inputChannel="...", outputChannel="...")
public Transformer transformer() {
    return new MailToStringTransformer();
}
Kotlin
   ...
   transform(Mail.toStringTransformer())
   ...
XML
<int-mail:mail-to-string-transformer ... >

バージョン 4.3 以降、トランスフォーマーは組み込み Part インスタンス(および以前に処理された Multipart インスタンス)を処理します。トランスフォーマーは、上記のリストのアドレスおよびサブジェクトヘッダーをマップする AbstractMailTransformer のサブクラスです。メッセージに対して他の変換を実行する場合は、AbstractMailTransformer のサブクラス化を検討してください。

バージョン 5.4 以降、headerMapper が提供されていない場合、autoCloseFolder は falsesimpleContent は false であり、MimeMessage は、生成された Spring メッセージのペイロードにそのまま返されます。このように、MimeMessage のコンテンツは、フローの後半で参照されるときにオンデマンドでロードされます。上記のすべての変換は引き続き有効です。

メール名前空間のサポート

Spring Integration は、メール関連の構成に名前空間を提供します。これを使用するには、次のスキーマの場所を構成します。

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

送信チャネルアダプターを設定するには、次の例に示すように、受信元のチャネルと MailSender を提供します。

<int-mail:outbound-channel-adapter channel="outboundMail"
    mail-sender="mailSender"/>

または、次の例に示すように、ホスト、ユーザー名、パスワードを指定できます。

<int-mail:outbound-channel-adapter channel="outboundMail"
    host="somehost" username="someuser" password="somepassword"/>

バージョン 5.1.3 以降、java-mail-properties が提供されている場合、hostusernamemail-sender は省略できます。ただし、host および username は、適切な Java メールプロパティで構成する必要があります。SMTP の場合:

[email protected] (英語)  
mail.smtp.host=smtp.gmail.com
mail.smtp.port=587
送信チャネルアダプターと同様に、参照されるチャネルが PollableChannel である場合、<poller> 要素を提供する必要があります(エンドポイント名前空間のサポートを参照)。

名前空間のサポートを使用する場合、header-enricher メッセージトランスフォーマーも使用できます。そうすることで、メール送信チャネルアダプターに送信する前のメッセージへの前述のヘッダーの適用が簡単になります。

次の例では、ペイロードが、指定されたプロパティに適切な getter を備えた Java Bean であると想定していますが、任意の SpEL 式を使用できます。

<int-mail:header-enricher input-channel="expressionsInput" default-overwrite="false">
	<int-mail:to expression="payload.to"/>
	<int-mail:cc expression="payload.cc"/>
	<int-mail:bcc expression="payload.bcc"/>
	<int-mail:from expression="payload.from"/>
	<int-mail:reply-to expression="payload.replyTo"/>
	<int-mail:subject expression="payload.subject" overwrite="true"/>
</int-mail:header-enricher>

または、value 属性を使用してリテラルを指定できます。default-overwrite および個々の overwrite 属性を指定して、既存のヘッダーの動作を制御することもできます。

受信チャネルアダプターを構成するには、ポーリングまたはイベントドリブンのいずれかを選択できます(メールサーバーが IMAP idle をサポートしている場合は、ポーリングが唯一のオプションです)。ポーリングチャネルアダプターには、ストア URI と受信メッセージの送信先チャネルが必要です。URI は pop3 または imap で始まる場合があります。次の例では、imap URI を使用しています。

<int-mail:inbound-channel-adapter id="imapAdapter"
      store-uri="imaps://[username]:[password]@imap.gmail.com/INBOX"
      java-mail-properties="javaMailProperties"
      channel="receiveChannel"
      should-delete-messages="true"
      should-mark-messages-as-read="true"
      auto-startup="true">
      <int:poller max-messages-per-poll="1" fixed-rate="5000"/>
</int-mail:inbound-channel-adapter>

IMAP idle をサポートしている場合は、代わりに imap-idle-channel-adapter 要素を構成することができます。idle コマンドはイベント駆動型通知を有効にするため、このアダプターにポーラーは必要ありません。新しいメールが利用可能であるという通知を受信するとすぐに、指定されたチャネルにメッセージを送信します。次の例では、IMAP idle メールチャネルを構成します。

<int-mail:imap-idle-channel-adapter id="customAdapter"
      store-uri="imaps://[username]:[password]@imap.gmail.com/INBOX"
      channel="receiveChannel"
      auto-startup="true"
      should-delete-messages="false"
      should-mark-messages-as-read="true"
      java-mail-properties="javaMailProperties"/>

javaMailProperties を提供するには、通常の java.utils.Properties オブジェクトを作成および設定します。たとえば、Spring が提供する util 名前空間を使用します。

ユーザー名に "@" 文字が含まれる場合は、"@" ではなく "%40" を使用して、基になる JavaMail API からの解析エラーを回避します。

次の例は、java.util.Properties オブジェクトを構成する方法を示しています。

<util:properties id="javaMailProperties">
  <prop key="mail.imap.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
  <prop key="mail.imap.socketFactory.fallback">false</prop>
  <prop key="mail.store.protocol">imaps</prop>
  <prop key="mail.debug">false</prop>
</util:properties>

デフォルトでは、ImapMailReceiver はデフォルトの SearchTerm に基づいてメッセージを検索します。これは、以下のすべてのメールメッセージです。

  • 最近 (サポートされている場合)

  • 回答されていません

  • 削除されません

  • 見えない

  • h このメール受信者によって処理されていない (カスタム USER フラグを使用して有効にするか、サポートされていない場合は単に NOT FLAGGED)

カスタムユーザーフラグは spring-integration-mail-adapter ですが、設定できます。バージョン 2.2 以降、ImapMailReceiver が使用する SearchTerm は SearchTermStrategy で完全に構成可能であり、search-term-strategy 属性を使用して注入できます。SearchTermStrategy は、ImapMailReceiver が使用する SearchTerm のインスタンスを作成できる単一のメソッドを持つストラテジーインターフェースです。次のリストは、SearchTermStrategy インターフェースを示しています。

public interface SearchTermStrategy {

    SearchTerm generateSearchTerm(Flags supportedFlags, Folder folder);

}

次の例は、デフォルトの SearchTermStrategy ではなく TestSearchTermStrategy に依存しています。

<mail:imap-idle-channel-adapter id="customAdapter"
			store-uri="imap:something"
			…
			search-term-strategy="searchTermStrategy"/>

<bean id="searchTermStrategy"
  class="o.s.i.mail.config.ImapIdleChannelAdapterParserTests.TestSearchTermStrategy"/>

メッセージのフラグ付けについては、\Recent がサポートされていない場合の IMAP メッセージのマーキングを参照してください。

重要: IMAP PEEK

バージョン 4.1.1 以降、IMAP メールレシーバーは、指定されている場合、mail.imap.peek または mail.imaps.peek JavaMail プロパティを使用します。以前は、レシーバーはプロパティを無視し、常に PEEK フラグを設定していました。このプロパティを明示的に false に設定すると、メッセージは shouldMarkMessagesRead の設定に関係なく \Seen としてマークされます。指定しない場合、以前の動作が保持されます(ピークは true です)。

IMAP idle と失われた接続

IMAP idle チャネルアダプターを使用すると、サーバーへの接続が失われる場合があります(たとえば、ネットワーク障害など)。また、JavaMail のドキュメントでは、実際の IMAP API は実験的であると明示されているため、API との違いを理解することが重要です IMAP idle アダプターを構成するときにそれらに対処する方法。現在、Spring Integration メールアダプターは JavaMail 1.4.1 および JavaMail 1.4.3 でテストされています。どちらを使用するかに応じて、自動再接続に関して設定する必要がある JavaMail プロパティのいくつかに特別な注意を払う必要があります。

Gmail では次の動作が観察されましたが、他のプロバイダーとの再接続の課題を解決する方法についてのヒントを提供する必要があります。ただし、フィードバックはいつでも歓迎します。繰り返しますが、次のメモは Gmail に基づいています。

JavaMail 1.4.1 では、mail.imaps.timeout プロパティを比較的短い時間(テストでは約 5 分)に設定すると、IMAPFolder.idle() はこのタイムアウト後に FolderClosedException をスローします。ただし、このプロパティが設定されていない場合(無期限である必要があります)、IMAPFolder.idle() メソッドは返されず、例外をスローしません。ただし、接続が短時間(テストでは 10 分未満)失われた場合、自動的に再接続します。ただし、接続が長時間(10 分以上)失われた場合、IMAPFolder.idle() は FolderClosedException をスローせず、接続を再確立せず、無期限にブロックされた状態のままになります。アダプターを再起動します。JavaMail 1.4.1 で再接続を動作させる唯一の方法は、mail.imaps.timeout プロパティを明示的に何らかの値に設定することですが、そのような値は比較的短く(10 分未満)、接続を比較的迅速に再確立する必要があることも意味します。繰り返しますが、Gmail 以外のプロバイダーでは異なる場合があります。JavaMail 1.4.3 により、API に大幅な改善が導入され、IMAPFolder.idle() メソッドが StoreClosedException または FolderClosedException を強制的に返すか、単に戻るように強制する条件が常に存在するようになり、自動再接続を続行できます。現在、自動再接続は無限に実行され、10 秒ごとに再接続を試みます。

どちらの構成でも、channel と should-delete-messages は必須属性です。should-delete-messages が必要な理由を理解する必要があります。課題は POP3 プロトコルにあります。POP3 プロトコルには、読み取られたメッセージに関する知識がありません。単一セッション内で読み取られた内容のみを知ることができます。つまり、POP3 メールアダプターを実行すると、各ポーリング中にメールが利用可能になり、メールが正常に消費され、単一のメールメッセージが複数回配信されることはありません。ただし、アダプターを再起動して新しいセッションを開始するとすぐに、以前のセッションで取得された可能性のあるすべてのメールメッセージが再度取得されます。それが POP3 の性質です。should-delete-messages はデフォルトで true であると主張する人もいます。言い換えれば、1 つの最適なデフォルトを選択するのが非常に困難になる、2 つの有効で相互排他的な使用箇所があります。アダプターを唯一のメール受信者として設定することもできます。その場合、以前に配信されたメッセージが再度配信されないことを恐れずにアダプターを再起動できるようにする必要があります。この場合、should-delete-messages を true に設定するのが最も理にかなっています。ただし、別のユースケースでは、複数のアダプターでメールサーバーとそのコンテンツを監視することもできます。言い換えると、「タッチするのではなく、のぞきたい」ということです。次に、should-delete-messages を false に設定するのがはるかに適切です。そのため、should-delete-messages 属性の正しいデフォルト値を選択するのは難しいため、ユーザーが設定する必要のある属性にしました。また、あなたに任せておくと、意図しない動作が発生する可能性が低くなります。
ポーリングメールアダプターの should-mark-messages-as-read 属性を構成する場合、メッセージを取得するために構成しているプロトコルに注意する必要があります。例: POP3 はこのフラグをサポートしていません。つまり、メッセージが既読としてマークされていないため、どちらの値に設定しても効果がありません。

サイレントにドロップされた接続の場合、アイドルキャンセルタスクがバックグラウンドで定期的に実行されます(通常、新しい IDLE はすぐに処理されます)。この間隔を制御するために、cancelIdleInterval オプションが提供されています。デフォルト 120 (2 分)。RFC 2177 は、29 分以内の間隔を推奨しています。

これらのアクション(メッセージの開封と削除)は、メッセージを受信した後、処理する前に実行されることを理解する必要があります。これにより、メッセージが失われる可能性があります。

代わりにトランザクション同期の使用を検討することをお勧めします。トランザクションの同期を参照してください。

<imap-idle-channel-adapter/> は 'error-channel' 属性も受け入れます。ダウンストリーム例外がスローされ、「エラーチャネル」が指定されている場合、失敗したメッセージと元の例外を含む MessagingException メッセージがこのチャネルに送信されます。そうでない場合、ダウンストリームチャネルが同期である場合、そのような例外はチャネルアダプターによって警告としてログに記録されます。

3.0 リリース以降、IMAP idle アダプターは、例外が発生するとアプリケーションイベント(具体的には ImapIdleExceptionEvent インスタンス)を発行します。これにより、アプリケーションはこれらの例外を検出して処理できます。ImapIdleExceptionEvent またはそのスーパークラスの 1 つを受信するように構成された <int-event:inbound-channel-adapter> または任意の ApplicationListener を使用して、イベントを取得できます。

\Recent がサポートされていない場合の IMAP メッセージのマーキング

shouldMarkMessagesAsRead が true の場合、IMAP アダプターは \Seen フラグを設定します。

さらに、メールサーバーが \Recent フラグをサポートしていない場合、サーバーがユーザーフラグをサポートしている限り、IMAP アダプターはメッセージをユーザーフラグ(デフォルトでは spring-integration-mail-adapter)でマークします。そうでない場合、Flag.FLAGGED は true に設定されます。これらのフラグは、shouldMarkMessagesRead 設定に関係なく適用されます。

[ 検索語 ] で説明したように、デフォルトの SearchTermStrategy は、フラグが立てられたメッセージを無視します。

バージョン 4.2.2 以降、MailReceiver で setUserFlag を使用してユーザーフラグの名前を設定できます。これにより、複数の受信者が異なるフラグを使用できるようになります(メールサーバーがユーザーフラグをサポートしている場合)。user-flag 属性は、名前空間を使用してアダプターを構成するときに使用できます。

メールメッセージのフィルタリング

多くの場合、受信メッセージをフィルタリングする必要があります(たとえば、Subject 行に "Spring Integration" が含まれるメールのみを読み取りたい場合)。これは、受信メールアダプターを式ベースの Filter に接続することで実現できます。それは機能しますが、このアプローチには欠点があります。受信メールアダプターを通過した後、メッセージはフィルタリングされるため、そのようなメッセージはすべて既読(SEEN)または未読(should-mark-messages-as-read 属性の値に応じて)としてマークされます。ただし、実際には、メッセージがフィルタリング条件に合格した場合にのみ、SEEN としてメッセージをマークする方が便利です。これは、プレビューペインですべてのメッセージをスクロールしながらメールクライアントを表示するのと似ていますが、実際に開かれて SEEN として読み取られたメッセージにのみフラグを付けます。

Spring Integration 2.0.4 は、inbound-channel-adapter および imap-idle-channel-adapter に mail-filter-expression 属性を導入しました。この属性を使用すると、SpEL と正規表現を組み合わせた式を提供できます。たとえば、件名に "Spring Integration" を含むメールのみを読みたい場合は、mail-filter-expression 属性を mail-filter-expression="subject matches '(?i).Spring Integration." のように構成します。

javax.mail.internet.MimeMessage は SpEL 評価コンテキストのルートコンテキストであるため、メッセージの実際の本文を含む MimeMessage を介して利用可能な任意の値でフィルタリングできます。これは特に重要です。メッセージの本文を読み取ると、通常、そのようなメッセージはデフォルトで SEEN としてマークされるためです。ただし、すべての受信メッセージの PEEK フラグを "true" に設定しているため、SEEN として明示的にマークされたメッセージのみが既読としてマークされます。

そのため、次の例では、フィルター式に一致するメッセージのみがこのアダプターによって出力され、それらのメッセージのみが既読としてマークされます。

<int-mail:imap-idle-channel-adapter id="customAdapter"
	store-uri="imaps://some_google_address:${password}@imap.gmail.com/INBOX"
	channel="receiveChannel"
	should-mark-messages-as-read="true"
	java-mail-properties="javaMailProperties"
	mail-filter-expression="subject matches '(?i).*Spring Integration.*'"/>

上記の例では、mail-filter-expression 属性のおかげで、件名行に "Spring Integration" を含むメッセージのみがこのアダプターによって生成されます。

別の妥当な質問は、次のポーリングまたはアイドルイベントで何が起こるか、そのようなアダプターが再起動されたときに何が起こるかです。フィルタリングするマッサージの重複はありますか? つまり、最後の取得時に、5 つの新しいメッセージがあり、1 つだけがフィルターを通過した場合、他の 4 つのメッセージはどうなるでしょうか? 次のポーリングまたはアイドルで再びフィルタリングロジックを使用しますか? 結局、それらは SEEN としてマークされていませんでした。答えはいいえだ。メールサーバーによって設定され、Spring Integration メール検索フィルターによって使用される別のフラグ(RECENT)により、重複処理の対象にはなりません。フォルダー実装はこのフラグを設定して、このメッセージがこのフォルダーにとって新しいものであることを示します。つまり、このフォルダーが最後に開かれてから届きました。言い換えれば、アダプターはメールを覗き込むかもしれませんが、メールサーバーはそのようなメールがタッチされたため、メールサーバーによって RECENT としてマークされる必要があることも通知します。

トランザクションの同期

受信アダプターのトランザクション同期により、トランザクションがコミットまたはロールバックした後、さまざまなアクションを実行できます。<transactional/> 要素を、ポーリングされた <inbound-adapter/> のポーラーまたは <imap-idle-inbound-adapter/> に追加することにより、トランザクションの同期を有効にできます。「実際の」トランザクションが関与していない場合でも、PseudoTransactionManager を <transactional/> 要素とともに使用することにより、この機能を有効にできます。詳細については、トランザクションの同期を参照してください。

多くの異なるメールサーバーと、特にいくつかの制限があるため、現時点では、これらのトランザクション同期の戦略のみを提供します。メッセージを他の Spring Integration コンポーネントに送信するか、カスタム Bean を呼び出して何らかのアクションを実行できます。例: トランザクションのコミット後に IMAP メッセージを別のフォルダーに移動するには、次のようなものを使用できます。

<int-mail:imap-idle-channel-adapter id="customAdapter"
    store-uri="imaps://something.com:[email protected] (英語)  /INBOX"
    channel="receiveChannel"
    auto-startup="true"
    should-delete-messages="false"
    java-mail-properties="javaMailProperties">
    <int:transactional synchronization-factory="syncFactory"/>
</int-mail:imap-idle-channel-adapter>

<int:transaction-synchronization-factory id="syncFactory">
    <int:after-commit expression="@syncProcessor.process(payload)"/>
</int:transaction-synchronization-factory>

<bean id="syncProcessor" class="thing1.thing2.Mover"/>

次の例は、Mover クラスがどのように見えるかを示しています。

public class Mover {

    public void process(MimeMessage message) throws Exception{
        Folder folder = message.getFolder();
        folder.open(Folder.READ_WRITE);
        String messageId = message.getMessageID();
        Message[] messages = folder.getMessages();
        FetchProfile contentsProfile = new FetchProfile();
        contentsProfile.add(FetchProfile.Item.ENVELOPE);
        contentsProfile.add(FetchProfile.Item.CONTENT_INFO);
        contentsProfile.add(FetchProfile.Item.FLAGS);
        folder.fetch(messages, contentsProfile);
        // find this message and mark for deletion
        for (int i = 0; i < messages.length; i++) {
            if (((MimeMessage) messages[i]).getMessageID().equals(messageId)) {
                messages[i].setFlag(Flags.Flag.DELETED, true);
                break;
            }
        }

        Folder somethingFolder = store.getFolder("SOMETHING"));
        somethingFolder.appendMessages(new MimeMessage[]{message});
        folder.expunge();
        folder.close(true);
        somethingFolder.close(false);
    }
}
トランザクション後もメッセージを操作できるようにするには、should-delete-messages を "false" に設定する必要があります。

Java DSL を使用したチャネルアダプターの構成

Java DSL でメールメールコンポーネントを構成するために、フレームワークは o.s.i.mail.dsl.Mail ファクトリを提供します。これは次のように使用できます。

@SpringBootApplication
public class MailApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(MailApplication.class)
            .web(false)
            .run(args);
    }

    @Bean
    public IntegrationFlow imapMailFlow() {
        return IntegrationFlows
                .from(Mail.imapInboundAdapter("imap://user:pw@host:port/INBOX")
                            .searchTermStrategy(this::fromAndNotSeenTerm)
                            .userFlag("testSIUserFlag")
                            .simpleContent(true)
                            .javaMailProperties(p -> p.put("mail.debug", "false")),
                    e -> e.autoStartup(true)
                            .poller(p -> p.fixedDelay(1000)))
                .channel(MessageChannels.queue("imapChannel"))
                .get();
    }

    @Bean
    public IntegrationFlow sendMailFlow() {
        return IntegrationFlows.from("sendMailChannel")
                .enrichHeaders(Mail.headers()
                        .subjectFunction(m -> "foo")
                        .from("foo@bar")
                        .toFunction(m -> new String[] { "bar@baz" }))
                .handle(Mail.outboundAdapter("gmail")
                            .port(smtpServer.getPort())
                            .credentials("user", "pw")
                            .protocol("smtp")),
                    e -> e.id("sendMailEndpoint"))
                .get();
    }
}