11/*
2- * Copyright 2013-2021 the original author or authors.
2+ * Copyright 2013-2023 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1818import java .util .ArrayList ;
1919import java .util .List ;
2020
21+ import javax .sql .DataSource ;
22+
2123import org .apache .commons .dbcp2 .BasicDataSource ;
24+ import org .junit .Assert ;
2225import org .junit .Test ;
2326import org .junit .runner .RunWith ;
2427import test .jdbc .datasource .DataSourceInitializer ;
2932import org .springframework .batch .core .JobInstance ;
3033import org .springframework .batch .core .JobInterruptedException ;
3134import org .springframework .batch .core .JobParameters ;
35+ import org .springframework .batch .core .JobParametersBuilder ;
3236import org .springframework .batch .core .Step ;
3337import org .springframework .batch .core .StepExecution ;
3438import org .springframework .batch .core .UnexpectedJobExecutionException ;
4953import org .springframework .batch .core .repository .JobRepository ;
5054import org .springframework .batch .core .repository .JobRestartException ;
5155import org .springframework .beans .factory .annotation .Autowired ;
56+ import org .springframework .context .ApplicationContext ;
57+ import org .springframework .context .annotation .AnnotationConfigApplicationContext ;
5258import org .springframework .context .annotation .Bean ;
5359import org .springframework .context .annotation .Configuration ;
5460import org .springframework .core .io .ClassPathResource ;
5561import org .springframework .core .io .Resource ;
62+ import org .springframework .jdbc .datasource .embedded .EmbeddedDatabaseBuilder ;
63+ import org .springframework .jdbc .datasource .embedded .EmbeddedDatabaseType ;
64+ import org .springframework .jdbc .support .JdbcTransactionManager ;
5665import org .springframework .test .context .ContextConfiguration ;
5766import org .springframework .test .context .junit4 .SpringJUnit4ClassRunner ;
5867
5968import static org .junit .Assert .assertEquals ;
6069import static org .junit .Assert .assertNotNull ;
6170
6271/**
63- * Integration test for the BATCH-2034 issue.
64- * The {@link FlowStep} execution should not fail in the remote partitioning use case because the {@link SimpleJobExplorer}
65- * doesn't retrieve the {@link JobInstance} from the {@link JobRepository}.
66- * To illustrate the issue the test simulates the behavior of the {@code StepExecutionRequestHandler}
67- * from the spring-batch-integration project.
68- *
72+ *
73+ * Integration tests for the <code>SimpleJobExplorer</code> implementation.
74+ *
6975 * @author Sergey Shcherbakov
7076 * @author Mahmoud Ben Hassine
7177 */
7278@ ContextConfiguration (classes ={SimpleJobExplorerIntegrationTests .Config .class })
7379@ RunWith (SpringJUnit4ClassRunner .class )
7480public class SimpleJobExplorerIntegrationTests {
75-
81+
82+ /*
83+ * Integration test for the BATCH-2034 issue. The {@link FlowStep} execution should
84+ * not fail in the remote partitioning use case because the {@link SimpleJobExplorer}
85+ * doesn't retrieve the {@link JobInstance} from the {@link JobRepository}. To
86+ * illustrate the issue the test simulates the behavior of the {@code
87+ * StepExecutionRequestHandler} from the spring-batch-integration project.
88+ */
7689 @ Configuration
7790 @ EnableBatchProcessing
7891 static class Config {
79-
92+
8093 @ Autowired
8194 private StepBuilderFactory steps ;
8295
8396 @ Bean
8497 public JobExplorer jobExplorer () throws Exception {
8598 return jobExplorerFactoryBean ().getObject ();
8699 }
87-
100+
88101 @ Bean
89102 public JobExplorerFactoryBean jobExplorerFactoryBean () {
90103 JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean ();
91104 jobExplorerFactoryBean .setDataSource (dataSource ());
92105 return jobExplorerFactoryBean ;
93106 }
94-
107+
95108 @ Bean
96109 public Step flowStep () throws Exception {
97110 return steps .get ("flowStep" ).flow (simpleFlow ()).build ();
@@ -111,7 +124,7 @@ public SimpleFlow simpleFlow() {
111124 simpleFlow .setStateTransitions (transitions );
112125 return simpleFlow ;
113126 }
114-
127+
115128 @ Bean
116129 public BasicDataSource dataSource () {
117130 BasicDataSource dataSource = new BasicDataSource ();
@@ -121,12 +134,12 @@ public BasicDataSource dataSource() {
121134 dataSource .setPassword ("" );
122135 return dataSource ;
123136 }
124-
137+
125138 @ Bean
126139 public DataSourceInitializer dataSourceInitializer () {
127140 DataSourceInitializer dataSourceInitializer = new DataSourceInitializer ();
128141 dataSourceInitializer .setDataSource (dataSource ());
129- dataSourceInitializer .setInitScripts (new Resource [] {
142+ dataSourceInitializer .setInitScripts (new Resource [] {
130143 new ClassPathResource ("org/springframework/batch/core/schema-drop-hsqldb.sql" ),
131144 new ClassPathResource ("org/springframework/batch/core/schema-hsqldb.sql" )
132145 });
@@ -155,19 +168,19 @@ public Job job(JobBuilderFactory jobBuilderFactory) {
155168
156169 @ Autowired
157170 private Job job ;
158-
171+
159172 @ Test
160173 public void testGetStepExecution () throws JobExecutionAlreadyRunningException , JobRestartException , JobInstanceAlreadyCompleteException , JobInterruptedException , UnexpectedJobExecutionException {
161174
162175 // Prepare the jobRepository for the test
163176 JobExecution jobExecution = jobRepository .createJobExecution ("myJob" , new JobParameters ());
164177 StepExecution stepExecution = jobExecution .createStepExecution ("flowStep" );
165178 jobRepository .add (stepExecution );
166-
179+
167180 // Executed on the remote end in remote partitioning use case
168181 StepExecution jobExplorerStepExecution = jobExplorer .getStepExecution (jobExecution .getId (), stepExecution .getId ());
169182 flowStep .execute (jobExplorerStepExecution );
170-
183+
171184 assertEquals (BatchStatus .COMPLETED , jobExplorerStepExecution .getStatus ());
172185 }
173186
@@ -180,4 +193,66 @@ public void getLastJobExecutionShouldFetchStepExecutions() throws Exception {
180193 StepExecution stepExecution = lastJobExecution .getStepExecutions ().iterator ().next ();
181194 assertNotNull (stepExecution .getExecutionContext ());
182195 }
196+
197+ /*
198+ * Test case for https://github.com/spring-projects/spring-batch/issues/4246:
199+ * SimpleJobExplorer#getJobExecutions(JobInstance) should return a list of job
200+ * executions, where each execution has its own job parameters.
201+ */
202+
203+ @ Configuration
204+ @ EnableBatchProcessing
205+ static class JobConfiguration {
206+
207+ @ Bean
208+ public Step step (StepBuilderFactory stepBuilderFactory ) {
209+ return stepBuilderFactory .get ("step" ).tasklet ((contribution , chunkContext ) -> {
210+ throw new RuntimeException ("Expected failure!" );
211+ }).build ();
212+ }
213+
214+ @ Bean
215+ public Job job (JobBuilderFactory jobBuilderFactory , Step step ) {
216+ return jobBuilderFactory .get ("job" ).start (step ).build ();
217+ }
218+
219+ @ Bean
220+ public DataSource dataSource () {
221+ return new EmbeddedDatabaseBuilder ().setType (EmbeddedDatabaseType .H2 )
222+ .addScript ("/org/springframework/batch/core/schema-h2.sql" ).generateUniqueName (true ).build ();
223+ }
224+
225+ @ Bean
226+ public JdbcTransactionManager transactionManager (DataSource dataSource ) {
227+ return new JdbcTransactionManager (dataSource );
228+ }
229+
230+ }
231+
232+ @ Test
233+ public void retrievedJobExecutionsShouldHaveTheirOwnParameters () throws Exception {
234+ // given
235+ ApplicationContext context = new AnnotationConfigApplicationContext (JobConfiguration .class );
236+ JobLauncher jobLauncher = context .getBean (JobLauncher .class );
237+ JobExplorer jobExplorer = context .getBean (JobExplorer .class );
238+ Job job = context .getBean (Job .class );
239+ long id = 1L ;
240+ JobParameters jobParameters1 = new JobParametersBuilder ().addLong ("id" , id ).addString ("name" , "foo" , false )
241+ .toJobParameters ();
242+ JobParameters jobParameters2 = new JobParametersBuilder ().addLong ("id" , id ).addString ("name" , "bar" , false )
243+ .toJobParameters ();
244+
245+ // when
246+ JobExecution jobExecution1 = jobLauncher .run (job , jobParameters1 );
247+ JobExecution jobExecution2 = jobLauncher .run (job , jobParameters2 );
248+
249+ // then
250+ Assert .assertEquals (jobExecution1 .getJobInstance (), jobExecution2 .getJobInstance ());
251+ List <JobExecution > jobExecutions = jobExplorer .getJobExecutions (jobExecution1 .getJobInstance ());
252+ Assert .assertEquals (2 , jobExecutions .size ());
253+ JobParameters actualJobParameters1 = jobExecutions .get (0 ).getJobParameters ();
254+ JobParameters actualJobParameters2 = jobExecutions .get (1 ).getJobParameters ();
255+ Assert .assertNotEquals (actualJobParameters1 , actualJobParameters2 );
256+ }
257+
183258}
0 commit comments