diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/BatchCommandLineLauncher.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/SimpleCommandLineJobDispatcher.java similarity index 66% rename from spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/BatchCommandLineLauncher.java rename to spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/SimpleCommandLineJobDispatcher.java index d4f380c3e..11c3ef5da 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/BatchCommandLineLauncher.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/bootstrap/support/SimpleCommandLineJobDispatcher.java @@ -36,7 +36,7 @@ import org.springframework.util.Assert; /** *

- * Basic Launcher for starting jobs from the command line. In general, it is + * Basic launcher for starting jobs from the command line. In general, it is * assumed that this launcher will primarily be used to start a job via a script * from an Enterprise Scheduler. Therefore, exit codes are mapped to integers so * that schedulers can use the returned values to determine the next course of @@ -48,19 +48,16 @@ import org.springframework.util.Assert; *

* *

- * With any launch of a batch job within Spring Batch, a minimum of two contexts - * must be loaded. One is the context containing the Job, the other contains the - * 'Execution Environment'. That is, the JobExecutorFacade (which contains all - * the executors, plus the repository), the JobIdentifierFactory, and a normal - * JobLauncher. This command line launcher loads these application contexts by - * first loading the execution environment context via a - * {@link ContextSingletonBeanFactoryLocator}, which will search for the - * default key from classpath*:beanRefContext.xml to return the context. This - * will then be used as the parent to the Job context. All required dependencies - * of the launcher will then be satisfied by autowiring by type from the - * combined application context. Default values are provided for all fields - * except the JobLauncher. Therefore, if autowiring fails to set it (it should - * be noted that dependency checking is disabled because most of the fields have + * With any launch of a batch job within Spring Batch, a Spring context + * containing the Job and the 'Execution Environment' has to be created. This + * command line launcher can be used to load that context from a single + * location. It can also be used to first load the execution environment context + * via a {@link ContextSingletonBeanFactoryLocator}. This will then be used as + * the parent to the Job context. All required dependencies of the launcher will + * then be satisfied by autowiring by type from the combined application + * context. Default values are provided for all fields except the + * {@link JobLauncher}. Therefore, if autowiring fails to set it (it should be + * noted that dependency checking is disabled because most of the fields have * default values and thus don't require dependencies to be fulfilled via * autowiring) then an exception will be thrown. It should also be noted that * even if an exception is thrown by this class, it will be mapped to an integer @@ -68,54 +65,39 @@ import org.springframework.util.Assert; *

* *

- * One odd field might be noticed in the launcher, SystemExiter. This class is - * used to exit from the main method, rather than calling System.exit directly. - * This is because unit testing a class the calls System.exit() is impossible - * without kicking off the test within a new Jvm, which it is possible to do, - * however it is a complex solution, much more so than strategizing the exiter. + * Notice a property is available to set the {@link SystemExiter}. This class + * is used to exit from the main method, rather than calling System.exit() + * directly. This is because unit testing a class the calls System.exit() is + * impossible without kicking off the test within a new Jvm, which it is + * possible to do, however it is a complex solution, much more so than + * strategizing the exiter. *

* *

* VM Arguments vs. Program arguments: Because all of the arguments to the main - * method are optional, VM arguments are used: - * - *

+ * method are optional, System properties (VM arguments) are used (@see + * {@link #main(String[])}). * * @author Dave Syer * @author Lucas Ward * @since 2.1 */ -public class BatchCommandLineLauncher { +public class SimpleCommandLineJobDispatcher { - protected static final Log logger = LogFactory.getLog(BatchCommandLineLauncher.class); - - /** - * The default key for the parent context. - */ - public static final String DEFAULT_PARENT_KEY = "batchExecutionEnvironment"; + protected static final Log logger = LogFactory.getLog(SimpleCommandLineJobDispatcher.class); /** * The default path to the job configuration. */ public static final String DEFAULT_JOB_CONFIGURATION_PATH = "job-configuration.xml"; - /** - * The default path to the bean reference context. - */ - public static final String DEFAULT_BEAN_REF_CONTEXT_PATH = "beanRefContext.xml"; + public static final String JOB_CONFIGURATION_PATH_KEY = "job.configuration.path"; - private static final String JOB_CONFIGURATION_PATH_KEY = "job.configuration.path"; + public static final String JOB_NAME_KEY = "job.name"; - private static final String JOB_NAME_KEY = "job.name"; + public static final String BATCH_EXECUTION_ENVIRONMENT_KEY = "batch.execution.environment.key"; - private static final String BATCH_EXECUTION_ENVIRONMENT_KEY = "batch.execution.environment.key"; - - private static final String BEAN_REF_CONTEXT_KEY = "bean.ref.context"; + public static final String BEAN_REF_CONTEXT_KEY = "bean.ref.context"; private JobIdentifierFactory jobIdentifierFactory = new ScheduledJobIdentifierFactory(); @@ -131,10 +113,13 @@ public class BatchCommandLineLauncher { private String defaultJobName; - public BatchCommandLineLauncher(String beanRefContextPath) { + public SimpleCommandLineJobDispatcher(String beanRefContextPath) { + if (beanRefContextPath == null) { + return; + } beanFactoryLocator = ContextSingletonBeanFactoryLocator.getInstance(beanRefContextPath); } - + /** * Setter for the name of the {@link Job} that this launcher will run. * @@ -213,11 +198,14 @@ public class BatchCommandLineLauncher { ClassPathXmlApplicationContext context = null; try { - ConfigurableApplicationContext parent = (ConfigurableApplicationContext) beanFactoryLocator.useBeanFactory( - parentKey).getFactory(); + ConfigurableApplicationContext parent = null; - parent.getAutowireCapableBeanFactory().autowireBeanProperties(this, - AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false); + if (beanFactoryLocator != null) { + parent = (ConfigurableApplicationContext) beanFactoryLocator.useBeanFactory(parentKey).getFactory(); + + parent.getAutowireCapableBeanFactory().autowireBeanProperties(this, + AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false); + } if (!path.endsWith(".xml")) { path = path + ".xml"; @@ -234,12 +222,12 @@ public class BatchCommandLineLauncher { if (jobName == null) { String[] names = context.getBeanNamesForType(Job.class); - if (names.length==1) { + if (names.length == 1) { Job job = (Job) context.getBean(names[0]); jobName = job.getName(); } } - + if (jobName == null) { jobName = defaultJobName; } @@ -275,12 +263,12 @@ public class BatchCommandLineLauncher { } /** - * Launch a batch job using a {@link BatchCommandLineLauncher}. Creates a - * new Spring context for the job execution, and uses a common parent for - * all such contexts. No exception are thrown from this method, rather - * exceptions are logged and an integer returned through the exit status in - * a {@link JvmSystemExiter} (which can be overridden by defining one in the - * Spring context). + * Launch a batch job using a {@link SimpleCommandLineJobDispatcher}. + * Creates a new Spring context for the job execution, and uses a common + * parent for all such contexts. No exception are thrown from this method, + * rather exceptions are logged and an integer returned through the exit + * status in a {@link JvmSystemExiter} (which can be overridden by defining + * one in the Spring context). * * @param args * */ public static void main(String[] args) { String path = System.getProperty(JOB_CONFIGURATION_PATH_KEY, DEFAULT_JOB_CONFIGURATION_PATH); String name = System.getProperty(JOB_NAME_KEY); - String beanRefContextPath = System.getProperty(BEAN_REF_CONTEXT_KEY, DEFAULT_BEAN_REF_CONTEXT_PATH); - String parentKey = System.getProperty(BATCH_EXECUTION_ENVIRONMENT_KEY, DEFAULT_PARENT_KEY); + String beanRefContextPath = System.getProperty(BEAN_REF_CONTEXT_KEY); + String parentKey = System.getProperty(BATCH_EXECUTION_ENVIRONMENT_KEY); - BatchCommandLineLauncher command = new BatchCommandLineLauncher(beanRefContextPath); + Assert.state(!(beanRefContextPath == null && parentKey != null), "If you specify the " + + BATCH_EXECUTION_ENVIRONMENT_KEY + " you must also specify a path for the " + BEAN_REF_CONTEXT_KEY); + + SimpleCommandLineJobDispatcher command = new SimpleCommandLineJobDispatcher(beanRefContextPath); int result = command.start(path, name, parentKey); command.exit(result); } diff --git a/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/BatchCommandLineLauncherTests.java b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/SimpleCommandLineJobDispatcherTests.java similarity index 63% rename from spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/BatchCommandLineLauncherTests.java rename to spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/SimpleCommandLineJobDispatcherTests.java index 560a4c7f7..813d72524 100644 --- a/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/BatchCommandLineLauncherTests.java +++ b/spring-batch-execution/src/test/java/org/springframework/batch/execution/bootstrap/support/SimpleCommandLineJobDispatcherTests.java @@ -26,19 +26,22 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.access.ContextSingletonBeanFactoryLocator; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.util.ClassUtils; /** * @author Dave Syer * */ -public class BatchCommandLineLauncherTests extends TestCase { +public class SimpleCommandLineJobDispatcherTests extends TestCase { - private static final String JOB_CONFIGURATION_PATH_KEY = "job.configuration.path"; - private static final String JOB_NAME_KEY = "job.name"; - private static final String BATCH_EXECUTION_ENVIRONMENT_KEY = "batch.execution.environment.key"; + private static final String JOB_CONFIGURATION_PATH_KEY = SimpleCommandLineJobDispatcher.JOB_CONFIGURATION_PATH_KEY; + private static final String JOB_NAME_KEY = SimpleCommandLineJobDispatcher.JOB_NAME_KEY; + private static final String BATCH_EXECUTION_ENVIRONMENT_KEY = SimpleCommandLineJobDispatcher.BATCH_EXECUTION_ENVIRONMENT_KEY; + private static final String BEAN_REF_CONTEXT_KEY = SimpleCommandLineJobDispatcher.BEAN_REF_CONTEXT_KEY; private static final String TEST_BATCH_ENVIRONMENT_KEY = "testBatchEnvironment"; private static final String TEST_BATCH_ENVIRONMENT_NO_LAUNCHER_KEY = "testBatchEnvironmentNoLauncher"; + private static final String TEST_JOB_CONFIGURATION_WITH_ENVIRONMENT = ClassUtils.addResourcePathToPackagePath(SimpleCommandLineJobDispatcherTests.class, "test-batch-environment-with-job.xml"); BeanFactoryLocator beanFactoryLocator = ContextSingletonBeanFactoryLocator .getInstance(); @@ -50,6 +53,8 @@ public class BatchCommandLineLauncherTests extends TestCase { super.setUp(); System.setProperty(BATCH_EXECUTION_ENVIRONMENT_KEY, TEST_BATCH_ENVIRONMENT_KEY); + System.setProperty(BEAN_REF_CONTEXT_KEY, + "beanRefContext.xml"); } protected void tearDown() throws Exception { @@ -57,6 +62,7 @@ public class BatchCommandLineLauncherTests extends TestCase { System.clearProperty(JOB_CONFIGURATION_PATH_KEY); System.clearProperty(JOB_NAME_KEY); System.clearProperty(BATCH_EXECUTION_ENVIRONMENT_KEY); + System.clearProperty(BEAN_REF_CONTEXT_KEY); } public void testParentWithNoLauncher() { @@ -66,7 +72,7 @@ public class BatchCommandLineLauncherTests extends TestCase { System.setProperty(BATCH_EXECUTION_ENVIRONMENT_KEY, TEST_BATCH_ENVIRONMENT_NO_LAUNCHER_KEY); - BatchCommandLineLauncher.main(new String[0]); + SimpleCommandLineJobDispatcher.main(new String[0]); assertEquals(ExitCodeMapper.JVM_EXITCODE_GENERIC_ERROR, systemExiter .getStatus()); @@ -74,7 +80,41 @@ public class BatchCommandLineLauncherTests extends TestCase { /** * Test method for - * {@link org.springframework.batch.execution.bootstrap.support.BatchCommandLineLauncher#main(java.lang.String[])}. + * {@link org.springframework.batch.execution.bootstrap.support.SimpleCommandLineJobDispatcher#main(java.lang.String[])}. + * + * @throws Exception + */ + public void testNoBeanRefContext() throws Exception { + // No error, so we assume it has worked! + System.clearProperty(BATCH_EXECUTION_ENVIRONMENT_KEY); + System.clearProperty(BEAN_REF_CONTEXT_KEY); + System.setProperty(JOB_CONFIGURATION_PATH_KEY, TEST_JOB_CONFIGURATION_WITH_ENVIRONMENT); + SimpleCommandLineJobDispatcher.main(new String[0]); + } + + /** + * Test method for + * {@link org.springframework.batch.execution.bootstrap.support.SimpleCommandLineJobDispatcher#main(java.lang.String[])}. + * + * @throws Exception + */ + public void testBeanRefContextButNoEnvironment() throws Exception { + // No error, so we assume it has worked! + System.clearProperty(BEAN_REF_CONTEXT_KEY); + System.setProperty(JOB_CONFIGURATION_PATH_KEY, TEST_JOB_CONFIGURATION_WITH_ENVIRONMENT); + try { + SimpleCommandLineJobDispatcher.main(new String[0]); + } catch (IllegalStateException e) { + // expected + String message = e.getMessage(); + assertTrue("Message should contain invalid property names:"+message, message.indexOf(SimpleCommandLineJobDispatcher.BATCH_EXECUTION_ENVIRONMENT_KEY)>=0); + assertTrue("Message should contain invalid property names:"+message, message.indexOf(SimpleCommandLineJobDispatcher.BEAN_REF_CONTEXT_KEY)>=0); + } + } + + /** + * Test method for + * {@link org.springframework.batch.execution.bootstrap.support.SimpleCommandLineJobDispatcher#main(java.lang.String[])}. * * @throws Exception */ @@ -86,7 +126,7 @@ public class BatchCommandLineLauncherTests extends TestCase { setReturnValue(ExitStatus.FINISHED); - BatchCommandLineLauncher.main(new String[0]); + SimpleCommandLineJobDispatcher.main(new String[0]); assertEquals(ExitCodeMapper.JVM_EXITCODE_COMPLETED, systemExiter .getStatus()); @@ -96,7 +136,7 @@ public class BatchCommandLineLauncherTests extends TestCase { /** * Test method for - * {@link org.springframework.batch.execution.bootstrap.support.BatchCommandLineLauncher#main(java.lang.String[])}. + * {@link org.springframework.batch.execution.bootstrap.support.SimpleCommandLineJobDispatcher#main(java.lang.String[])}. * * @throws Exception */ @@ -108,7 +148,7 @@ public class BatchCommandLineLauncherTests extends TestCase { setReturnValue(ExitStatus.FINISHED); System.setProperty(JOB_NAME_KEY, "foo"); - BatchCommandLineLauncher.main(new String[0]); + SimpleCommandLineJobDispatcher.main(new String[0]); assertEquals(ExitCodeMapper.JVM_EXITCODE_COMPLETED, systemExiter .getStatus()); @@ -124,7 +164,7 @@ public class BatchCommandLineLauncherTests extends TestCase { /** * Test method for - * {@link org.springframework.batch.execution.bootstrap.support.BatchCommandLineLauncher#main(java.lang.String[])}. + * {@link org.springframework.batch.execution.bootstrap.support.SimpleCommandLineJobDispatcher#main(java.lang.String[])}. * * @throws Exception */ @@ -132,17 +172,17 @@ public class BatchCommandLineLauncherTests extends TestCase { // We can only test this without running the whole test in another jvm // by using a special SystemExiter in the default configuration because // otherwise it calls System.exit() by default. - BatchCommandLineLauncher.main(new String[0]); + SimpleCommandLineJobDispatcher.main(new String[0]); } public void testInvalidJobConfig() { // To test this without kicking off in a new jvm, we have to autowire // the launcher (in BatchCommandLineLauncher.start) from the parent, // *then* the child context. - buildContext(BatchCommandLineLauncher.DEFAULT_PARENT_KEY); + buildContext("batchExecutionEnvironment"); assertNotNull(systemExiter); System.setProperty(JOB_CONFIGURATION_PATH_KEY, "foo"); - BatchCommandLineLauncher.main(new String[0]); + SimpleCommandLineJobDispatcher.main(new String[0]); } private void buildContext(String key) { diff --git a/spring-batch-execution/src/test/resources/org/springframework/batch/execution/bootstrap/support/test-batch-environment-no-launcher.xml b/spring-batch-execution/src/test/resources/org/springframework/batch/execution/bootstrap/support/test-batch-environment-no-launcher.xml index b4e8474ed..adf314f6d 100644 --- a/spring-batch-execution/src/test/resources/org/springframework/batch/execution/bootstrap/support/test-batch-environment-no-launcher.xml +++ b/spring-batch-execution/src/test/resources/org/springframework/batch/execution/bootstrap/support/test-batch-environment-no-launcher.xml @@ -12,6 +12,6 @@ + class="org.springframework.batch.execution.bootstrap.support.SimpleCommandLineJobDispatcherTests$StubSystemExiter" /> diff --git a/spring-batch-execution/src/test/resources/org/springframework/batch/execution/bootstrap/support/test-batch-environment-with-job.xml b/spring-batch-execution/src/test/resources/org/springframework/batch/execution/bootstrap/support/test-batch-environment-with-job.xml new file mode 100644 index 000000000..ecaa727ea --- /dev/null +++ b/spring-batch-execution/src/test/resources/org/springframework/batch/execution/bootstrap/support/test-batch-environment-with-job.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-batch-execution/src/test/resources/org/springframework/batch/execution/bootstrap/support/test-batch-environment.xml b/spring-batch-execution/src/test/resources/org/springframework/batch/execution/bootstrap/support/test-batch-environment.xml index cf59d0a77..238809664 100644 --- a/spring-batch-execution/src/test/resources/org/springframework/batch/execution/bootstrap/support/test-batch-environment.xml +++ b/spring-batch-execution/src/test/resources/org/springframework/batch/execution/bootstrap/support/test-batch-environment.xml @@ -14,6 +14,6 @@ + class="org.springframework.batch.execution.bootstrap.support.SimpleCommandLineJobDispatcherTests$StubSystemExiter" /> diff --git a/spring-batch-execution/src/test/resources/simple-container-definition.xml b/spring-batch-execution/src/test/resources/simple-container-definition.xml index 537b99e69..d70698fd1 100644 --- a/spring-batch-execution/src/test/resources/simple-container-definition.xml +++ b/spring-batch-execution/src/test/resources/simple-container-definition.xml @@ -113,6 +113,6 @@ + class="org.springframework.batch.execution.bootstrap.support.SimpleCommandLineJobDispatcherTests$StubSystemExiter" />