メールサポート
このセクションでは、Spring Integration でメールメッセージを操作する方法について説明します。
この依存関係をプロジェクトに含める必要があります。
jakarta.mail:jakarta.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");
メールを受信するためのもう 1 つのオプションは、IMAP idle
コマンドです (メールサーバーでサポートされている場合)。Spring Integration は、それ自体がメッセージ生成エンドポイントである ImapIdleChannelAdapter
を提供します。ImapMailReceiver
のインスタンスに委譲します。次のセクションでは、「メール」スキーマでの Spring Integration の名前空間サポートを使用して両方の型の受信チャネルアダプターを構成する例を示します。
通常、 |
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
です。
バージョン 5.5.11 以降、メッセージが受信されなかった場合、autoCloseFolder
フラグとは関係なくすべてのメッセージがフィルターで除外された場合、AbstractMailReceiver.receive()
の後にフォルダーは自動的に閉じられます。この場合、IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE
ヘッダー周辺の可能なロジックのダウンストリームを生成するものはありません。
バージョン 6.0.5 以降、ImapIdleChannelAdapter
は非同期メッセージ発行を実行しなくなりました。これは、メールフォルダーを開いたままにする必要があるため、ダウンストリームのメッセージ処理 (大きな接続ファイルなど) のアイドルリスナーループをブロックするために必要です。非同期ハンドオフが必要な場合は、ExecutorChannel
をこのチャネルアダプターの出力チャネルとして使用できます。
受信メールメッセージマッピング
デフォルトでは、受信アダプターによって生成されるメッセージのペイロードは生の MimeMessage
です。そのオブジェクトを使用して、ヘッダーとコンテンツを調べることができます。バージョン 4.3 から、HeaderMapper<MimeMessage>
を提供してヘッダーを MessageHeaders
にマップできます。便宜上、Spring Integration はこの目的のために DefaultMailHeaderMapper
を提供します。次のヘッダーをマップします。
mail_from
:from
アドレスのString
表現。mail_bcc
:bcc
アドレスを含むString
配列。mail_cc
:cc
アドレスを含むString
配列。mail_to
:to
アドレスを含むString
配列。mail_replyTo
:replyTo
アドレスのString
表現。mail_subject
: メールの件名。mail_lineCount
: 行数(使用可能な場合)。mail_receivedDate
: 受信日(利用可能な場合)。mail_size
: メールのサイズ(利用可能な場合)。mail_expunged
: メッセージが削除されたかどうかを示すブール値。mail_raw
: すべてのメールヘッダーとその値を含むMultiValueMap
。mail_contentType
: 元のメールメッセージのコンテンツ型。contentType
: ペイロードコンテンツ型(以下を参照)。
メッセージマッピングが有効な場合、ペイロードはメールメッセージとその実装に依存します。通常、メールの内容は、MimeMessage
内の DataHandler
によってレンダリングされます。
text/*
メールの場合、ペイロードは String
であり、contentType
ヘッダーは mail_contentType
と同じです。
jakarta.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[]
としてレンダリングされます。
ヘッダーマッパーを指定しない場合、メッセージペイロードは jakarta.mail
によって提示される MimeMessage
です。フレームワークは、メールの内容を String
に変換する戦略を使用してメッセージを変換するために使用できる MailToStringTransformer
を提供します。
Java DSL
Java
Kotlin
XML
...
.transform(Mail.toStringTransformer())
...
@Bean
@Transformer(inputChannel="...", outputChannel="...")
public Transformer transformer() {
return new MailToStringTransformer();
}
...
transform(Mail.toStringTransformer())
...
<int-mail:mail-to-string-transformer ... >
バージョン 4.3 以降、トランスフォーマーは組み込み Part
インスタンス(および以前に処理された Multipart
インスタンス)を処理します。トランスフォーマーは、上記のリストのアドレスおよびサブジェクトヘッダーをマップする AbstractMailTransformer
のサブクラスです。メッセージに対して他の変換を実行する場合は、AbstractMailTransformer
のサブクラス化を検討してください。
バージョン 5.4 以降、headerMapper
が提供されず、autoCloseFolder
が false
であり、simpleContent
が false
である場合、生成された Spring メッセージのペイロードで MimeMessage
がそのまま返されます。このようにして、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
が提供されている場合、host
、username
および mail-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
名前空間を使用します。
ユーザー名に @ 文字が含まれている場合は、基盤となる JavaMail API からの解析エラーを回避するために、@ ではなく %40 を使用します。 |
次の例は、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
に基づいてメッセージを検索します。これは、以下のすべてのメールメッセージです。
最近 (サポートされている場合)
回答されていません
削除されません
見えない
hHave はこのメール受信者によって処理されていません (カスタム 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 メールレシーバーは、指定されている場合、 |
IMAP idle
と失われた接続
IMAP idle
チャネルアダプターを使用する場合、サーバーへの接続が失われる可能性があります (たとえば、ネットワーク障害によって)。JavaMail のドキュメントでは、実際の IMAP API は実験的であると明示的に述べられているため、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 にするべきだと主張する人もいるかもしれません。言い換えれば、有効な 2 つの使用箇所が相互に排他的であるため、最適なデフォルトを 1 つ選択することが非常に困難になります。アダプターを唯一のメール受信者として構成することもできます。その場合、以前に配信されたメッセージが再度配信されないことを心配せずにアダプターを再起動できるようにする必要があります。この場合、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
の設定に関係なく適用されます。ただし、バージョン 6.4 以降では、\Flagged
も無効にできます。AbstractMailReceiver
は、\Flagged
の設定をスキップする setFlaggedAsFallback(boolean flaggedAsFallback)
オプションを公開します。シナリオによっては、\Recent
またはユーザーフラグがサポートされていないかどうかに関係なく、メールボックス内のメッセージにこのようなフラグを設定することは望ましくありません。
SearchTerm
で説明したように、デフォルトの 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.*"
のように構成します。
jakarta.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 IntegrationFlow
.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 IntegrationFlow.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();
}
}