diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/explore/support/SimpleJobExplorerIntegrationTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/explore/support/SimpleJobExplorerIntegrationTests.java index d7bc8078c..658d74cb0 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/explore/support/SimpleJobExplorerIntegrationTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/explore/support/SimpleJobExplorerIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2021 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,10 @@ package org.springframework.batch.core.explore.support; import java.util.ArrayList; import java.util.List; +import javax.sql.DataSource; + import org.apache.commons.dbcp2.BasicDataSource; +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import test.jdbc.datasource.DataSourceInitializer; @@ -29,6 +32,7 @@ import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobInstance; import org.springframework.batch.core.JobInterruptedException; import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.JobParametersBuilder; import org.springframework.batch.core.Step; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.UnexpectedJobExecutionException; @@ -49,10 +53,15 @@ import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteExcep import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.repository.JobRestartException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import org.springframework.jdbc.support.JdbcTransactionManager; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -60,23 +69,27 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; /** - * Integration test for the BATCH-2034 issue. - * The {@link FlowStep} execution should not fail in the remote partitioning use case because the {@link SimpleJobExplorer} - * doesn't retrieve the {@link JobInstance} from the {@link JobRepository}. - * To illustrate the issue the test simulates the behavior of the {@code StepExecutionRequestHandler} - * from the spring-batch-integration project. - * + * + * Integration tests for the SimpleJobExplorer implementation. + * * @author Sergey Shcherbakov * @author Mahmoud Ben Hassine */ @ContextConfiguration(classes={SimpleJobExplorerIntegrationTests.Config.class}) @RunWith(SpringJUnit4ClassRunner.class) public class SimpleJobExplorerIntegrationTests { - + + /* + * Integration test for the BATCH-2034 issue. The {@link FlowStep} execution should + * not fail in the remote partitioning use case because the {@link SimpleJobExplorer} + * doesn't retrieve the {@link JobInstance} from the {@link JobRepository}. To + * illustrate the issue the test simulates the behavior of the {@code + * StepExecutionRequestHandler} from the spring-batch-integration project. + */ @Configuration @EnableBatchProcessing static class Config { - + @Autowired private StepBuilderFactory steps; @@ -84,14 +97,14 @@ public class SimpleJobExplorerIntegrationTests { public JobExplorer jobExplorer() throws Exception { return jobExplorerFactoryBean().getObject(); } - + @Bean public JobExplorerFactoryBean jobExplorerFactoryBean() { JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean(); jobExplorerFactoryBean.setDataSource(dataSource()); return jobExplorerFactoryBean; } - + @Bean public Step flowStep() throws Exception { return steps.get("flowStep").flow(simpleFlow()).build(); @@ -111,7 +124,7 @@ public class SimpleJobExplorerIntegrationTests { simpleFlow.setStateTransitions(transitions); return simpleFlow; } - + @Bean public BasicDataSource dataSource() { BasicDataSource dataSource = new BasicDataSource(); @@ -121,12 +134,12 @@ public class SimpleJobExplorerIntegrationTests { dataSource.setPassword(""); return dataSource; } - + @Bean public DataSourceInitializer dataSourceInitializer() { DataSourceInitializer dataSourceInitializer = new DataSourceInitializer(); dataSourceInitializer.setDataSource(dataSource()); - dataSourceInitializer.setInitScripts(new Resource[] { + dataSourceInitializer.setInitScripts(new Resource[] { new ClassPathResource("org/springframework/batch/core/schema-drop-hsqldb.sql"), new ClassPathResource("org/springframework/batch/core/schema-hsqldb.sql") }); @@ -155,7 +168,7 @@ public class SimpleJobExplorerIntegrationTests { @Autowired private Job job; - + @Test public void testGetStepExecution() throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobInterruptedException, UnexpectedJobExecutionException { @@ -163,11 +176,11 @@ public class SimpleJobExplorerIntegrationTests { JobExecution jobExecution = jobRepository.createJobExecution("myJob", new JobParameters()); StepExecution stepExecution = jobExecution.createStepExecution("flowStep"); jobRepository.add(stepExecution); - + // Executed on the remote end in remote partitioning use case StepExecution jobExplorerStepExecution = jobExplorer.getStepExecution(jobExecution.getId(), stepExecution.getId()); flowStep.execute(jobExplorerStepExecution); - + assertEquals(BatchStatus.COMPLETED, jobExplorerStepExecution.getStatus()); } @@ -180,4 +193,66 @@ public class SimpleJobExplorerIntegrationTests { StepExecution stepExecution = lastJobExecution.getStepExecutions().iterator().next(); assertNotNull(stepExecution.getExecutionContext()); } + + /* + * Test case for https://github.com/spring-projects/spring-batch/issues/4246: + * SimpleJobExplorer#getJobExecutions(JobInstance) should return a list of job + * executions, where each execution has its own job parameters. + */ + + @Configuration + @EnableBatchProcessing + static class JobConfiguration { + + @Bean + public Step step(StepBuilderFactory stepBuilderFactory) { + return stepBuilderFactory.get("step").tasklet((contribution, chunkContext) -> { + throw new RuntimeException("Expected failure!"); + }).build(); + } + + @Bean + public Job job(JobBuilderFactory jobBuilderFactory, Step step) { + return jobBuilderFactory.get("job").start(step).build(); + } + + @Bean + public DataSource dataSource() { + return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2) + .addScript("/org/springframework/batch/core/schema-h2.sql").generateUniqueName(true).build(); + } + + @Bean + public JdbcTransactionManager transactionManager(DataSource dataSource) { + return new JdbcTransactionManager(dataSource); + } + + } + + @Test + public void retrievedJobExecutionsShouldHaveTheirOwnParameters() throws Exception { + // given + ApplicationContext context = new AnnotationConfigApplicationContext(JobConfiguration.class); + JobLauncher jobLauncher = context.getBean(JobLauncher.class); + JobExplorer jobExplorer = context.getBean(JobExplorer.class); + Job job = context.getBean(Job.class); + long id = 1L; + JobParameters jobParameters1 = new JobParametersBuilder().addLong("id", id).addString("name", "foo", false) + .toJobParameters(); + JobParameters jobParameters2 = new JobParametersBuilder().addLong("id", id).addString("name", "bar", false) + .toJobParameters(); + + // when + JobExecution jobExecution1 = jobLauncher.run(job, jobParameters1); + JobExecution jobExecution2 = jobLauncher.run(job, jobParameters2); + + // then + Assert.assertEquals(jobExecution1.getJobInstance(), jobExecution2.getJobInstance()); + List jobExecutions = jobExplorer.getJobExecutions(jobExecution1.getJobInstance()); + Assert.assertEquals(2, jobExecutions.size()); + JobParameters actualJobParameters1 = jobExecutions.get(0).getJobParameters(); + JobParameters actualJobParameters2 = jobExecutions.get(1).getJobParameters(); + Assert.assertNotEquals(actualJobParameters1, actualJobParameters2); + } + }