Files
2023-09-06 08:58:43 +02:00

282 lines
10 KiB
Plaintext

[[runningAJob]]
= Running a Job
At a minimum, launching a batch job requires two things: the
`Job` to be launched and a
`JobLauncher`. Both can be contained within the same
context or different contexts. For example, if you launch jobs from the
command line, a new JVM is instantiated for each `Job`. Thus, every
job has its own `JobLauncher`. However, if
you run from within a web container that is within the scope of an
`HttpRequest`, there is usually one
`JobLauncher` (configured for asynchronous job
launching) that multiple requests invoke to launch their jobs.
[[runningJobsFromCommandLine]]
== Running Jobs from the Command Line
If you want to run your jobs from an enterprise
scheduler, the command line is the primary interface. This is because
most schedulers (with the exception of Quartz, unless using
`NativeJob`) work directly with operating system
processes, primarily kicked off with shell scripts. There are many ways
to launch a Java process besides a shell script, such as Perl, Ruby, or
even build tools, such as Ant or Maven. However, because most people
are familiar with shell scripts, this example focuses on them.
[[commandLineJobRunner]]
=== The CommandLineJobRunner
Because the script launching the job must kick off a Java
Virtual Machine, there needs to be a class with a `main` method to act
as the primary entry point. Spring Batch provides an implementation
that serves this purpose:
`CommandLineJobRunner`. Note
that this is just one way to bootstrap your application. There are
many ways to launch a Java process, and this class should in no way be
viewed as definitive. The `CommandLineJobRunner`
performs four tasks:
* Load the appropriate `ApplicationContext`.
* Parse command line arguments into `JobParameters`.
* Locate the appropriate job based on arguments.
* Use the `JobLauncher` provided in the application context to launch the job.
All of these tasks are accomplished with only the arguments passed in.
The following table describes the required arguments:
.CommandLineJobRunner arguments
|===============
|`jobPath`|The location of the XML file that is used to
create an `ApplicationContext`. This file
should contain everything needed to run the complete
`Job`.
|`jobName`|The name of the job to be run.
|===============
These arguments must be passed in, with the path first and the name second. All arguments
after these are considered to be job parameters, are turned into a `JobParameters` object,
and must be in the format of `name=value`.
[tabs]
====
Java::
+
The following example shows a date passed as a job parameter to a job defined in Java:
+
[source]
----
<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay schedule.date=2007-05-05,java.time.LocalDate
----
XML::
+
The following example shows a date passed as a job parameter to a job defined in XML:
+
[source]
----
<bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay schedule.date=2007-05-05,java.time.LocalDate
----
====
[NOTE]
=====
By default, the `CommandLineJobRunner` uses a `DefaultJobParametersConverter` that implicitly converts
key/value pairs to identifying job parameters. However, you can explicitly specify
which job parameters are identifying and which are not by suffixing them with `true` or `false`, respectively.
In the following example, `schedule.date` is an identifying job parameter, while `vendor.id` is not:
[source]
----
<bash$ java CommandLineJobRunner endOfDayJob.xml endOfDay \
schedule.date=2007-05-05,java.time.LocalDate,true \
vendor.id=123,java.lang.Long,false
----
[source]
----
<bash$ java CommandLineJobRunner io.spring.EndOfDayJobConfiguration endOfDay \
schedule.date=2007-05-05,java.time.LocalDate,true \
vendor.id=123,java.lang.Long,false
----
You can override this behavior by using a custom `JobParametersConverter`.
=====
[tabs]
====
Java::
+
In most cases, you would want to use a manifest to declare your `main` class in a jar. However,
for simplicity, the class was used directly. This example uses the `EndOfDay`
example from the xref:domain.adoc[The Domain Language of Batch]. The first
argument is `io.spring.EndOfDayJobConfiguration`, which is the fully qualified class name
to the configuration class that contains the Job. The second argument, `endOfDay`, represents
the job name. The final argument, `schedule.date=2007-05-05,java.time.LocalDate`, is converted
into a `JobParameter` object of type `java.time.LocalDate`.
+
The following example shows a sample configuration for `endOfDay` in Java:
+
[source, java]
----
@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();
}
}
----
XML::
+
In most cases, you would want to use a manifest to declare your `main` class in a jar. However,
for simplicity, the class was used directly. This example uses the `EndOfDay`
example from the xref:domain.adoc[The Domain Language of Batch]. The first
argument is `endOfDayJob.xml`, which is the Spring ApplicationContext that contains the
`Job`. The second argument, `endOfDay,` represents the job name. The final argument,
`schedule.date=2007-05-05,java.time.LocalDate`, is converted into a `JobParameter` object of type
`java.time.LocalDate`.
+
The following example shows a sample configuration for `endOfDay` in XML:
+
[source, xml]
----
<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" />
----
====
The preceding example is overly simplistic, since there are many more requirements to a
run a batch job in Spring Batch in general, but it serves to show the two main
requirements of the `CommandLineJobRunner`: `Job` and `JobLauncher`.
[[exitCodes]]
=== Exit Codes
When launching a batch job from the command-line, an enterprise
scheduler is often used. Most schedulers are fairly dumb and work only
at the process level. This means that they only know about some
operating system process (such as a shell script that they invoke).
In this scenario, the only way to communicate back to the scheduler
about the success or failure of a job is through return codes. A
return code is a number that is returned to a scheduler by the process
to indicate the result of the run. In the simplest case, 0 is
success and 1 is failure. However, there may be more complex
scenarios, such as "`If job A returns 4, kick off job B, and, if it returns 5, kick
off job C.`" This type of behavior is configured at the scheduler level,
but it is important that a processing framework such as Spring Batch
provide a way to return a numeric representation of the exit code
for a particular batch job. In Spring Batch, this is encapsulated
within an `ExitStatus`, which is covered in more
detail in Chapter 5. For the purposes of discussing exit codes, the
only important thing to know is that an
`ExitStatus` has an exit code property that is
set by the framework (or the developer) and is returned as part of the
`JobExecution` returned from the
`JobLauncher`. The
`CommandLineJobRunner` converts this string value
to a number by using the `ExitCodeMapper`
interface:
[source, java]
----
public interface ExitCodeMapper {
public int intValue(String exitCode);
}
----
The essential contract of an
`ExitCodeMapper` is that, given a string exit
code, a number representation will be returned. The default
implementation used by the job runner is the `SimpleJvmExitCodeMapper`
that returns 0 for completion, 1 for generic errors, and 2 for any job
runner errors such as not being able to find a
`Job` in the provided context. If anything more
complex than the three values above is needed, a custom
implementation of the `ExitCodeMapper` interface
must be supplied. Because the
`CommandLineJobRunner` is the class that creates
an `ApplicationContext` and, thus, cannot be
'wired together', any values that need to be overwritten must be
autowired. This means that if an implementation of
`ExitCodeMapper` is found within the `BeanFactory`,
it is injected into the runner after the context is created. All
that needs to be done to provide your own
`ExitCodeMapper` is to declare the implementation
as a root level bean and ensure that it is part of the
`ApplicationContext` that is loaded by the
runner.
[[runningJobsFromWebContainer]]
== Running Jobs from within a Web Container
Historically, offline processing (such as batch jobs) has been
launched from the command-line, as described earlier. However, there are
many cases where launching from an `HttpRequest` is
a better option. Many such use cases include reporting, ad-hoc job
running, and web application support. Because a batch job (by definition)
is long running, the most important concern is to launch the
job asynchronously:
.Asynchronous Job Launcher Sequence From Web Container
image::launch-from-request.png[Async Job Launcher Sequence from web container, scaledwidth="60%"]
The controller in this case is a Spring MVC controller. See the
Spring Framework Reference Guide for more about https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc[Spring MVC].
The controller launches a `Job` by using a
`JobLauncher` that has been configured to launch
xref:job/running.adoc#runningJobsFromWebContainer[asynchronously], which
immediately returns a `JobExecution`. The
`Job` is likely still running. However, this
nonblocking behavior lets the controller return immediately, which
is required when handling an `HttpRequest`. The following listing
shows an example:
[source, java]
----
@Controller
public class JobLauncherController {
@Autowired
JobLauncher jobLauncher;
@Autowired
Job job;
@RequestMapping("/jobLauncher.html")
public void handle() throws Exception{
jobLauncher.run(job, new JobParameters());
}
}
----