ジョブを実行する

少なくとも、バッチジョブを起動するには、起動する Job と JobLauncher の 2 つが必要です。どちらも同じコンテキストまたは異なるコンテキストに含めることができます。例: コマンドラインからジョブを起動すると、新しい JVM が各 Job に対してインスタンス化されます。すべてのジョブには独自の JobLauncher があります。ただし、HttpRequest のスコープ内にある Web コンテナー内から実行する場合、通常、複数のリクエストがジョブを起動するために呼び出す 1 つの JobLauncher (非同期ジョブ起動用に構成) があります。

コマンドラインからジョブを実行する

エンタープライズスケジューラからジョブを実行する場合は、コマンドラインが主要なインターフェースになります。これは、ほとんどのスケジューラ ( Quartz を除き、NativeJob を使用しない場合) が、主にシェルスクリプトで開始されるオペレーティングシステムプロセスと直接連携するためです。Perl や Ruby などのシェルスクリプトや、Ant や Maven などのビルドツール以外にも、Java プロセスを起動する方法は多数あります。ただし、ほとんどの人はシェルスクリプトに精通しているため、この例ではシェルスクリプトに焦点を当てています。

CommandLineJobRunner

ジョブを起動するスクリプトは Java 仮想マシンを起動する必要があるため、プライマリエントリポイントとして機能する main メソッドを持つクラスが必要です。Spring Batch は、この目的に役立つ実装を提供します: CommandLineJobRunner。これは、アプリケーションをブートストラップする 1 つの方法にすぎないことに注意してください。Java プロセスを起動する方法は多数ありますが、このクラスを決定的なものと見なすべきではありません。CommandLineJobRunner は次の 4 つのタスクを実行します。

  • 適切な ApplicationContext をロードします。

  • コマンドライン引数を JobParameters に解析します。

  • 引数に基づいて適切なジョブを見つけます。

  • アプリケーションコンテキストで提供される JobLauncher を使用して、ジョブを起動します。

これらのタスクはすべて、渡された引数のみで実行されます。次の表に、必要な引数を示します。

表 1: CommandLineJobRunner 引数

jobPath

ApplicationContext の作成に使用される XML ファイルの場所。このファイルには、完全な Job を実行するために必要なすべてが含まれている必要があります。

jobName

実行するジョブの名前。

これらの引数は、最初にパス、2 番目に名前を付けて渡す必要があります。これらの後のすべての引数は、ジョブパラメーターと見なされ、JobParameters オブジェクトに変換され、name=value の形式である必要があります。

  • Java

  • XML

次の例は、Java で定義されたジョブにジョブパラメーターとして渡された日付を示しています。

<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay schedule.date=2007-05-05,java.time.LocalDate

次の例は、ジョブパラメーターとして XML で定義されたジョブに渡される日付を示しています。

<bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay schedule.date=2007-05-05,java.time.LocalDate

デフォルトでは、CommandLineJobRunner は DefaultJobParametersConverter を使用します。この DefaultJobParametersConverter は、キーと値のペアを識別ジョブパラメーターに暗黙的に変換します。ただし、それぞれ true または false の接尾辞を付けることで、識別するジョブパラメーターと識別しないジョブパラメーターを明示的に指定できます。

次の例では、schedule.date は識別ジョブパラメーターですが、vendor.id はそうではありません。

<bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay \
                                 schedule.date=2007-05-05,java.time.LocalDate,true \
                                 vendor.id=123,java.lang.Long,false
<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay \
                                 schedule.date=2007-05-05,java.time.LocalDate,true \
                                 vendor.id=123,java.lang.Long,false

カスタム JobParametersConverter を使用して、この動作をオーバーライドできます。

  • Java

  • XML

ほとんどの場合、マニフェストを使用して jar で main クラスを宣言することをお勧めします。ただし、簡単にするために、クラスを直接使用しました。この例では、バッチのドメイン言語の EndOfDay 例を使用します。最初の引数は io.spring.EndOfDayJobConfiguration で、ジョブを含む構成クラスの完全修飾クラス名です。2 番目の引数 endOfDay はジョブ名を表します。最後の引数 schedule.date=2007-05-05,java.time.LocalDate は、java.time.LocalDate 型の JobParameter オブジェクトに変換されます。

次の例は、Java での endOfDay の構成例を示しています。

@Configuration
@EnableBatchProcessing
public class EndOfDayJobConfiguration {

    @Bean
    public Job endOfDay(JobRepository jobRepository, Step step1) {
        return new JobBuilder("endOfDay", jobRepository)
    				.start(step1)
    				.build();
    }

    @Bean
    public Step step1(JobRepository jobRepository, PlatformTransactionManager transactionManager) {
        return new StepBuilder("step1", jobRepository)
    				.tasklet((contribution, chunkContext) -> null, transactionManager)
    				.build();
    }
}

ほとんどの場合、マニフェストを使用して jar で main クラスを宣言する必要があります。ただし、簡単にするために、クラスを直接使用しました。この例では、バッチのドメイン言語の EndOfDay 例を使用します。最初の引数は endOfDayJob.xml で、これは Job を含む Spring ApplicationContext です。2 番目の引数 endOfDay, はジョブ名を表します。最後の引数 schedule.date=2007-05-05,java.time.LocalDate は、java.time.LocalDate 型の JobParameter オブジェクトに変換されます。

次の例は、XML での endOfDay の構成例を示しています。

<job id="endOfDay">
    <step id="step1" parent="simpleStep" />
</job>

<!-- Launcher details removed for clarity -->
<beans:bean id="jobLauncher"
         class="org.springframework.batch.core.launch.support.TaskExecutorJobLauncher" />

上記の例は、一般に Spring Batch でバッチジョブを実行するための要件が他にもたくさんあるため、非常に単純化されていますが、CommandLineJobRunner の 2 つの主要な要件である Job と JobLauncher を示すのに役立ちます。

終了コード

コマンドラインからバッチジョブを起動する場合、エンタープライズスケジューラがよく使用されます。ほとんどのスケジューラはかなり馬鹿げており、プロセスレベルでしか機能しません。これは、一部のオペレーティングシステムプロセス (呼び出したシェルスクリプトなど) しか認識していないことを意味します。このシナリオでは、ジョブの成功または失敗についてスケジューラに返信する唯一の方法は、リターンコードを使用することです。リターンコードは、実行の結果を示すためにプロセスによってスケジューラに返される番号です。最も単純なケースでは、0 は成功、1 は失敗です。ただし、「ジョブ A が 4 を返す場合はジョブ B を開始し、5 を返す場合はジョブ C を開始する」など、より複雑なシナリオが存在する場合もあります。この型の動作はスケジューラレベルで構成されますが、Spring Batch などの処理フレームワークが特定のバッチジョブの終了コードの数値表現を返す方法を提供することが重要です。Spring Batch では、これは ExitStatus 内にカプセル化されます。これについては、第 5 章で詳しく説明します。終了コードについて説明する目的で知っておくべき唯一の重要なことは、ExitStatus にはフレームワークによって設定される終了コードプロパティがあることです (または開発者) であり、JobLauncher から返される JobExecution の一部として返されます。CommandLineJobRunner は、ExitCodeMapper インターフェースを使用して、この文字列値を数値に変換します。

public interface ExitCodeMapper {

    public int intValue(String exitCode);

}

ExitCodeMapper の本質的な契約は、文字列の終了コードを指定すると、数値表現が返されるということです。ジョブランナーが使用するデフォルトの実装は SimpleJvmExitCodeMapper で、完了の場合は 0、一般的なエラーの場合は 1、指定されたコンテキストで Job が見つからないなどのジョブランナーのエラーの場合は 2 を返します。上記の 3 つの値よりも複雑なものが必要な場合は、ExitCodeMapper インターフェースのカスタム実装を提供する必要があります。CommandLineJobRunner は ApplicationContext を作成するクラスであるため、「一緒に接続」することはできないため、上書きする必要がある値はすべてオートワイヤーする必要があります。これは、ExitCodeMapper の実装が BeanFactory 内で見つかった場合、コンテキストが作成された後にランナーに注入されることを意味します。独自の ExitCodeMapper を提供するために行う必要があるのは、実装をルートレベル Bean として宣言し、それがランナーによってロードされる ApplicationContext の一部であることを確認することだけです。

Web コンテナー内からのジョブの実行

以前は、前述のように、オフライン処理 (バッチジョブなど) はコマンドラインから起動されていました。ただし、HttpRequest からの起動がより適切なオプションである場合が多くあります。このようなユースケースの多くには、レポート、アドホックジョブの実行、および Web アプリケーションのサポートが含まれます。バッチジョブは (定義上) 長時間実行されるため、最も重要な関心事は、ジョブを非同期で起動することです。

Async Job Launcher Sequence from web container
図 1: Web コンテナーからの非同期ジョブランチャーシーケンス

この場合のコントローラーは Spring MVC コントローラーです。Spring MVC の詳細については、Spring Framework リファレンスガイドを参照してください。コントローラーは、非同期で起動するように構成された JobLauncher を使用して Job を起動し、すぐに JobExecution を返します。Job はまだ実行されている可能性があります。ただし、この非ブロック動作により、コントローラーはすぐに戻ることができます。これは、HttpRequest を処理するときに必要です。次のリストは例を示しています。

@Controller
public class JobLauncherController {

    @Autowired
    JobLauncher jobLauncher;

    @Autowired
    Job job;

    @RequestMapping("/jobLauncher.html")
    public void handle() throws Exception{
        jobLauncher.run(job, new JobParameters());
    }
}