+ * Domain object representing information about a person. + *
+ */ +public class Person { + private String lastName; + private String firstName; + + public Person() { + + } + + public Person(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + public void setFirstName(final String firstName) { + this.firstName = firstName; + } + + public String getFirstName() { + return firstName; + } + + public void setLastName(final String lastName) { + this.lastName = lastName; + } + + public String getLastName() { + return lastName; + } + + @Override + public String toString() { + return "firstName: " + firstName + ", lastName: " + lastName; + } +} diff --git a/archetypes/simple-cli-javaconfig/src/main/java/example/PersonItemProcessor.java b/archetypes/simple-cli-javaconfig/src/main/java/example/PersonItemProcessor.java new file mode 100644 index 000000000..b9a25bdea --- /dev/null +++ b/archetypes/simple-cli-javaconfig/src/main/java/example/PersonItemProcessor.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example; + +import org.springframework.batch.item.ItemProcessor; + +/** + *+ * An example {@link org.springframework.batch.item.ItemProcessor} implementation that upper cases attributes on the + * provided {@link Person} object. + *
+ */ +public class PersonItemProcessor implements ItemProcessor+ * Example test case processing a {@link Person} using the {@link PersonItemProcessor}. + *
+ */ +public class PersonItemProcessorTest { + @Test + public void testProcessedPersonRecord() throws Exception { + final Person person = new Person(); + person.setFirstName("Jane"); + person.setLastName("Doe"); + + final Person processedPerson = new PersonItemProcessor().process(person); + + assertEquals("First name does not match expected value.", "JANE", processedPerson.getFirstName()); + assertEquals("Last name does not match expected value.", "DOE", processedPerson.getLastName()); + } +} diff --git a/archetypes/simple-cli-javaconfig/src/test/java/example/PersonJobConfigurationTest.java b/archetypes/simple-cli-javaconfig/src/test/java/example/PersonJobConfigurationTest.java new file mode 100644 index 000000000..c119254eb --- /dev/null +++ b/archetypes/simple-cli-javaconfig/src/test/java/example/PersonJobConfigurationTest.java @@ -0,0 +1,97 @@ +/* + * Copyright 2006-2013 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobInstance; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.launch.JobOperator; +import org.springframework.batch.test.JobLauncherTestUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + *+ * Test cases asserting on the example job's configuration. + *
+ */ +@ContextConfiguration(classes=TestContext.class) +@RunWith(SpringJUnit4ClassRunner.class) +public class PersonJobConfigurationTest { + @Autowired + private JobLauncher jobLauncher; + + @Autowired + private Job job; + + @Autowired + private JobOperator jobOperator; + + @Autowired + private JobLauncherTestUtils jobLauncherTestUtils; + + /** + *+ * Creates a new {@link JobExecution} using a {@link JobLauncher}. + *
+ * + * @throws Exception if any {@link Exception}'s occur + */ + @Test + public void testLaunchJobWithJobLauncher() throws Exception { + final JobExecution jobExecution = jobLauncher.run(job, new JobParameters()); + assertEquals("Batch status not COMPLETED", BatchStatus.COMPLETED, jobExecution.getStatus()); + } + + /** + *+ * Create a unique job instance and check it's execution completes successfully. + * Uses the convenience methods provided by the testing superclass. + *
+ * + * @throws Exception if any {@link Exception}'s occur + */ + @Test + public void testLaunchJob() throws Exception { + final JobExecution jobExecution = jobLauncherTestUtils.launchJob(jobLauncherTestUtils.getUniqueJobParameters()); + assertEquals("Batch status not COMPLETED", BatchStatus.COMPLETED, jobExecution.getStatus()); + } + + /** + *+ * Execute a fresh {@link JobInstance} using {@link JobOperator} which is closer to + * a remote invocation scenario. + *
+ * + * @throws Exception if any {@link Exception}'s occur + */ + @Test + public void testLaunchByJobOperator() throws Exception { + final long jobExecutionId = jobOperator.startNextInstance(jobLauncherTestUtils.getJob().getName()); + + final String result = jobOperator.getSummary(jobExecutionId); + assertTrue("Result does not contain status=COMPLETED", result.contains("status=" + BatchStatus.COMPLETED)); + } +} diff --git a/archetypes/simple-cli-javaconfig/src/test/java/example/TestContext.java b/archetypes/simple-cli-javaconfig/src/test/java/example/TestContext.java new file mode 100644 index 000000000..19b5c27c8 --- /dev/null +++ b/archetypes/simple-cli-javaconfig/src/test/java/example/TestContext.java @@ -0,0 +1,59 @@ +package example; + +import javax.sql.DataSource; + +import org.springframework.batch.core.configuration.JobRegistry; +import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; +import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor; +import org.springframework.batch.core.configuration.support.MapJobRegistry; +import org.springframework.batch.core.explore.JobExplorer; +import org.springframework.batch.core.explore.support.JobExplorerFactoryBean; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.launch.JobOperator; +import org.springframework.batch.core.launch.support.SimpleJobOperator; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.batch.test.JobLauncherTestUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@EnableBatchProcessing +@Import(LaunchContext.class) +public class TestContext { + + @Bean + JobLauncherTestUtils jobLauncherTestUtils() { + return new JobLauncherTestUtils(); + } + + @Bean + JobOperator jobOperator(final JobLauncher jobLauncher, final JobExplorer jobExplorer, + final JobRepository jobRepository, final JobRegistry jobRegistry) { + return new SimpleJobOperator() {{ + setJobLauncher(jobLauncher); + setJobExplorer(jobExplorer); + setJobRepository(jobRepository); + setJobRegistry(jobRegistry); + }}; + } + + @Bean + JobExplorerFactoryBean jobExplorer(final DataSource dataSource) { + return new JobExplorerFactoryBean() {{ + setDataSource(dataSource); + }}; + } + + @Bean + MapJobRegistry jobRegister() { + return new MapJobRegistry(); + } + + @Bean + JobRegistryBeanPostProcessor jobRegisterBeanPostProcess(final JobRegistry jobRegistry) { + return new JobRegistryBeanPostProcessor() {{ + setJobRegistry(jobRegistry); + }}; + } +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/CommandLineJobRunner.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/CommandLineJobRunner.java index b8c9e71cb..f4d05ace1 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/CommandLineJobRunner.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/CommandLineJobRunner.java @@ -47,10 +47,13 @@ import org.springframework.batch.core.launch.JobExecutionNotRunningException; import org.springframework.batch.core.launch.JobExecutionNotStoppedException; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.launch.JobParametersNotFoundException; +import org.springframework.batch.core.launch.NoSuchJobException; import org.springframework.batch.core.repository.JobRepository; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -281,7 +284,12 @@ public class CommandLineJobRunner { ConfigurableApplicationContext context = null; try { - context = new ClassPathXmlApplicationContext(jobPath); + try { + context = new ClassPathXmlApplicationContext(jobPath); + } catch (BeansException e) { + logger.info("No XML-based context named " + jobPath + ". Trying class-based configuration."); + context = new AnnotationConfigApplicationContext(Class.forName(jobPath)); + } context.getAutowireCapableBeanFactory().autowireBeanProperties(this, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false); @@ -292,7 +300,7 @@ public class CommandLineJobRunner { } String jobName = jobIdentifier; - + JobParameters jobParameters = jobParametersConverter.getJobParameters(StringUtils .splitArrayElementsIntoProperties(parameters, "=")); Assert.isTrue(parameters == null || parameters.length == 0 || !jobParameters.isEmpty(), @@ -333,11 +341,14 @@ public class CommandLineJobRunner { jobName = jobExecution.getJobInstance().getJobName(); } - Job job; + Job job = null; if (jobLocator != null) { - job = jobLocator.getJob(jobName); + try { + job = jobLocator.getJob(jobName); + } catch (NoSuchJobException e) { + } } - else { + if (job == null) { job = (Job) context.getBean(jobName); } @@ -566,7 +577,7 @@ public class CommandLineJobRunner { } if (jobPath == null || jobIdentifier == null) { - String message = "At least 2 arguments are required: JobPath and jobIdentifier."; + String message = "At least 2 arguments are required: JobPath/JobClass and jobIdentifier."; logger.error(message); CommandLineJobRunner.message = message; command.exit(1);