OPEN - issue BATCH-304: BatchCommandLineLauncher simplified and rename

http://jira.springframework.org/browse/BATCH-304

Change name to SimpleCommandLineJobDispatcher and make beanRefContext optional.
This commit is contained in:
dsyer
2008-01-22 16:11:29 +00:00
parent ce401e05cd
commit 3d87a045ac
6 changed files with 152 additions and 78 deletions

View File

@@ -36,7 +36,7 @@ import org.springframework.util.Assert;
/**
* <p>
* 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;
* </p>
*
* <p>
* 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;
* </p>
*
* <p>
* 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.
* </p>
*
* <p>
* VM Arguments vs. Program arguments: Because all of the arguments to the main
* method are optional, VM arguments are used:
*
* <ul>
* <li>-Djob.configuration.path: the classpath location of the Job to use
* <li>-Djob.name: job name to be passed to the {@link JobLauncher}
* <li>-Dbatch.execution.environment.key: the key in beanRefContext.xml used to
* load the execution envrionement.
* </ul>
* 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
* <ul>
@@ -288,18 +276,24 @@ public class BatchCommandLineLauncher {
* JobConfiguration to use
* <li>-Djob.name: job name to be passed to the {@link JobLauncher}
* <li>-Dbatch.execution.environment.key: the key in beanRefContext.xml
* used to load the execution envrionment.
* <li>-Dbean.ref.context: an altrernative location for beanRefContext.xml.</li>
* used to load the execution environment (mandatory if bean.ref.context is
* specified).
* <li>-Dbean.ref.context: an alternative location for beanRefContext.xml
* (optional, default is to only use the context specified in the
* job.configuration.path).</li>
* </ul>
*/
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);
}

View File

@@ -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) {

View File

@@ -12,6 +12,6 @@
<bean class="org.springframework.batch.execution.bootstrap.support.SimpleJvmExitCodeMapper" />
<bean
class="org.springframework.batch.execution.bootstrap.support.BatchCommandLineLauncherTests$StubSystemExiter" />
class="org.springframework.batch.execution.bootstrap.support.SimpleCommandLineJobDispatcherTests$StubSystemExiter" />
</beans>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="simpleContainerLauncher" class="org.springframework.batch.execution.bootstrap.support.StubJobLauncher" />
<bean id="jobConfigurationRegistry" class="org.springframework.batch.execution.configuration.MapJobRegistry"/>
<bean class="org.springframework.batch.execution.bootstrap.support.SimpleJvmExitCodeMapper" />
<bean
class="org.springframework.batch.execution.bootstrap.support.BatchCommandLineLauncherTests$StubSystemExiter" />
<bean id="test-job" class="org.springframework.batch.core.domain.Job">
<property name="steps">
<bean id="step1" class="org.springframework.batch.execution.step.SimpleStep">
<constructor-arg>
<bean
class="org.springframework.batch.execution.tasklet.ItemOrientedTasklet">
<property name="itemReader">
<bean
class="org.springframework.batch.item.reader.ListItemReader">
<constructor-arg value="foo,bar,spam"/>
</bean>
</property>
<property name="itemProcessor">
<bean class="org.springframework.batch.execution.launch.EmptyItemProcessor"/>
</property>
</bean>
</constructor-arg>
</bean>
</property>
</bean>
</beans>

View File

@@ -14,6 +14,6 @@
<bean class="org.springframework.batch.execution.bootstrap.support.SimpleJvmExitCodeMapper" />
<bean
class="org.springframework.batch.execution.bootstrap.support.BatchCommandLineLauncherTests$StubSystemExiter" />
class="org.springframework.batch.execution.bootstrap.support.SimpleCommandLineJobDispatcherTests$StubSystemExiter" />
</beans>

View File

@@ -113,6 +113,6 @@
</bean>
<bean
class="org.springframework.batch.execution.bootstrap.support.BatchCommandLineLauncherTests$StubSystemExiter" />
class="org.springframework.batch.execution.bootstrap.support.SimpleCommandLineJobDispatcherTests$StubSystemExiter" />
</beans>