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:
lucasward
2007-09-18 07:41:33 +00:00
parent 7ddb692cef
commit 91c90e6d0c
11 changed files with 409 additions and 139 deletions

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}