OPEN - issue BATCH-24: Provide exit code for job executed as a main method
http://opensource.atlassian.com/projects/spring/browse/BATCH-24
This commit is contained in:
@@ -1,120 +1,141 @@
|
||||
/*
|
||||
* 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.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @since 2.1
|
||||
*/
|
||||
public class BatchCommandLineLauncher {
|
||||
|
||||
/**
|
||||
* The key for the parent context.
|
||||
*/
|
||||
public static final String PARENT_KEY = "simple-container";
|
||||
|
||||
private ConfigurableApplicationContext parent;
|
||||
|
||||
private JobLauncher launcher;
|
||||
|
||||
/**
|
||||
* Default constructor for the launcher. Sets up the parent context to use
|
||||
* for all job executions using a context key {@link #PARENT_KEY}.
|
||||
*/
|
||||
public BatchCommandLineLauncher() {
|
||||
parent = (ConfigurableApplicationContext) ContextSingletonBeanFactoryLocator
|
||||
.getInstance().useBeanFactory(PARENT_KEY).getFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection setter for the {@link JobLauncher}.
|
||||
*
|
||||
* @param launcher
|
||||
* the launcher to set
|
||||
*/
|
||||
public void setLauncher(JobLauncher launcher) {
|
||||
this.launcher = launcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path
|
||||
* the path to a Spring context configuration for this job
|
||||
* @param jobName
|
||||
* the name of the job execution to use
|
||||
* @throws NoSuchJobConfigurationException
|
||||
*/
|
||||
private void start(String path, String jobName)
|
||||
throws NoSuchJobConfigurationException {
|
||||
if (!path.endsWith(".xml")) {
|
||||
path = path + ".xml";
|
||||
}
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
new String[] { path }, parent);
|
||||
context.getAutowireCapableBeanFactory().autowireBeanProperties(this,
|
||||
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
|
||||
try {
|
||||
if (!launcher.isRunning()) {
|
||||
if (jobName == null) {
|
||||
launcher.run();
|
||||
} else {
|
||||
launcher.run(jobName);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
context.stop();
|
||||
} finally {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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> path to resource to load job configuration context
|
||||
* (default "job-configuration.xml");</li>
|
||||
* <li>runtime name for job execution (default
|
||||
* "job-execution-id").</li>
|
||||
* </ol>
|
||||
* @throws NoSuchJobConfigurationException
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
throws NoSuchJobConfigurationException {
|
||||
String path = "job-configuration.xml";
|
||||
String name = null;
|
||||
if (args.length > 0) {
|
||||
path = args[0];
|
||||
}
|
||||
if (args.length > 1) {
|
||||
name = args[1];
|
||||
}
|
||||
BatchCommandLineLauncher command = new BatchCommandLineLauncher();
|
||||
command.start(path, name);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* 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.repeat.ExitStatus;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @since 2.1
|
||||
*/
|
||||
public class BatchCommandLineLauncher {
|
||||
|
||||
/**
|
||||
* The key for the parent context.
|
||||
*/
|
||||
public static final String PARENT_KEY = "simple-container";
|
||||
|
||||
private ConfigurableApplicationContext parent;
|
||||
private JvmExitCodeMapper exitCodeMapper;
|
||||
|
||||
private JobLauncher launcher;
|
||||
|
||||
/**
|
||||
* Default constructor for the launcher. Sets up the parent context to use
|
||||
* for all job executions using a context key {@link #PARENT_KEY}.
|
||||
*/
|
||||
public BatchCommandLineLauncher() {
|
||||
parent = (ConfigurableApplicationContext) ContextSingletonBeanFactoryLocator
|
||||
.getInstance().useBeanFactory(PARENT_KEY).getFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection setter for the {@link JobLauncher}.
|
||||
*
|
||||
* @param launcher
|
||||
* the launcher to set
|
||||
*/
|
||||
public void setLauncher(JobLauncher launcher) {
|
||||
this.launcher = launcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection setter for the {@link JvmExitCodeMapper}.
|
||||
*
|
||||
* @param exitCodeMapper
|
||||
* the exitCodeMapper to set
|
||||
*/
|
||||
public void setExitCodeMapper(JvmExitCodeMapper exitCodeMapper) {
|
||||
this.exitCodeMapper = exitCodeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path
|
||||
* the path to a Spring context configuration for this job
|
||||
* @param jobName
|
||||
* the name of the job execution to use
|
||||
* @throws NoSuchJobConfigurationException
|
||||
*/
|
||||
int start(String path, String jobName) {
|
||||
if (!path.endsWith(".xml")) {
|
||||
path = path + ".xml";
|
||||
}
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
new String[] { path }, parent);
|
||||
context.getAutowireCapableBeanFactory().autowireBeanProperties(this,
|
||||
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
|
||||
|
||||
ExitStatus status = null;
|
||||
|
||||
try {
|
||||
if (!launcher.isRunning()) {
|
||||
if (jobName == null) {
|
||||
status = launcher.run();
|
||||
} else {
|
||||
status = launcher.run(jobName);
|
||||
}
|
||||
}
|
||||
} catch (NoSuchJobConfigurationException e) {
|
||||
status = new ExitStatus(false,
|
||||
JvmExitCodeMapper.BATCH_EXITCODE_NO_SUCH_JOBCONFIGURATION,
|
||||
"Could not locate JobConfiguration \"" + jobName + "\"" );
|
||||
} finally {
|
||||
try {
|
||||
context.stop();
|
||||
} finally {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
return exitCodeMapper.getExitCode(status.getExitCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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> path to resource to load job configuration context
|
||||
* (default "job-configuration.xml");</li>
|
||||
* <li>runtime name for job execution (default
|
||||
* "job-execution-id").</li>
|
||||
* </ol>
|
||||
* @throws NoSuchJobConfigurationException
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
|
||||
String path = "job-configuration.xml";
|
||||
String name = null;
|
||||
if (args.length > 0) {
|
||||
path = args[0];
|
||||
}
|
||||
if (args.length > 1) {
|
||||
name = args[1];
|
||||
}
|
||||
BatchCommandLineLauncher command = new BatchCommandLineLauncher();
|
||||
int result = command.start(path, name);
|
||||
System.exit(result);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.repeat.ExitCodeMapper;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract class for mapping ExitCodes from the Batch framework to
|
||||
* JVM Return Codes
|
||||
*
|
||||
* @author Stijn Maller
|
||||
* @author Lucas Ward
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Transform the exitcode known by the batchframework into a JVM return
|
||||
* value.(Must be of type int)
|
||||
* @param exitCode The exitcode which is used internally by the batch framework.
|
||||
* @return The corresponding JVM return value
|
||||
*/
|
||||
public int getExitCode(String exitCode);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.support;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.execution.bootstrap.JvmExitCodeMapper;
|
||||
|
||||
|
||||
/**
|
||||
* An implementation of JvmExitCodeMapper that can be configured
|
||||
* through the Spring ApplicationContext
|
||||
*
|
||||
* @author Stijn Maller
|
||||
* @author Lucas Ward
|
||||
*/
|
||||
|
||||
public class SimpleJvmExitCodeMapper implements JvmExitCodeMapper {
|
||||
|
||||
protected Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Map mapping;
|
||||
|
||||
public SimpleJvmExitCodeMapper(){
|
||||
mapping = new HashMap();
|
||||
mapping.put(BATCH_EXITCODE_COMPLETED,
|
||||
new Integer(JVM_EXITCODE_COMPLETED));
|
||||
mapping.put(BATCH_EXITCODE_GENERIC_ERROR,
|
||||
new Integer(JVM_EXITCODE_GENERIC_ERROR));
|
||||
mapping.put(BATCH_EXITCODE_NO_SUCH_JOBCONFIGURATION,
|
||||
new Integer(JVM_EXITCODE_NO_SUCH_JOBCONFIGURATION));
|
||||
}
|
||||
|
||||
public Map getMapping() {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supply the ExitCodeMappings
|
||||
* @param exitCodeMap A set of mappings between environment specific exit codes
|
||||
* and batch framework internal exit codes
|
||||
*/
|
||||
public void setMapping(Map exitCodeMap) {
|
||||
mapping.putAll(exitCodeMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JVM exitcode that matches a certain Batch Framework Exitcode
|
||||
* @param exitCode The exitcode of the Batch Job as known by the Batch Framework
|
||||
* @return The exitCode of the Batch Job as known by the JVM
|
||||
*/
|
||||
public int getExitCode(String exitCode) {
|
||||
|
||||
Integer statusCode = null;
|
||||
|
||||
try{
|
||||
statusCode = (Integer)mapping.get(exitCode);
|
||||
}
|
||||
catch(RuntimeException ex){
|
||||
//We still need to return an exit code, even if there is an issue with
|
||||
//the mapper.
|
||||
logger.fatal("Error mapping exit code, generic exit code returned.", ex);
|
||||
}
|
||||
|
||||
return (statusCode != null) ? statusCode.intValue() : JVM_EXITCODE_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import org.springframework.batch.core.domain.BatchStatus;
|
||||
import org.springframework.batch.core.domain.JobExecution;
|
||||
import org.springframework.batch.core.domain.JobInstance;
|
||||
import org.springframework.batch.core.domain.StepInstance;
|
||||
import org.springframework.batch.core.executor.ExitCodeExceptionClassifier;
|
||||
import org.springframework.batch.core.executor.JobExecutor;
|
||||
import org.springframework.batch.core.executor.StepExecutor;
|
||||
import org.springframework.batch.core.executor.StepExecutorFactory;
|
||||
@@ -34,6 +35,7 @@ import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.runtime.JobExecutionContext;
|
||||
import org.springframework.batch.core.runtime.StepExecutionContext;
|
||||
import org.springframework.batch.execution.step.SimpleStepExecutorFactory;
|
||||
import org.springframework.batch.execution.step.simple.SimpleExitCodeExceptionClassifier;
|
||||
import org.springframework.batch.io.exception.BatchCriticalException;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
import org.springframework.batch.repeat.RepeatContext;
|
||||
@@ -52,6 +54,8 @@ public class DefaultJobExecutor implements JobExecutor {
|
||||
private JobRepository jobRepository;
|
||||
|
||||
private StepExecutorFactory stepExecutorFactory = DEFAULT_STEP_EXECUTOR_FACTORY;
|
||||
|
||||
private ExitCodeExceptionClassifier exceptionClassifier = new SimpleExitCodeExceptionClassifier();
|
||||
|
||||
public ExitStatus run(JobConfiguration configuration, JobExecutionContext jobExecutionContext)
|
||||
throws BatchCriticalException {
|
||||
@@ -81,10 +85,12 @@ public class DefaultJobExecutor implements JobExecutor {
|
||||
}
|
||||
catch (StepInterruptedException e) {
|
||||
updateStatus(jobExecutionContext, BatchStatus.STOPPED);
|
||||
status = exceptionClassifier.classifyForExitCode(e);
|
||||
rethrow(e);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
updateStatus(jobExecutionContext, BatchStatus.FAILED);
|
||||
status = exceptionClassifier.classifyForExitCode(t);
|
||||
rethrow(t);
|
||||
}
|
||||
finally {
|
||||
@@ -154,4 +160,8 @@ public class DefaultJobExecutor implements JobExecutor {
|
||||
this.stepExecutorFactory = stepExecutorResolver;
|
||||
}
|
||||
|
||||
public void setExceptionClassifier(
|
||||
ExitCodeExceptionClassifier exceptionClassifier) {
|
||||
this.exceptionClassifier = exceptionClassifier;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
package org.springframework.batch.execution.step.simple;
|
||||
|
||||
import org.springframework.batch.core.executor.ExitCodeExceptionClassifier;
|
||||
import org.springframework.batch.core.executor.StepInterruptedException;
|
||||
import org.springframework.batch.repeat.ExitStatus;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link ExitCodeExceptionClassifier} that returns basic
|
||||
* <p>Simple implementation of {@link ExitCodeExceptionClassifier} that returns basic
|
||||
* String exit codes, and defaults to the class name of the throwable
|
||||
* for the message. Most users will want to write their own implementation
|
||||
* that creates more specific exit codes for different exception types.
|
||||
* that creates more specific exit codes for different exception types.</p>
|
||||
*
|
||||
* @author Lucas Ward
|
||||
*
|
||||
@@ -42,8 +43,15 @@ public class SimpleExitCodeExceptionClassifier implements
|
||||
*/
|
||||
public Object classify(Throwable throwable) {
|
||||
|
||||
ExitStatus exitStatus = new ExitStatus(false,
|
||||
ExitStatus exitStatus = ExitStatus.FAILED;
|
||||
|
||||
if(throwable instanceof StepInterruptedException){
|
||||
exitStatus = new ExitStatus(false, STEP_INTERRUPTED, StepInterruptedException.class.getName());
|
||||
}
|
||||
else{
|
||||
exitStatus = new ExitStatus(false,
|
||||
FATAL_EXCEPTION, throwable == null ? "" : throwable.getClass().getName());
|
||||
}
|
||||
|
||||
return exitStatus;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user