IN PROGRESS - issue BATCH-24: Provide exit code for job executed as a main method

http://opensource.atlassian.com/projects/spring/browse/BATCH-24

Applied Stijn's refactoring of ExitCodes and moved BatchCommandLineLauncher to bootstrap.support
This commit is contained in:
lucasward
2007-09-20 06:31:38 +00:00
parent 455cc67780
commit 0891667d44
13 changed files with 371 additions and 396 deletions

View File

@@ -1,78 +1,81 @@
/*
* Copyright 2006-2007 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 org.springframework.batch.execution.bootstrap;
import org.springframework.batch.core.configuration.NoSuchJobConfigurationException;
import org.springframework.batch.core.runtime.JobIdentifier;
import org.springframework.batch.execution.facade.JobExecutorFacade;
import org.springframework.batch.repeat.ExitStatus;
/**
* Simple interface for controlling jobs from the job configuration registry,
* including possible ad-hoc executions, based on different runtime identifiers.
* Implementations should concentrate on managing jobs and delegate the
* launching to a {@link JobExecutorFacade}.
*
* @author Lucas Ward
* @author Dave Syer
*/
public interface JobLauncher {
/**
* Start a job execution with default name and other runtime information
* generated on the fly.<br/>
*
* @return the exit code from the job if it returns synchronously.
*
*/
public ExitStatus run() throws NoSuchJobConfigurationException;
/**
* Start a job execution with the given name and other runtime information
* generated on the fly.
*
* @param name
* the name to assign to the job
* @return the exit code from the job if it returns synchronously.
* @throws NoSuchJobConfigurationException
*/
public ExitStatus run(String jobName)
throws NoSuchJobConfigurationException;
/**
* Start a job execution with the given runtime information.
*
* @return the exit code from the job if it returns synchronously.
* @throws NoSuchJobConfigurationException
*/
public ExitStatus run(JobIdentifier jobIdentifier)
throws NoSuchJobConfigurationException;
/**
* Stop the current job executions if there are any. If not, no action will
* be taken.
*
* @see org.springframework.context.Lifecycle#stop()
*/
public void stop();
/**
* Return whether or not a job execution is currently running.
*/
public boolean isRunning();
}
/*
* Copyright 2006-2007 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 org.springframework.batch.execution.bootstrap;
import org.springframework.batch.core.configuration.NoSuchJobConfigurationException;
import org.springframework.batch.core.runtime.JobIdentifier;
import org.springframework.batch.execution.facade.JobExecutorFacade;
import org.springframework.batch.repeat.ExitStatus;
/**
* Simple interface for controlling jobs from the job configuration registry,
* including possible ad-hoc executions, based on different runtime identifiers.
* Implementations should concentrate on managing jobs and delegate the
* launching to a {@link JobExecutorFacade}.
*
* @author Lucas Ward
* @author Dave Syer
*/
public interface JobLauncher {
public static final String NO_SUCH_JOB_CONFIGURATION = "NO_SUCH_JOB_CONFIGURATION";
public static final String JOB_CONFIGURATION_NOT_PROVIDED = "JOB_CONFIGURATION_NOT_PROVIDED";
/**
* Start a job execution with default name and other runtime information
* generated on the fly.<br/>
*
* @return the exit code from the job if it returns synchronously.
*
*/
public ExitStatus run() throws NoSuchJobConfigurationException;
/**
* Start a job execution with the given name and other runtime information
* generated on the fly.
*
* @param name
* the name to assign to the job
* @return the exit code from the job if it returns synchronously.
* @throws NoSuchJobConfigurationException
*/
public ExitStatus run(String jobName)
throws NoSuchJobConfigurationException;
/**
* Start a job execution with the given runtime information.
*
* @return the exit code from the job if it returns synchronously.
* @throws NoSuchJobConfigurationException
*/
public ExitStatus run(JobIdentifier jobIdentifier)
throws NoSuchJobConfigurationException;
/**
* Stop the current job executions if there are any. If not, no action will
* be taken.
*
* @see org.springframework.context.Lifecycle#stop()
*/
public void stop();
/**
* Return whether or not a job execution is currently running.
*/
public boolean isRunning();
}

View File

@@ -31,7 +31,7 @@ public interface JvmExitCodeMapper extends ExitCodeMapper {
static int JVM_EXITCODE_COMPLETED = 0;
static int JVM_EXITCODE_GENERIC_ERROR = 1;
static int JVM_EXITCODE_NO_SUCH_JOBCONFIGURATION = 2;
static int JVM_EXITCODE_JOB_CONFIGURATION_ERROR = 2;
/**
* Transform the exitcode known by the batchframework into a JVM return

View File

@@ -1,192 +1,192 @@
/*
* Copyright 2006-2007 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 org.springframework.batch.execution.bootstrap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.configuration.JobConfiguration;
import org.springframework.batch.core.configuration.NoSuchJobConfigurationException;
import org.springframework.batch.core.runtime.JobIdentifier;
import org.springframework.batch.core.runtime.JobIdentifierFactory;
import org.springframework.batch.execution.facade.JobExecutorFacade;
import org.springframework.batch.execution.runtime.ScheduledJobIdentifierFactory;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.util.Assert;
/**
* Simple bootstrapping mechanism for running a single job execution in a
* {@link JobExecutorFacade}.
*
* <p>
* This simple implementation does not run the job asynchronously, so the start
* method will not return before the job ends. However, the job execution to be
* interrupted via the stop method in another thread.
* </p>
*
* @author Lucas Ward
* @author Dave Syer
* @since 2.1
*/
public class SimpleJobLauncher implements JobLauncher {
private static final Log logger = LogFactory.getLog(SimpleJobLauncher.class);
private volatile Thread processingThread;
private volatile boolean running = false;
private JobExecutorFacade jobExecutorFacade;
private JobIdentifierFactory jobIdentifierFactory = new ScheduledJobIdentifierFactory();;
private String jobConfigurationName;
/**
* Check that mandatory properties are set.
*
* @see #setJobExecutorFacade(JobExecutorFacade)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(jobExecutorFacade);
}
/**
* Return whether or not the container is currently running. This is done by
* checking the thread to see if it is still alive.
*/
public boolean isRunning() {
return running && processingThread != null && processingThread.isAlive();
}
/**
* Start the provided facade. The current thread will first be saved.
* This may seem odd at first, however, this simple bootstrap requires that
* only one thread can kick off a container, and that the first thread that
* calls start is the 'processing thread'. If the container has already been
* started, no exception will be thrown.
* @throws NoSuchJobConfigurationException
* @see Lifecycle#start().
*
* @throws IllegalStateException if JobConfiguration is null.
*/
public ExitStatus run(JobIdentifier jobIdentifier) throws NoSuchJobConfigurationException {
Assert.notNull(jobIdentifier, "JobIdentifier must not be null.");
Assert.isTrue(!isRunning(), "SynchronousLaunchers can run only one job at at time.");
/*
* There is no reason to kick off a new thread, since only one thread
* should be processing at once. However, a handle to the thread should
* be maintained to allow for interrupt
*/
processingThread = Thread.currentThread();
running = true;
try {
return jobExecutorFacade.start(jobIdentifier);
}
finally {
running = false;
}
}
/**
* Start a job execution with the given name. If a job is already running
* has no effect.
*
* @param name the name to assign to the job
* @throws NoSuchJobConfigurationException
*/
public ExitStatus run(String name) throws NoSuchJobConfigurationException {
JobIdentifier runtimeInformation = jobIdentifierFactory.getJobIdentifier(name);
return this.run(runtimeInformation);
}
/**
* Start a job execution with default name and other runtime information
* provided by the factory. If a job is already running has no effect. The
* default name is taken from the enclosed {@link JobConfiguration}.
* @throws NoSuchJobConfigurationException if the job configuration cannot be located
*
* @see #setJobIdentifierFactory(JobIdentifierFactory)
* @see org.springframework.context.Lifecycle#start()
*/
public ExitStatus run(){
if (jobConfigurationName==null) {
return new ExitStatus(false, "JOB_CONFIGURATION_NOT_PROVIDED", "No JobConfiguration was " +
"provided to the launcher.");
}
try {
return this.run(jobConfigurationName);
}
catch (NoSuchJobConfigurationException e) {
logger.error("JobExecutorFacade failed to find a JobConfiguration" +
" for the provided JobIdentifier", e);
return new ExitStatus(false, "NO_SUCH_JOB_CONFIGURATION", "JobExecutor Facade failed" +
"to find a JobConfiguration for the provided JobIdentifier");
}
}
/**
* Stop the job if it is running by interrupting its thread. If no job is
* running, no action will be taken.
*
* (non-Javadoc)
* @see org.springframework.context.Lifecycle#stop()
*/
public void stop() {
if (isRunning()) {
processingThread.interrupt();
running = false;
}
}
/**
* The facade to which job launching will be delegated.
*
* @param jobExecutorFacade a {@link JobExecutorFacade}.
*/
public void setJobExecutorFacade(JobExecutorFacade jobExecutorFacade) {
this.jobExecutorFacade = jobExecutorFacade;
}
/**
* Public setter for injecting {@link JobIdentifierFactory}. When a job is
* launched by name the factory needs to be used to create a new identifier
* for it. Defaults to a {@link ScheduledJobIdentifierFactory}.
*
* @param jobIdentifierFactory
* the {@link JobIdentifierFactory} to use when constructing
* identifiers for jobs.
*/
public void setJobIdentifierFactory(
JobIdentifierFactory jobIdentifierFactory) {
this.jobIdentifierFactory = jobIdentifierFactory;
}
/**
* Public setter for the default job configuration name to launch if none is specified.
*
* @param jobConfigurationName the name of a {@link JobConfiguration} in the registry.
*/
public void setJobConfigurationName(String jobConfigurationName) {
this.jobConfigurationName = jobConfigurationName;
}
}
/*
* Copyright 2006-2007 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 org.springframework.batch.execution.bootstrap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.configuration.JobConfiguration;
import org.springframework.batch.core.configuration.NoSuchJobConfigurationException;
import org.springframework.batch.core.runtime.JobIdentifier;
import org.springframework.batch.core.runtime.JobIdentifierFactory;
import org.springframework.batch.execution.facade.JobExecutorFacade;
import org.springframework.batch.execution.runtime.ScheduledJobIdentifierFactory;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.util.Assert;
/**
* Simple bootstrapping mechanism for running a single job execution in a
* {@link JobExecutorFacade}.
*
* <p>
* This simple implementation does not run the job asynchronously, so the start
* method will not return before the job ends. However, the job execution to be
* interrupted via the stop method in another thread.
* </p>
*
* @author Lucas Ward
* @author Dave Syer
* @since 2.1
*/
public class SimpleJobLauncher implements JobLauncher {
private static final Log logger = LogFactory.getLog(SimpleJobLauncher.class);
private volatile Thread processingThread;
private volatile boolean running = false;
private JobExecutorFacade jobExecutorFacade;
private JobIdentifierFactory jobIdentifierFactory = new ScheduledJobIdentifierFactory();;
private String jobConfigurationName;
/**
* Check that mandatory properties are set.
*
* @see #setJobExecutorFacade(JobExecutorFacade)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(jobExecutorFacade);
}
/**
* Return whether or not the container is currently running. This is done by
* checking the thread to see if it is still alive.
*/
public boolean isRunning() {
return running && processingThread != null && processingThread.isAlive();
}
/**
* Start the provided facade. The current thread will first be saved.
* This may seem odd at first, however, this simple bootstrap requires that
* only one thread can kick off a container, and that the first thread that
* calls start is the 'processing thread'. If the container has already been
* started, no exception will be thrown.
* @throws NoSuchJobConfigurationException
* @see Lifecycle#start().
*
* @throws IllegalStateException if JobConfiguration is null.
*/
public ExitStatus run(JobIdentifier jobIdentifier) throws NoSuchJobConfigurationException {
Assert.notNull(jobIdentifier, "JobIdentifier must not be null.");
Assert.isTrue(!isRunning(), "SynchronousLaunchers can run only one job at at time.");
/*
* There is no reason to kick off a new thread, since only one thread
* should be processing at once. However, a handle to the thread should
* be maintained to allow for interrupt
*/
processingThread = Thread.currentThread();
running = true;
try {
return jobExecutorFacade.start(jobIdentifier);
}
finally {
running = false;
}
}
/**
* Start a job execution with the given name. If a job is already running
* has no effect.
*
* @param name the name to assign to the job
* @throws NoSuchJobConfigurationException
*/
public ExitStatus run(String name) throws NoSuchJobConfigurationException {
JobIdentifier runtimeInformation = jobIdentifierFactory.getJobIdentifier(name);
return this.run(runtimeInformation);
}
/**
* Start a job execution with default name and other runtime information
* provided by the factory. If a job is already running has no effect. The
* default name is taken from the enclosed {@link JobConfiguration}.
* @throws NoSuchJobConfigurationException if the job configuration cannot be located
*
* @see #setJobIdentifierFactory(JobIdentifierFactory)
* @see org.springframework.context.Lifecycle#start()
*/
public ExitStatus run(){
if (jobConfigurationName==null) {
return new ExitStatus(false, JOB_CONFIGURATION_NOT_PROVIDED, "No JobConfiguration was " +
"provided to the launcher.");
}
try {
return this.run(jobConfigurationName);
}
catch (NoSuchJobConfigurationException e) {
logger.error("JobExecutorFacade failed to find a JobConfiguration" +
" for the provided JobIdentifier", e);
return new ExitStatus(false, NO_SUCH_JOB_CONFIGURATION, "JobExecutor Facade failed" +
"to find a JobConfiguration for the provided JobIdentifier");
}
}
/**
* Stop the job if it is running by interrupting its thread. If no job is
* running, no action will be taken.
*
* (non-Javadoc)
* @see org.springframework.context.Lifecycle#stop()
*/
public void stop() {
if (isRunning()) {
processingThread.interrupt();
running = false;
}
}
/**
* The facade to which job launching will be delegated.
*
* @param jobExecutorFacade a {@link JobExecutorFacade}.
*/
public void setJobExecutorFacade(JobExecutorFacade jobExecutorFacade) {
this.jobExecutorFacade = jobExecutorFacade;
}
/**
* Public setter for injecting {@link JobIdentifierFactory}. When a job is
* launched by name the factory needs to be used to create a new identifier
* for it. Defaults to a {@link ScheduledJobIdentifierFactory}.
*
* @param jobIdentifierFactory
* the {@link JobIdentifierFactory} to use when constructing
* identifiers for jobs.
*/
public void setJobIdentifierFactory(
JobIdentifierFactory jobIdentifierFactory) {
this.jobIdentifierFactory = jobIdentifierFactory;
}
/**
* Public setter for the default job configuration name to launch if none is specified.
*
* @param jobConfigurationName the name of a {@link JobConfiguration} in the registry.
*/
public void setJobConfigurationName(String jobConfigurationName) {
this.jobConfigurationName = jobConfigurationName;
}
}

View File

@@ -14,21 +14,19 @@
* limitations under the License.
*/
package org.springframework.batch.execution.bootstrap;
package org.springframework.batch.execution.bootstrap.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.configuration.NoSuchJobConfigurationException;
import org.springframework.batch.core.executor.ExitCodeExceptionClassifier;
import org.springframework.batch.execution.bootstrap.support.JvmSystemExiter;
import org.springframework.batch.execution.bootstrap.support.SimpleJvmExitCodeMapper;
import org.springframework.batch.execution.bootstrap.JobLauncher;
import org.springframework.batch.execution.bootstrap.JvmExitCodeMapper;
import org.springframework.batch.execution.bootstrap.SystemExiter;
import org.springframework.batch.execution.step.simple.SimpleExitCodeExceptionClassifier;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.Assert;
@@ -69,7 +67,7 @@ public class BatchCommandLineLauncher {
/**
* Injection setter for the {@link JobLauncher}.
*
*
* @param launcher
* the launcher to set
*/
@@ -79,7 +77,7 @@ public class BatchCommandLineLauncher {
/**
* Injection setter for the {@link ExitCodeExceptionClassifier}
*
*
* @param exceptionClassifier
*/
public void setExceptionClassifier(
@@ -89,7 +87,7 @@ public class BatchCommandLineLauncher {
/**
* Injection setter for the {@link JvmExitCodeMapper}.
*
*
* @param exitCodeMapper
* the exitCodeMapper to set
*/
@@ -99,7 +97,7 @@ public class BatchCommandLineLauncher {
/**
* Injection setter for the {@link SystemExiter}.
*
*
* @param systemExitor
*/
public void setSystemExiter(SystemExiter systemExitor) {
@@ -123,7 +121,7 @@ public class BatchCommandLineLauncher {
ExitStatus status = ExitStatus.FAILED;
ClassPathXmlApplicationContext context = null;
try {
ClassPathXmlApplicationContext parent = (ClassPathXmlApplicationContext) beanFactoryLocator
.useBeanFactory(parentKey).getFactory();
@@ -174,7 +172,7 @@ 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.
*
*
* @param args
* <ol>
* <li> classpath location of resource to load job configuration

View File

@@ -21,7 +21,10 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.execution.bootstrap.JobLauncher;
import org.springframework.batch.execution.bootstrap.JvmExitCodeMapper;
import org.springframework.batch.execution.bootstrap.SimpleJobLauncher;
import org.springframework.batch.repeat.ExitStatus;
/**
@@ -40,12 +43,14 @@ public class SimpleJvmExitCodeMapper implements JvmExitCodeMapper {
public SimpleJvmExitCodeMapper(){
mapping = new HashMap();
mapping.put(BATCH_EXITCODE_COMPLETED,
mapping.put(ExitStatus.FINISHED.getExitCode(),
new Integer(JVM_EXITCODE_COMPLETED));
mapping.put(BATCH_EXITCODE_GENERIC_ERROR,
mapping.put(ExitStatus.FAILED.getExitCode(),
new Integer(JVM_EXITCODE_GENERIC_ERROR));
mapping.put(BATCH_EXITCODE_NO_SUCH_JOBCONFIGURATION,
new Integer(JVM_EXITCODE_NO_SUCH_JOBCONFIGURATION));
mapping.put(JobLauncher.JOB_CONFIGURATION_NOT_PROVIDED,
new Integer(JVM_EXITCODE_JOB_CONFIGURATION_ERROR));
mapping.put(JobLauncher.NO_SUCH_JOB_CONFIGURATION,
new Integer(JVM_EXITCODE_JOB_CONFIGURATION_ERROR));
}
public Map getMapping() {