From c6437b90b1dcc56bba1d73666d52178094046074 Mon Sep 17 00:00:00 2001 From: lucasward Date: Wed, 28 Jan 2009 23:42:00 +0000 Subject: [PATCH] BATCH-883: Added section in documentation on JobExplorer and JobOperator --- docs/src/site/docbook/reference/job.xml | 257 +++++++++++++++++++++--- 1 file changed, 226 insertions(+), 31 deletions(-) diff --git a/docs/src/site/docbook/reference/job.xml b/docs/src/site/docbook/reference/job.xml index 3637e0fe9..c352b8107 100644 --- a/docs/src/site/docbook/reference/job.xml +++ b/docs/src/site/docbook/reference/job.xml @@ -639,44 +639,245 @@
Advanced Meta-Data Usage - + So far, both the JobLauncher and JobRepository interfaces have been + discussed. Together, they represent simple launching of a job, and basic + CRUD operations of batch domain objects: + + + + + + + + + + + + A JobLauncher uses the + JobRepository to create new + JobExecution objects, and run them. + Job and Step implementations + later use the same JobRepository for basic updates + of the same executions during the running of a Job. + The basic operations suffice for simple scenarios. However, in a large + batch environment with hundreds of batch jobs and complex scheduling + requirements, more advanced access of the meta data is required: + + + + + + + + + + + + The JobExplorer and JobOperatoer interfaces, which will be discussed + below, add additional functionality for querying and controlling the meta + data.
Querying the repository - + The most basic need before any advanced features is the ability to + query the repository for existing executions. This functionality is + provided by the JobExplorer interface: -
- JobExporer + + public interface JobExplorer { - -
+ List<JobInstance> getJobInstances(String jobName, int start, int count); + + JobExecution getJobExecution(Long executionId); + + StepExecution getStepExecution(Long jobExecutionId, Long stepExecutionId); + + JobInstance getJobInstance(Long instanceId); + + List<JobExecution> getJobExecutions(JobInstance jobInstance); + + Set<JobExecution> findRunningJobExecutions(String jobName); + } + + + + As is evident from the method signatures above, + JobExplorer is a read-only version of the + JobRepository, and like the + JobRepository, it can be easily configured via a + factory bean: + + + <bean id="jobExplorer" class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean" + p:dataSource-ref="dataSource" /> + + + + Earlier in this chapter, it was mentioned that the table prefix of + the JobRepository can be modified to allow for different versions or + schemas. Because the JobExplorer is working with the same tables, it too + needs the ability to set a prefix: + + + <bean id="jobExplorer" class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean" + p:dataSource-ref="dataSource" p:tablePrefix="BATCH_" /> + + +
+ +
+ JobOperator + + As previously discussed, the JobRepository + provides CRUD operations on the meta-data, and the + JobExplorer provides read-only operations on the + meta-data. However, those operations are most useful when used together + to perform common monitoring tasks such as stopping, restarting, or + summarizing a Job, as is commonly done by batch operators. Spring Batch + provides for these types of operations via the + JobOperator interface: + + + public interface JobOperator { + + List<Long> getExecutions(long instanceId) throws NoSuchJobInstanceException; + + List<Long> getJobInstances(String jobName, int start, int count) throws NoSuchJobException; + + Set<Long> getRunningExecutions(String jobName) throws NoSuchJobException; + + String getParameters(long executionId) throws NoSuchJobExecutionException; + + Long start(String jobName, String parameters) + throws NoSuchJobException, JobInstanceAlreadyExistsException; + + Long restart(long executionId) + throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException, + NoSuchJobException, JobRestartException; + + Long startNextInstance(String jobName) + throws NoSuchJobException, JobParametersNotFoundException, JobRestartException, + JobExecutionAlreadyRunningException, JobInstanceAlreadyCompleteException; + + boolean stop(long executionId) throws NoSuchJobExecutionException, JobExecutionNotRunningException; + + String getSummary(long executionId) throws NoSuchJobExecutionException; + + Map<Long, String> getStepExecutionSummaries(long executionId) throws NoSuchJobExecutionException; + + Set<String> getJobNames(); + + } + + + + The above operations represent methods from many different + interfaces, such as JobLauncher, + JobRepository, + JobExplorer, and + JobRegistry. For this reason, the provided + implementation of JobOperator, SimpleJobOperator, has many dependencies: + + + + <bean id="jobOperator" class="org.springframework.batch.core.launch.support.SimpleJobOperator"> + <property name="jobExplorer"> + <bean class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean"> + <property name="dataSource" ref="dataSource" /> + </bean> + </property> + <property name="jobRepository" ref="jobRepository" /> + <property name="jobRegistry" ref="jobRegistry" /> + <property name="jobLauncher" ref="jobLauncher" /> + </bean> + + +
+ +
+ JobParametersIncrementer + + Most of the methods on JobOperator are + self-explanatory, and more detailed explanations can be found on the + javadoc of the interface. However, the 'startNextInstance' method is + worth noting. This method will always start a new instance of a Job. + This can be extremely useful if there are serious issues in a + JobExecution, and the Job + needs to be started over again from the beginning. Unlike + JobLauncher though, which requires a new + JobParameters that will trigger a new JobInstance + if they are different than any previous one, the startNextInstance + method will use the JobParametersIncrementer tied to the Job to force + the Job to a new instance: + + + public interface JobParametersIncrementer { + + JobParameters getNext(JobParameters parameters); + } + + + + The contract of JobParametersIncrementer is that, given a + JobParameters, it will return the 'next' parameter by incrementing any + values it may contain. This strategy is useful because the framework has + no way of knowing what changes to the JobParameters make it the 'next' + instance. For example, if the only value in JobParameters is a date, and + the next instance should be created, should that value be incremented by + one day? Or one week? (if the job is weekly for instance) The same can + be said for any numerical values that help to identify the Job, as shown + below: + + + public class SampleIncrementer implements JobParametersIncrementer { + + public JobParameters getNext(JobParameters parameters) { + if (parameters==null || parameters.isEmpty()) { + return new JobParametersBuilder().addLong("run.id", 1L).toJobParameters(); + } + long id = parameters.getLong("run.id",1L) + 1; + return new JobParametersBuilder().addLong("run.id", id).toJobParameters(); + } +} + + + + In this example, the value with a key of 'run.id' is used to + descriminate between JobInstances. If the JobParameters passed in is + null, it can be assumed that the Job has never been run before and thus + it's initial state can be returned. However, if not, the old value is + obtained, incremented by one, and returned. An incrementer can be + associated with Job via the 'incrementer' attribute in the + namespace: + + + <job id="footballJob" incrementer="sampleIncrementer"> + <step name="playerload" next="gameLoad"/> + <step name="gameLoad" next="playerSummarization"/> + <step name="playerSummarization"/> + </job> + +
Stopping a Job - One of the most common reasons for wanting to launching a - job asynchronously is to be able to gracefully - stop it. This can be done through the - JobExecution returned by the - JobLauncher: + One of the most common use cases of JobOperator is gracefully + stopping a Job: - JobExecution jobExecution = launcher.run(getJob(), jobParameters); + + Set<Long> executions = jobOperator.getRunningExecutions("sampleJob"); - //give job adequate time to start - Thread.sleep(1000); - - assertEquals(BatchStatus.STARTED, jobExecution.getStatus()); - assertTrue(jobExecution.isRunning()); - - jobExecution.stop(); - - //give job time to stop - Thread.sleep(1000); - - assertEquals(BatchStatus.STOPPED, jobExecution.getStatus()); - assertFalse(jobExecution.isRunning()); + jobOperator.stop(executions.iterator().next()); + The shutdown is not immediate, since there is no way to force immediate shutdown, especially if the execution is currently in @@ -686,12 +887,6 @@ StepExecution to BatchStatus.STOPPED, save it, then do the same for the JobExecution before finishing. - -
- JobOperator - - -
\ No newline at end of file