ファイルを読む

FileReadingMessageSource を使用して、ファイルシステムからファイルを消費できます。これは、ファイルシステムディレクトリからメッセージを作成する MessageSource の実装です。次の例は、FileReadingMessageSource を構成する方法を示しています。

<bean id="pollableFileSource"
    class="org.springframework.integration.file.FileReadingMessageSource"
    p:directory="${input.directory}"/>

特定のファイルのメッセージを作成しないようにするには、FileListFilter を指定できます。デフォルトでは、次のフィルターを使用します。

  • IgnoreHiddenFileListFilter

  • AcceptOnceFileListFilter

IgnoreHiddenFileListFilter は、隠しファイルが処理されないようにします。hidden の正確な定義はシステムに依存することに注意してください。例: UNIX ベースのシステムでは、ピリオド文字で始まるファイルは非表示と見なされます。一方、Microsoft Windows には、隠しファイルを示す専用のファイル属性があります。

バージョン 4.2 は IgnoreHiddenFileListFilter を導入しました。以前のバージョンでは、隠しファイルが含まれていました。デフォルト構成では、IgnoreHiddenFileListFilter が最初にトリガーされ、次に AcceptOnceFileListFilter がトリガーされます。

AcceptOnceFileListFilter は、ファイルがディレクトリから 1 回だけピックアップされるようにします。

AcceptOnceFileListFilter はその状態をメモリに保存します。システムの再起動後も状態を維持したい場合は、FileSystemPersistentAcceptOnceFileListFilter を使用できます。このフィルターは、受け入れられたファイル名を MetadataStore 実装に保管します(メタデータストアを参照)。このフィルターは、ファイル名と変更時刻で一致します。

バージョン 4.0 以降、このフィルターには ConcurrentMetadataStore が必要です。共有データストア(Redis と RedisMetadataStore など)で使用すると、フィルターキーを複数のアプリケーションインスタンス間または複数のサーバーで使用されているネットワークファイル共有間で共有できます。

バージョン 4.1.5 以降、このフィルターには新しいプロパティ(flushOnUpdate)があり、更新ごとにメタデータストアをフラッシュします(ストアが Flushable を実装している場合)。

永続ファイルリストフィルターにブールプロパティ forRecursion が追加されました。このプロパティを true に設定すると、alwaysAcceptDirectories も設定されます。これは、送信ゲートウェイ(ls および mget)での再帰操作が常にディレクトリツリー全体を毎回トラバースすることを意味します。これは、ディレクトリツリーの奥深くで変更が検出されなかった問題を解決するためです。さらに、forRecursion=true により、ファイルへのフルパスがメタデータストアキーとして使用されます。これにより、同じ名前のファイルが異なるディレクトリに複数回表示された場合にフィルターが正しく機能しなかった問題が解決されます。重要: これは、永続メタデータストア内の既存のキーが、最上位ディレクトリにあるファイルで見つからないことを意味します。このため、プロパティはデフォルトで false です。これは将来のリリースで変更される可能性があります。

次の例では、フィルターを使用して FileReadingMessageSource を構成します。

<bean id="pollableFileSource"
    class="org.springframework.integration.file.FileReadingMessageSource"
    p:inputDirectory="${input.directory}"
    p:filter-ref="customFilterBean"/>

ファイルの読み取りに関する一般的な問題は、準備が整う前にファイルが検出される可能性があることです(つまり、他のプロセスがまだファイルを書き込んでいる可能性があります)。デフォルトの AcceptOnceFileListFilter はこれを妨げません。ほとんどの場合、ファイル書き込みプロセスが読み取りの準備ができるとすぐに各ファイルの名前を変更すると、これを防ぐことができます。デフォルトの AcceptOnceFileListFilter で構成された(おそらく既知のサフィックスに基づいた)準備ができているファイルのみを受け入れる filename-pattern または filename-regex フィルターは、この状況に対応します。次の例に示すように、CompositeFileListFilter は構成を有効にします。

<bean id="pollableFileSource"
    class="org.springframework.integration.file.FileReadingMessageSource"
    p:inputDirectory="${input.directory}"
    p:filter-ref="compositeFilter"/>

<bean id="compositeFilter"
    class="org.springframework.integration.file.filters.CompositeFileListFilter">
    <constructor-arg>
        <list>
            <bean class="o.s.i.file.filters.AcceptOnceFileListFilter"/>
            <bean class="o.s.i.file.filters.RegexPatternFileListFilter">
                <constructor-arg value="^test.*$"/>
            </bean>
        </list>
    </constructor-arg>
</bean>

一時的な名前でファイルを作成し、最終的な名前に名前を変更することができない場合、Spring Integration は別の代替手段を提供します。バージョン 4.2 は LastModifiedFileListFilter を追加しました。このフィルターは、この値より古いファイルのみがフィルターによって渡されるように、age プロパティで構成できます。経過時間のデフォルトは 60 秒ですが、ファイルの早期取得を回避するのに十分な大きさの経過時間を選択する必要があります(ネットワークの不具合などによる)。次の例は、LastModifiedFileListFilter を構成する方法を示しています。

<bean id="filter" class="org.springframework.integration.file.filters.LastModifiedFileListFilter">
    <property name="age" value="120" />
</bean>

バージョン 4.3.7 から、ChainFileListFilter (CompositeFileListFilter の拡張)が導入され、後続のフィルターが前のフィルターの結果のみを表示するシナリオを可能にしました。(CompositeFileListFilter では、すべてのフィルターがすべてのファイルを表示しますが、すべてのフィルターを通過したファイルのみを通過させます)。新しい動作が必要な場所の例は、LastModifiedFileListFilter と AcceptOnceFileListFilter の組み合わせです。一定の時間が経過するまでファイルを受け入れたくない場合です。CompositeFileListFilter では、AcceptOnceFileListFilter は最初のパスですべてのファイルを見るため、後で他のフィルターがパスするときにファイルを渡しません。CompositeFileListFilter アプローチは、パターンフィルターを、ファイル転送が完了したことを示すセカンダリファイルを探すカスタムフィルターと組み合わせる場合に役立ちます。パターンフィルターはプライマリファイル(something.txt など)のみを渡す場合がありますが、「完了」フィルターは(たとえば) something.done が存在するかどうかを確認する必要があります。

ファイル a.txta.doneb.txt があるとします。

パターンフィルターは a.txt と b.txt のみを通過させますが、「完了」フィルターは 3 つのファイルすべてを認識し、a.txt のみを通過させます。複合フィルターの最終結果は、a.txt のみがリリースされることです。

ChainFileListFilter では、チェーンのいずれかのフィルターが空のリストを返す場合、残りのフィルターは呼び出されません。

バージョン 5.0 は、コンテキスト評価ルートオブジェクトとしてファイルに対して SpEL 式を実行する ExpressionFileListFilter を導入しました。この目的のために、次の例に示すように、ファイル処理用のすべての XML コンポーネント(ローカルおよびリモート)と既存の filter 属性が filter-expression オプションとともに提供されています。

<int-file:inbound-channel-adapter
        directory="${inputdir}"
        filter-expression="name matches '.text'"
        auto-startup="false"/>

バージョン 5.0.5 は、拒否されたファイルに関心がある DiscardAwareFileListFilter 実装を導入しました。この目的のために、そのようなフィルター実装には addDiscardCallback(Consumer<File>) を介してコールバックを提供する必要があります。フレームワークでは、この機能は FileReadingMessageSource.WatchServiceDirectoryScanner から LastModifiedFileListFilter と組み合わせて使用されます。通常の DirectoryScanner とは異なり、WatchService はターゲットファイルシステムのイベントに応じて処理するファイルを提供します。これらのファイルで内部キューをポーリングする瞬間に、LastModifiedFileListFilter は、構成された age に比べて若すぎるため、破棄する場合があります。今後の考慮事項のためにファイルを失います。破棄コールバックフックを使用すると、内部キューにファイルを保持できるため、後続のポーリングで age に対してファイルをチェックできます。CompositeFileListFilter は DiscardAwareFileListFilter も実装し、そのすべての DiscardAwareFileListFilter デリゲートに廃棄コールバックを追加します。

CompositeFileListFilter はすべてのデリゲートに対してファイルを照合するため、同じファイルに対して discardCallback が複数回呼び出される場合があります。

バージョン 5.1 以降、FileReadingMessageSource はディレクトリの存在を確認せず、start() が呼び出されるまで(通常は SourcePollingChannelAdapter をラップすることにより)ディレクトリを作成しません。以前は、テストからディレクトリを参照するとき、または後でアクセス許可が適用されるときに、オペレーティングシステムのアクセス許可エラーを防ぐ簡単な方法はありませんでした。

メッセージヘッダー

バージョン 5.0 以降、FileReadingMessageSource は(ポーリングされた File としての payload に加えて)送信 Message に以下のヘッダーを取り込みます。

  • FileHeaders.FILENAME: 送信するファイルの File.getName()。後続の名前変更またはコピーロジックに使用できます。

  • FileHeaders.ORIGINAL_FILEFile オブジェクト自体。通常、このヘッダーは、元の File オブジェクトが失われると、フレームワークコンポーネント(スプリッタートランスフォーマーなど)によって自動的に入力されます。ただし、他のカスタムユースケースとの一貫性と利便性のために、このヘッダーは元のファイルにアクセスできます。

  • FileHeaders.RELATIVE_PATH: スキャンのルートディレクトリに相対的なファイルパスの部分を表すために導入された新しいヘッダー。このヘッダーは、他の場所でソースディレクトリ階層を復元する必要がある場合に役立ちます。この目的のために、このヘッダーを使用するように DefaultFileNameGenerator ( "ファイル名の生成" を参照)を構成できます。

ディレクトリのスキャンとポーリング

FileReadingMessageSource は、ディレクトリからのファイルのメッセージをすぐには生成しません。scanner によって返される「適格ファイル」に内部キューを使用します。scanEachPoll オプションを使用して、内部キューが各ポーリングの最新の入力ディレクトリコンテンツでリフレッシュされるようにします。デフォルト(scanEachPoll = false)では、FileReadingMessageSource はディレクトリを再度スキャンする前にキューを空にします。このデフォルトの動作は、ディレクトリ内の多数のファイルのスキャンを減らすのに特に役立ちます。ただし、カスタムの順序付けが必要な場合は、このフラグを true に設定する効果を考慮することが重要です。ファイルが処理される順序は予想どおりではない場合があります。デフォルトでは、キュー内のファイルは自然な(path)順序で処理されます。スキャンによって追加された新しいファイルは、キューにすでにファイルがある場合でも、適切な位置に挿入され、その自然な順序が維持されます。順序をカスタマイズするために、FileReadingMessageSource は Comparator<File> をコンストラクター引数として受け入れることができます。内部(PriorityBlockingQueue)によって使用され、ビジネス要件に従ってコンテンツを並べ替えます。特定の順序でファイルを処理するには、カスタム DirectoryScanner によって作成されたリストを並べ替えるのではなく、FileReadingMessageSource にコンパレーターを提供する必要があります。

バージョン 5.0 では、ファイルツリー訪問を実行するために RecursiveDirectoryScanner を導入しました。実装は、Files.walk(Path start, int maxDepth, FileVisitOption…​ options) 機能に基づいています。ルートディレクトリ(DirectoryScanner.listFiles(File))引数は結果から除外されます。他のすべてのサブディレクトリの包含および除外は、ターゲット FileListFilter 実装に基づいています。例: SimplePatternFileListFilter はデフォルトでディレクトリを除外します。詳細については、AbstractDirectoryAwareFileListFilter (Javadoc) とその実装を参照してください。

バージョン 5.5 以降、Java DSL の FileInboundChannelAdapterSpec には、デフォルトの FileReadingMessageSource の代わりにターゲット FileReadingMessageSource で RecursiveDirectoryScanner を使用するための便利な recursive(boolean) オプションがあります。

名前空間サポート

ファイル固有の名前空間を使用すると、ファイル読み取りの構成を簡素化できます。そのためには、次のテンプレートを使用します。

<?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:int-file="http://www.springframework.org/schema/integration/file"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration
    https://www.springframework.org/schema/integration/spring-integration.xsd
    http://www.springframework.org/schema/integration/file
    https://www.springframework.org/schema/integration/file/spring-integration-file.xsd">
</beans>

この名前空間内で、次のように FileReadingMessageSource を減らして受信チャネルアダプターにラップできます。

<int-file:inbound-channel-adapter id="filesIn1"
    directory="file:${input.directory}" prevent-duplicates="true" ignore-hidden="true"/>

<int-file:inbound-channel-adapter id="filesIn2"
    directory="file:${input.directory}"
    filter="customFilterBean" />

<int-file:inbound-channel-adapter id="filesIn3"
    directory="file:${input.directory}"
    filename-pattern="test*" />

<int-file:inbound-channel-adapter id="filesIn4"
    directory="file:${input.directory}"
    filename-regex="test[0-9]+\.txt" />

最初のチャネルアダプターの例は、デフォルトの FileListFilter 実装に依存しています。

  • IgnoreHiddenFileListFilter (隠しファイルを処理しません)

  • AcceptOnceFileListFilter (重複を防ぐ)

prevent-duplicates および ignore-hidden 属性はデフォルトで true であるため、省略することもできます。

Spring Integration 4.2 は ignore-hidden 属性を導入しました。以前のバージョンでは、隠しファイルが含まれていました。

2 番目のチャネルアダプターの例はカスタムフィルターを使用し、3 番目は filename-pattern 属性を使用して AntPathMatcher ベースのフィルターを追加し、4 番目は filename-regex 属性を使用して正規表現パターンベースのフィルターを FileReadingMessageSource に追加します。filename-pattern および filename-regex 属性は、それぞれ通常の filter 参照属性と相互に排他的です。ただし、filter 属性を使用して、特定のニーズに合わせて 1 つ以上のパターンベースのフィルターを含む、任意の数のフィルターを組み合わせた CompositeFileListFilter のインスタンスを参照できます。

複数のプロセスが同じディレクトリから読み取る場合、ファイルが同時に取得されないようにファイルをロックすることができます。そのためには、FileLocker を使用できます。java.nio ベースの実装が利用可能ですが、独自のロックスキームを実装することもできます。nio ロッカーは、次のように挿入できます。

<int-file:inbound-channel-adapter id="filesIn"
    directory="file:${input.directory}" prevent-duplicates="true">
    <int-file:nio-locker/>
</int-file:inbound-channel-adapter>

次のようにカスタムロッカーを設定できます。

<int-file:inbound-channel-adapter id="filesIn"
    directory="file:${input.directory}" prevent-duplicates="true">
    <int-file:locker ref="customLocker"/>
</int-file:inbound-channel-adapter>
ファイル受信アダプターがロッカーで構成されている場合、ファイルの受信を許可する前にロックを取得する責任があります。ファイルのロックを解除する責任は負いません。ファイルを処理し、ロックをぶら下げたままにしておくと、メモリリークが発生します。これが問題になる場合は、適切なタイミングで自分で FileLocker.unlock(File file) を呼び出す必要があります。

ファイルのフィルタリングとロックだけでは不十分な場合は、ファイルを完全にリストする方法を制御する必要があります。この型の要件を実装するには、DirectoryScanner の実装を使用できます。このスキャナーを使用すると、各ポーリングでリストされるファイルを正確に判別できます。これは、Spring Integration が FileListFilter インスタンスと FileLocker を FileReadingMessageSource にワイヤリングするために内部的に使用するインターフェースでもあります。次の例に示すように、scanner 属性の <int-file:inbound-channel-adapter/> にカスタム DirectoryScanner を挿入できます。

<int-file:inbound-channel-adapter id="filesIn" directory="file:${input.directory}"
     scanner="customDirectoryScanner"/>

これにより、順序付け、リスト、ロックの戦略を自由に選択できます。

また、フィルター(patternsregexprevent-duplicates などを含む)および locker インスタンスが実際に scanner によって使用されることを理解することも重要です。アダプターに設定されたこれらの属性のいずれかは、その後内部 scanner に注入されます。外部 scanner の場合、FileReadingMessageSource ではすべてのフィルターおよびロッカー属性が禁止されています。カスタム DirectoryScanner で指定する必要があります(必要な場合)。つまり、scanner を FileReadingMessageSource に挿入する場合、FileReadingMessageSource ではなく、その scanner で filter および locker を指定する必要があります。

デフォルトでは、DefaultDirectoryScanner は IgnoreHiddenFileListFilter と AcceptOnceFileListFilter を使用します。使用しないようにするには、独自のフィルター(AcceptAllFileListFilter など)を構成するか、null に設定することもできます。

WatchServiceDirectoryScanner

FileReadingMessageSource.WatchServiceDirectoryScanner は、新しいファイルがディレクトリに追加されるとき、ファイルシステムイベントに依存します。初期化中に、ディレクトリはイベントを生成するために登録されます。初期ファイルリストも初期化中に作成されます。ディレクトリツリーをたどっていくと、発生したサブディレクトリも登録され、イベントが生成されます。最初のポーリングでは、ディレクトリ内を移動した最初のファイルリストが返されます。後続のポーリングでは、新規作成イベントからのファイルが返されます。新しいサブディレクトリが追加されると、その作成イベントを使用して新しいサブツリーをたどって既存のファイルを見つけ、見つかった新しいサブディレクトリを登録します。

WatchKey の内部イベント queue が、ディレクトリ変更イベントが発生するほど迅速にプログラムによって排出されない場合、WatchKey に課題があります。キューサイズを超えると、一部のファイルシステムイベントが失われる可能性があることを示す StandardWatchEventKinds.OVERFLOW が発行されます。この場合、ルートディレクトリは完全に再スキャンされます。重複を避けるため、適切な FileListFilter (AcceptOnceFileListFilter など)を使用するか、処理が完了したらファイルを削除することを検討してください。

WatchServiceDirectoryScanner は、scanner オプションと相互に排他的な FileReadingMessageSource.use-watch-service オプションによって有効にできます。提供された directory の内部 FileReadingMessageSource.WatchServiceDirectoryScanner インスタンスが読み込まれます。

さらに、WatchService ポーリングロジックは StandardWatchEventKinds.ENTRY_MODIFY および StandardWatchEventKinds.ENTRY_DELETE を追跡できるようになりました。

既存のファイルと新しいファイルの変更を追跡する必要がある場合は、FileListFilter に ENTRY_MODIFY イベントロジックを実装する必要があります。それ以外の場合、それらのイベントのファイルは同じように扱われます。

ResettableFileListFilter 実装は、ENTRY_DELETE イベントを取得します。その結果、それらのファイルは remove() 操作用に提供されます。このイベントを有効にすると、AcceptOnceFileListFilter などのフィルターによってファイルが削除されます。その結果、同じ名前のファイルが表示された場合、そのファイルはフィルターを通過し、メッセージとして送信されます。

この目的のために、watch-events プロパティ(FileReadingMessageSource.setWatchEvents(WatchEventType…​ watchEvents))が導入されました。(WatchEventType は FileReadingMessageSource のパブリック内部列挙です)このようなオプションを使用すると、新しいファイルに 1 つのダウンストリームフローロジックを使用し、変更されたファイルに他のロジックを使用できます。次の例は、同じディレクトリでイベントを作成および変更するためのさまざまなロジックを構成する方法を示しています。

ENTRY_DELETE イベントは、監視対象ディレクトリのサブディレクトリの名前変更操作に関係していることに注意してください。具体的には、以前のディレクトリ名に関連する ENTRY_DELETE イベントが、新しい (名前が変更された) ディレクトリを通知する ENTRY_CREATE イベントよりも前に発生します。一部のオペレーティングシステム (Windows など) では、その状況に対処するために ENTRY_DELETE イベントを登録する必要があります。そうしないと、ファイルエクスプローラーで監視サブディレクトリの名前を変更すると、そのサブディレクトリで新しいファイルが検出されなくなる可能性があります。

<int-file:inbound-channel-adapter id="newFiles"
     directory="${input.directory}"
     use-watch-service="true"/>

<int-file:inbound-channel-adapter id="modifiedFiles"
     directory="${input.directory}"
     use-watch-service="true"
     filter="acceptAllFilter"
     watch-events="MODIFY"/> <!-- The default is CREATE. -->

バージョン 6.1 以降、FileReadingMessageSource は 2 つの新しい WatchService 関連オプションを公開します。

  • watchMaxDepth - Files.walkFileTree(Path root, Set attributes, int maxDepth, FileVisitor visitor) API の引数。

  • watchDirPredicate - Predicate<Path> は、スキャンされたツリー内のディレクトリを探索し、WatchService および構成された監視イベントの種類に登録する必要があるかどうかをテストします。

メモリ消費の制限

HeadDirectoryScanner を使用して、メモリに保持されるファイルの数を制限できます。これは、大きなディレクトリをスキャンするときに役立ちます。XML 構成では、受信チャネルアダプターの queue-size プロパティを設定することでこれを有効にします。

バージョン 4.2 より前は、この設定は他のフィルターの使用と互換性がありませんでした。他のフィルター(prevent-duplicates="true" を含む)は、サイズを制限するために使用されるフィルターを上書きしました。

HeadDirectoryScanner の使用は AcceptOnceFileListFilter と互換性がありません。ポーリングの決定中にすべてのフィルターが調べられるため、AcceptOnceFileListFilter は他のフィルターが一時的にファイルをフィルターしている可能性があることを知りません。以前に HeadDirectoryScanner.HeadFilter によってフィルタリングされたファイルが利用可能になった場合でも、AcceptOnceFileListFilter はそれらをフィルタリングします。

通常、この場合は AcceptOnceFileListFilter を使用する代わりに、処理済みのファイルを削除して、以前にフィルタリングされたファイルが今後の投票で利用できるようにする必要があります。

Java 構成を使用した構成

次の Spring Boot アプリケーションは、Java 構成で送信アダプターを構成する方法の例を示しています。

@SpringBootApplication
public class FileReadingJavaApplication {

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

    @Bean
    public MessageChannel fileInputChannel() {
        return new DirectChannel();
    }

    @Bean
    @InboundChannelAdapter(value = "fileInputChannel", poller = @Poller(fixedDelay = "1000"))
    public MessageSource<File> fileReadingMessageSource() {
         FileReadingMessageSource source = new FileReadingMessageSource();
         source.setDirectory(new File(INBOUND_PATH));
         source.setFilter(new SimplePatternFileListFilter("*.txt"));
         return source;
    }

    @Bean
    @Transformer(inputChannel = "fileInputChannel", outputChannel = "processFileChannel")
    public FileToStringTransformer fileToStringTransformer() {
        return new FileToStringTransformer();
    }

}

Java DSL を使用した構成

次の Spring Boot アプリケーションは、Java DSL で送信アダプターを構成する方法の例を示しています。

@SpringBootApplication
public class FileReadingJavaApplication {

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

    @Bean
    public IntegrationFlow fileReadingFlow() {
         return IntegrationFlow
                  .from(Files.inboundAdapter(new File(INBOUND_PATH))
                              .patternFilter("*.txt"),
                          e -> e.poller(Pollers.fixedDelay(1000)))
                  .transform(Files.toStringTransformer())
                  .channel("processFileChannel")
                  .get();
    }

}

"tail" ファイル

もう 1 つの一般的な使用例は、ファイルの最後 (または末尾) から「行」を取得し、新しい行が追加されたときにそれをキャプチャーすることです。2 つの実装が提供されています。最初の OSDelegatingFileTailingMessageProducer は、ネイティブの tail コマンドを使用します (コマンドがあるオペレーティングシステムの場合)。これは通常、これらのプラットフォームで最も効率的な実装です。tail コマンドがないオペレーティングシステムの場合、2 番目の実装 ApacheCommonsFileTailingMessageProducer は Apache commons-io Tailer クラスを使用します。

どちらの場合も、利用できないファイルやその他のイベントなどのファイルシステムイベントは、通常の Spring イベント発行メカニズムを使用して ApplicationEvent インスタンスとして発行されます。このようなイベントの例には次のものがあります。

[message=tail: cannot open '/tmp/somefile' for reading:
               No such file or directory, file=/tmp/somefile]

[message=tail: '/tmp/somefile' has become accessible, file=/tmp/somefile]

[message=tail: '/tmp/somefile' has become inaccessible:
               No such file or directory, file=/tmp/somefile]

[message=tail: '/tmp/somefile' has appeared;
               following end of new file, file=/tmp/somefile]

前の例に示されている一連のイベントは、たとえば、ファイルがローテーションされるときに発生する可能性があります。

バージョン 5.0 以降、idleEventInterval の実行中にファイルにデータがない場合、FileTailingIdleEvent が発行されます。次の例は、そのようなイベントがどのように見えるかを示しています。

[message=Idle timeout, file=/tmp/somefile] [idle time=5438]
tail コマンドをサポートするすべてのプラットフォームがこれらのステータスメッセージを提供するわけではありません。

これらのエンドポイントから発信されるメッセージには、次のヘッダーがあります。

  • FileHeaders.ORIGINAL_FILEFile オブジェクト

  • FileHeaders.FILENAME: ファイル名 (File.getName())

バージョン 5.0 より前のバージョンでは、FileHeaders.FILENAME ヘッダーにはファイルの絶対パスの文字列表現が含まれていました。これで、元のファイルヘッダーで getAbsolutePath() を呼び出して、その文字列表現を取得できます。

次の例では、デフォルトのオプション( "-F -n 0"、現在の末尾からファイル名を追跡することを意味する)でネイティブアダプターを作成します。

<int-file:tail-inbound-channel-adapter id="native"
	channel="input"
	task-executor="exec"
	file="/tmp/foo"/>

次の例では、"-F -n +0" オプションを使用してネイティブアダプターを作成します(つまり、ファイル名に従い、既存のすべての行を出力します)。

<int-file:tail-inbound-channel-adapter id="native"
	channel="input"
	native-options="-F -n +0"
	task-executor="exec"
	file-delay=10000
	file="/tmp/foo"/>

tail コマンドが失敗する場合(プラットフォームによっては、-F が指定されている場合でもファイルの欠落により tail が失敗する場合)、コマンドは 10 秒ごとに再試行されます。

デフォルトでは、ネイティブアダプターは標準出力からキャプチャーし、コンテンツをメッセージとして送信します。また、標準エラーからキャプチャーしてイベントを発生させます。バージョン 4.3.6 以降、次の例に示すように、enable-status-reader を false に設定することにより、標準エラーイベントを破棄できます。

<int-file:tail-inbound-channel-adapter id="native"
	channel="input"
	enable-status-reader="false"
	task-executor="exec"
	file="/tmp/foo"/>

次の例では、IdleEventInterval は 5000 に設定されています。つまり、5 秒間行が書き込まれない場合、FileTailingIdleEvent は 5 秒ごとにトリガーされます。

<int-file:tail-inbound-channel-adapter id="native"
	channel="input"
	idle-event-interval="5000"
	task-executor="exec"
	file="/tmp/somefile"/>

これは、アダプターを停止する必要がある場合に役立ちます。

次の例では、2 秒ごとにファイルの新しい行を調べ、10 秒ごとに不足しているファイルの存在を確認する Apache commons-io Tailer アダプターを作成します。

<int-file:tail-inbound-channel-adapter id="apache"
	channel="input"
	task-executor="exec"
	file="/tmp/bar"
	delay="2000"
	end="false"             (1)
	reopen="true"           (2)
	file-delay="10000"/>
1 ファイルは、末尾(デフォルト)ではなく先頭(end="false")から末尾に付けられます。
2 ファイルはチャンクごとに再度開かれます(デフォルトではファイルを開いたままにします)。
delayendreopen 属性を指定すると、Apache commons-io アダプターの使用が強制され、native-options 属性は使用できなくなります。

不完全なデータの処理

ファイル転送シナリオの一般的な問題は、転送が完了したことを確認して、不完全なファイルの読み取りを開始しないようにする方法です。この問題を解決する一般的な方法は、一時的な名前でファイルを作成し、アトミックに名前を最終的な名前に変更することです。この技術は、一時ファイルがコンシューマーによって選択されないようにマスクするフィルターとともに、堅牢なソリューションを提供します。この手法は、ファイルを(ローカルまたはリモートで)書き込む Spring Integration コンポーネントによって使用されます。デフォルトでは、ファイル名に .writing を追加し、転送が完了したら削除します。

もう 1 つの一般的な手法は、2 番目の「マーカー」ファイルを作成して、ファイル転送が完了したことを示すことです。このシナリオでは、somefile.txt.complete も存在するまで、somefile.txt (たとえば)が使用可能であると見なすべきではありません。Spring Integration バージョン 5.0 は、このメカニズムをサポートする新しいフィルターを導入しました。ファイルシステム(FileSystemMarkerFilePresentFileListFilter)、FTP および SFTP の実装が提供されています。マーカーファイルには任意の名前を付けることができますが、通常は転送されるファイルに関連しています。詳細については、Javadoc を参照してください。