Hopefully fix FieldSet changes (uncommitted) and add also some work in progress on JobLauncher interface change

This commit is contained in:
dsyer
2008-01-17 13:01:03 +00:00
parent ce9ff5d25e
commit 72167a78c6
27 changed files with 736 additions and 407 deletions

View File

@@ -18,9 +18,13 @@ package org.springframework.batch.execution.bootstrap.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.domain.NoSuchJobException;
import org.springframework.batch.core.executor.ExitCodeExceptionClassifier;
import org.springframework.batch.core.runtime.JobIdentifierFactory;
import org.springframework.batch.execution.launch.JobLauncher;
import org.springframework.batch.execution.runtime.ScheduledJobIdentifierFactory;
import org.springframework.batch.execution.step.simple.SimpleExitCodeExceptionClassifier;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.beans.factory.access.BeanFactoryLocator;
@@ -45,22 +49,22 @@ import org.springframework.util.Assert;
*
* <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
* 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 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 and returned.
* 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
* 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
* and returned.
* </p>
*
* <p>
@@ -76,8 +80,7 @@ import org.springframework.util.Assert;
* method are optional, VM arguments are used:
*
* <ul>
* <li>-Djob.configuration.path: the classpath location of the Job
* to use
* <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.
@@ -89,27 +92,33 @@ import org.springframework.util.Assert;
*/
public class BatchCommandLineLauncher {
protected static final Log logger = LogFactory
.getLog(BatchCommandLineLauncher.class);
protected static final Log logger = LogFactory.getLog(BatchCommandLineLauncher.class);
/**
* The default key for the parent context.
*/
public static final String DEFAULT_PARENT_KEY = "batchExecutionEnvironment";
/**
* 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";
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 BEAN_REF_CONTEXT_KEY = "bean.ref.context";
private JobIdentifierFactory jobIdentifierFactory = new ScheduledJobIdentifierFactory();
private BeanFactoryLocator beanFactoryLocator;
private ExitCodeMapper exitCodeMapper = new SimpleJvmExitCodeMapper();
@@ -120,16 +129,34 @@ public class BatchCommandLineLauncher {
private SystemExiter systemExiter = new JvmSystemExiter();
private String defaultJobName;
public BatchCommandLineLauncher(String beanRefContextPath) {
beanFactoryLocator = ContextSingletonBeanFactoryLocator
.getInstance(beanRefContextPath);
beanFactoryLocator = ContextSingletonBeanFactoryLocator.getInstance(beanRefContextPath);
}
/**
* Setter for the name of the {@link Job} that this launcher will run.
*
* @param jobName the job name to set
*/
public void setDefaultJobName(String defaultJobName) {
this.defaultJobName = defaultJobName;
}
/**
* Setter for {@link JobIdentifierFactory}.
*
* @param jobIdentifierFactory the {@link JobIdentifierFactory} to set
*/
public void setJobIdentifierFactory(JobIdentifierFactory jobIdentifierFactory) {
this.jobIdentifierFactory = jobIdentifierFactory;
}
/**
* Injection setter for the {@link JobLauncher}.
*
* @param launcher
* the launcher to set
* @param launcher the launcher to set
*/
public void setLauncher(JobLauncher launcher) {
this.launcher = launcher;
@@ -140,16 +167,14 @@ public class BatchCommandLineLauncher {
*
* @param exceptionClassifier
*/
public void setExceptionClassifier(
ExitCodeExceptionClassifier exceptionClassifier) {
public void setExceptionClassifier(ExitCodeExceptionClassifier exceptionClassifier) {
this.exceptionClassifier = exceptionClassifier;
}
/**
* Injection setter for the {@link JvmExitCodeMapper}.
*
* @param exitCodeMapper
* the exitCodeMapper to set
* @param exitCodeMapper the exitCodeMapper to set
*/
public void setExitCodeMapper(ExitCodeMapper exitCodeMapper) {
this.exitCodeMapper = exitCodeMapper;
@@ -174,15 +199,13 @@ public class BatchCommandLineLauncher {
}
/**
* @param path
* the path to a Spring context configuration for this job
* @param jobName
* the name of the job execution to use
* @param path the path to a Spring context configuration for this job
* @param jobName the name of the job execution to use
* @parm parentKey the key to be loaded by
* ContextSingletonBeanFactoryLocator and used as the parent context.
* ContextSingletonBeanFactoryLocator and used as the parent context.
* @throws NoSuchJobException
* @throws IllegalStateException
* if JobLauncher is not autowired by the ApplicationContext
* @throws IllegalStateException if JobLauncher is not autowired by the
* ApplicationContext
*/
int start(String path, String jobName, String parentKey) {
@@ -190,8 +213,8 @@ public class BatchCommandLineLauncher {
ClassPathXmlApplicationContext context = null;
try {
ConfigurableApplicationContext parent = (ConfigurableApplicationContext) beanFactoryLocator
.useBeanFactory(parentKey).getFactory();
ConfigurableApplicationContext parent = (ConfigurableApplicationContext) beanFactoryLocator.useBeanFactory(
parentKey).getFactory();
parent.getAutowireCapableBeanFactory().autowireBeanProperties(this,
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
@@ -200,39 +223,50 @@ public class BatchCommandLineLauncher {
path = path + ".xml";
}
context = new ClassPathXmlApplicationContext(new String[] { path },
parent);
context = new ClassPathXmlApplicationContext(new String[] { path }, parent);
context.getAutowireCapableBeanFactory().autowireBeanProperties(
this, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
context.getAutowireCapableBeanFactory().autowireBeanProperties(this,
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
Assert
.state(
launcher != null,
"JobLauncher must be provided in the parent ApplicationContext"
+ ", check the context created within classpath*:beanRefContext.xml to ensure a JobLauncher"
+ " is declared");
Assert.state(launcher != null, "JobLauncher must be provided in the parent ApplicationContext"
+ ", check the context created within classpath*:beanRefContext.xml to ensure a JobLauncher"
+ " is declared");
if (!launcher.isRunning()) {
if (jobName == null) {
status = launcher.run().getExitStatus();
} else {
status = launcher.run(jobName).getExitStatus();
if (jobName == null) {
String[] names = context.getBeanNamesForType(Job.class);
if (names.length==1) {
Job job = (Job) context.getBean(names[0]);
jobName = job.getName();
}
}
} catch (NoSuchJobException e) {
logger.fatal("Could not locate JobConfiguration \"" + jobName
+ "\"", e);
status = new ExitStatus(false,
ExitCodeMapper.NO_SUCH_JOB);
} catch (Throwable t) {
if (jobName == null) {
jobName = defaultJobName;
}
if (jobName == null) {
throw new NoSuchJobException("Null job name cannot be located.");
}
JobIdentifier runtimeInformation = jobIdentifierFactory.getJobIdentifier(jobName);
if (!launcher.isRunning()) {
status = launcher.run(runtimeInformation).getExitStatus();
}
}
catch (NoSuchJobException e) {
logger.fatal("Could not locate JobConfiguration \"" + jobName + "\"", e);
status = new ExitStatus(false, ExitCodeMapper.NO_SUCH_JOB);
}
catch (Throwable t) {
logger.fatal(t);
status = exceptionClassifier.classifyForExitCode(t);
} finally {
}
finally {
if (context != null) {
try {
context.stop();
} finally {
}
finally {
context.close();
}
}
@@ -249,29 +283,23 @@ public class BatchCommandLineLauncher {
* Spring context).
*
* @param args
* <ul>
* <li>-Djob.configuration.path: the classpath location of the
* 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>
* </ul>
* <ul>
* <li>-Djob.configuration.path: the classpath location of the
* 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>
* </ul>
*/
public static void main(String[] args) {
String path = System.getProperty(JOB_CONFIGURATION_PATH_KEY,
DEFAULT_JOB_CONFIGURATION_PATH);
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, DEFAULT_BEAN_REF_CONTEXT_PATH);
String parentKey = System.getProperty(BATCH_EXECUTION_ENVIRONMENT_KEY, DEFAULT_PARENT_KEY);
BatchCommandLineLauncher command = new BatchCommandLineLauncher(
beanRefContextPath);
BatchCommandLineLauncher command = new BatchCommandLineLauncher(beanRefContextPath);
int result = command.start(path, name, parentKey);
command.exit(result);
}

View File

@@ -19,11 +19,14 @@ package org.springframework.batch.execution.bootstrap.support;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.TypeMismatchException;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
@@ -47,8 +50,7 @@ public class TypeConverterMethodInterceptor implements MethodInterceptor {
* where exception reporting is a little weak (and in addition the class of
* the exception might not be available remotely).
*
* @param convertException
* the flag to set (default false)
* @param convertException the flag to set (default false)
*/
public void setConvertException(boolean convertException) {
this.convertException = convertException;
@@ -58,8 +60,7 @@ public class TypeConverterMethodInterceptor implements MethodInterceptor {
* Public setter for the {@link TypeConverter} property. Defaults to a
* {@link SimpleTypeConverter}.
*
* @param typeConverter
* the typeConverter to set
* @param typeConverter the typeConverter to set
*/
public void setTypeConverter(TypeConverter typeConverter) {
this.typeConverter = typeConverter;
@@ -72,20 +73,48 @@ public class TypeConverterMethodInterceptor implements MethodInterceptor {
*
* @return an object that satisfies the signature of the proxy method.
*
* @throws TypeMismatchException
* if the target method returns an object that cannot be
* converted to the desired type.
* @throws TypeMismatchException if the target method returns an object that
* cannot be converted to the desired type.
*
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
// The method called on the proxy
Method invoked = invocation.getMethod();
final Method invoked = invocation.getMethod();
// The corresponding method on the target if there is one...
Method method = ReflectionUtils.findMethod(invocation.getThis()
.getClass(), invoked.getName(), invoked.getParameterTypes());
Method method = ReflectionUtils.findMethod(invocation.getThis().getClass(), invoked.getName(), invoked
.getParameterTypes());
Object[] arguments = invocation.getArguments();
// If there was no such method look for one with String args
if (method == null) {
final List methods = new ArrayList();
ReflectionUtils.doWithMethods(invocation.getThis().getClass(), new ReflectionUtils.MethodCallback() {
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
methods.add(method);
}
}, new ReflectionUtils.MethodFilter() {
public boolean matches(Method method) {
if (method.getName().equals(invoked.getName())
&& method.getParameterTypes().length == invoked.getParameterTypes().length) {
return true;
}
return false;
}
});
if (methods.size()==1) {
method = (Method) methods.get(0);
for (int i = 0; i < arguments.length; i++) {
Object arg = arguments[i];
arguments[i] = convert(arg, method.getParameterTypes()[i]);
}
}
}
// If there was no such method do nothing... TODO: throw Exception?
if (method == null) {
@@ -96,12 +125,13 @@ public class TypeConverterMethodInterceptor implements MethodInterceptor {
Object result = null;
try {
result = ReflectionUtils.invokeMethod(method, invocation.getThis(),
invocation.getArguments());
} catch (Throwable e) {
result = ReflectionUtils.invokeMethod(method, invocation.getThis(), arguments);
}
catch (Throwable e) {
if (convertException) {
result = e;
} else {
}
else {
throw e;
}
}

View File

@@ -30,33 +30,6 @@ import org.springframework.batch.core.repository.JobExecutionAlreadyRunningExcep
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. If the
* implementation is asynchronous, the status might well be unknown.
* @throws JobExecutionAlreadyRunningException
*
*/
public JobExecution run() throws NoSuchJobException, JobExecutionAlreadyRunningException;
/**
* Start a job execution with the given name and other runtime information
* generated on the fly. The name is used to locate a job configuration, and
* the other runtime information is used to identify the job instance.
*
* @param name
* the name to assign to the job configuration
* @return the exit code from the job if it returns synchronously. If the
* implementation is asynchronous, the status might well be unknown.
*
* @throws NoSuchJobException
* @throws JobExecutionAlreadyRunningException
*/
public JobExecution run(String jobName)
throws NoSuchJobException, JobExecutionAlreadyRunningException;
/**
* Start a job execution with the given runtime information.
*

View File

@@ -38,15 +38,11 @@ import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.runtime.JobIdentifierFactory;
import org.springframework.batch.execution.job.DefaultJobExecutor;
import org.springframework.batch.execution.runtime.ScheduledJobIdentifierFactory;
import org.springframework.batch.io.exception.BatchConfigurationException;
import org.springframework.batch.repeat.interceptor.RepeatOperationsApplicationEvent;
import org.springframework.batch.statistics.StatisticsProvider;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.util.Assert;
@@ -58,11 +54,10 @@ import org.springframework.util.Assert;
* @see JobLauncher
* @author Dave Syer
*/
public class SimpleJobLauncher implements JobLauncher, InitializingBean,
ApplicationListener, ApplicationEventPublisherAware, StatisticsProvider {
public class SimpleJobLauncher implements JobLauncher, InitializingBean, ApplicationEventPublisherAware,
StatisticsProvider {
protected static final Log logger = LogFactory
.getLog(SimpleJobLauncher.class);
protected static final Log logger = LogFactory.getLog(SimpleJobLauncher.class);
private JobExecutor jobExecutor = new DefaultJobExecutor();
@@ -80,12 +75,6 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
private List listeners = new ArrayList();
private String jobName;
// Do not autostart by default - allow user to set a job
// later and then manually start:
private volatile boolean autoStart = false;
private JobIdentifierFactory jobIdentifierFactory = new ScheduledJobIdentifierFactory();
private final Object monitor = new Object();
@@ -98,53 +87,28 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
/**
* Setter for {@link JobIdentifierFactory}.
*
* @param jobIdentifierFactory
* the {@link JobIdentifierFactory} to set
* @param jobIdentifierFactory the {@link JobIdentifierFactory} to set
*/
public void setJobIdentifierFactory(
JobIdentifierFactory jobIdentifierFactory) {
public void setJobIdentifierFactory(JobIdentifierFactory jobIdentifierFactory) {
this.jobIdentifierFactory = jobIdentifierFactory;
}
/**
* Setter for the {@link Job} that this launcher will run.
*
* @param jobName
* the job name to set
*/
public void setJobName(String jobName) {
this.jobName = jobName;
}
/**
* Setter for autostart flag. If this is true then the container will be
* started when the Spring context is refreshed. Defaults to false.
*
* @param autoStart
*/
public void setAutoStart(boolean autoStart) {
this.autoStart = autoStart;
}
/**
* Public setter for the listeners property.
*
* @param listeners
* the listeners to set - a list of {@link JobExecutionListener}.
* @param listeners the listeners to set - a list of
* {@link JobExecutionListener}.
*/
public void setJobExecutionListeners(List listeners) {
this.listeners = listeners;
}
/**
* Setter for injection of {@link JobLocator}. Mandatory with
* no default.
* Setter for injection of {@link JobLocator}. Mandatory with no default.
*
* @param jobLocator
* the jobLocator to set
* @param jobLocator the jobLocator to set
*/
public void setJobLocator(
JobLocator jobLocator) {
public void setJobLocator(JobLocator jobLocator) {
this.jobLocator = jobLocator;
}
@@ -188,8 +152,7 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
Assert.notNull(jobExecutor);
Assert.notNull(jobRepository);
SimpleJobExecutorFacade jobExecutorFacade = new SimpleJobExecutorFacade();
jobExecutorFacade
.setJobLocator(jobLocator);
jobExecutorFacade.setJobLocator(jobLocator);
jobExecutorFacade.setJobExecutionListeners(listeners);
jobExecutorFacade.setJobExecutor(jobExecutor);
jobExecutorFacade.setJobRepository(jobRepository);
@@ -197,34 +160,6 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
}
}
/**
* If autostart flag is on, initialise on context start-up and call
* {@link #run()}.
*
* @throws BatchConfigurationException
* if the job tries to but cannot start because of a
* {@link NoSuchJobException}.
*
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*
*/
public void onApplicationEvent(ApplicationEvent event) {
if ((event instanceof ContextRefreshedEvent) && this.autoStart
&& !isRunning()) {
try {
run();
} catch (NoSuchJobException e) {
throw new BatchConfigurationException(
"Cannot start job on context refresh because it does not exist",
e);
} catch (JobExecutionAlreadyRunningException e) {
throw new BatchConfigurationException(
"Cannot start job on context refresh because it is already running",
e);
}
}
}
/**
* This method is wrapped in a Runnable by {@link #run(JobIdentifier)}, so
* that the internal housekeeping is done consistently. Subclasses should be
@@ -234,21 +169,21 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
* @return
* @throws NoSuchJobException
*/
protected final void runInternal(JobExecution execution)
throws NoSuchJobException {
protected final void runInternal(JobExecution execution) throws NoSuchJobException {
JobIdentifier jobIdentifier = execution.getJobInstance().getIdentifier();
if (getJobExecution(jobIdentifier)==null) {
logger.info("Job already stopped (not launching): "+jobIdentifier);
if (getJobExecution(jobIdentifier) == null) {
logger.info("Job already stopped (not launching): " + jobIdentifier);
return;
}
try {
logger.info("Launching: "+jobIdentifier);
logger.info("Launching: " + jobIdentifier);
jobExecutorFacade.start(execution);
logger.info("Completed successfully: "+jobIdentifier);
} finally {
logger.info("Completed successfully: " + jobIdentifier);
}
finally {
unregister(jobIdentifier);
}
@@ -257,23 +192,19 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
/**
* Start the job using the task executor provided.
*
* @throws NoSuchJobException
* if the identifier cannot be used to locate a
* {@link Job}.
* @throws NoSuchJobException if the identifier cannot be used to locate a
* {@link Job}.
*
* @see org.springframework.batch.execution.launch.SimpleJobLauncher#run(org.springframework.batch.core.domain.JobIdentifier)
*/
public JobExecution run(final JobIdentifier jobIdentifier)
throws NoSuchJobException,
public JobExecution run(final JobIdentifier jobIdentifier) throws NoSuchJobException,
JobExecutionAlreadyRunningException {
if (getJobExecution(jobIdentifier) != null) {
throw new JobExecutionAlreadyRunningException(
"A job is already executing with this identifier: ["
+ jobIdentifier + "]");
throw new JobExecutionAlreadyRunningException("A job is already executing with this identifier: ["
+ jobIdentifier + "]");
}
final JobExecution execution = jobExecutorFacade
.createExecutionFrom(jobIdentifier);
final JobExecution execution = jobExecutorFacade.createExecutionFrom(jobIdentifier);
// TODO: throw JobExecutionAlreadyRunningException if it is in a running
// state (someone else launched it)
final JobExecutionHolder holder = register(execution);
@@ -284,23 +215,21 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
synchronized (monitor) {
if (isInternalRunning(jobIdentifier)) {
logger.info("This job is already running, so not re-launched: "+jobIdentifier);
logger.info("This job is already running, so not re-launched: " + jobIdentifier);
return;
}
}
holder.start();
runInternal(execution);
} catch (NoSuchJobException e) {
applicationEventPublisher
.publishEvent(new RepeatOperationsApplicationEvent(
jobIdentifier, "No such job",
RepeatOperationsApplicationEvent.ERROR));
logger.error(
"Job could not be located inside Runnable for identifier: ["
+ jobIdentifier + "]", e);
} finally {
}
catch (NoSuchJobException e) {
applicationEventPublisher.publishEvent(new RepeatOperationsApplicationEvent(jobIdentifier,
"No such job", RepeatOperationsApplicationEvent.ERROR));
logger.error("Job could not be located inside Runnable for identifier: [" + jobIdentifier + "]", e);
}
finally {
holder.stop();
}
}
@@ -310,59 +239,14 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
}
/**
* 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 NoSuchJobException
* @throws JobExecutionAlreadyRunningException
*/
public JobExecution run(String name)
throws NoSuchJobException,
JobExecutionAlreadyRunningException {
if (name == null) {
throw new NoSuchJobException(
"Null job name cannot be located.");
}
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 Job}.
*
* @throws NoSuchJobException
*
* @throws NoSuchJobException
* if the job cannot be located
* @throws JobExecutionAlreadyRunningException
*
* @see #setJobIdentifierFactory(JobIdentifierFactory)
* @see org.springframework.context.Lifecycle#start()
*/
public JobExecution run() throws NoSuchJobException,
JobExecutionAlreadyRunningException {
if (jobName != null) {
return this.run(jobName);
}
throw new NoSuchJobException(
"Null default job name cannot be located.");
}
/**
* Extension point for subclasses to stop a specific job.
*
* @throws NoSuchJobExecutionException
*/
protected void doStop(JobIdentifier jobIdentifier)
throws NoSuchJobExecutionException {
protected void doStop(JobIdentifier jobIdentifier) throws NoSuchJobExecutionException {
JobExecution execution = getJobExecution(jobIdentifier);
logger.info("Stopping job: "+jobIdentifier);
logger.info("Stopping job: " + jobIdentifier);
if (execution != null) {
jobExecutorFacade.stop(execution);
}
@@ -378,12 +262,12 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
* @see org.springframework.batch.execution.launch.JobLauncher#stop()
*/
final public void stop() {
for (Iterator iter = new HashSet(registry.keySet()).iterator(); iter
.hasNext();) {
for (Iterator iter = new HashSet(registry.keySet()).iterator(); iter.hasNext();) {
JobIdentifier context = (JobIdentifier) iter.next();
try {
stop(context);
} catch (NoSuchJobExecutionException e) {
}
catch (NoSuchJobExecutionException e) {
logger.error(e);
}
}
@@ -398,8 +282,7 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
* @see org.springframework.batch.execution.launch.JobLauncher#stop(org.springframework.batch.core.domain.JobIdentifier)
* @see BatchContainer#stop(JobRuntimeInformation))
*/
final public void stop(JobIdentifier runtimeInformation)
throws NoSuchJobExecutionException {
final public void stop(JobIdentifier runtimeInformation) throws NoSuchJobExecutionException {
synchronized (monitor) {
doStop(runtimeInformation);
}
@@ -437,8 +320,7 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
private boolean isInternalRunning(JobIdentifier jobIdentifier) {
synchronized (registry) {
JobExecutionHolder jobExecutionHolder = getJobExecutionHolder(jobIdentifier);
return isRunning(jobIdentifier)
&& jobExecutionHolder!=null && jobExecutionHolder.isRunning();
return isRunning(jobIdentifier) && jobExecutionHolder != null && jobExecutionHolder.isRunning();
}
}
@@ -447,10 +329,9 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
* {@link JobIdentifier} to see if it is running. As long as at least one
* job is running the launcher is deemed to be running.
*
* @param jobIdentifier
* a {@link JobIdentifier}
* @param jobIdentifier a {@link JobIdentifier}
* @return always true. Subclasses can override and provide more accurate
* information.
* information.
*/
protected boolean isRunning(JobIdentifier jobIdentifier) {
return true;
@@ -515,8 +396,7 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
* Setter for the {@link TaskExecutor}. Defaults to a
* {@link SyncTaskExecutor}.
*
* @param taskExecutor
* the taskExecutor to set
* @param taskExecutor the taskExecutor to set
*/
public void setTaskExecutor(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
@@ -532,29 +412,32 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
* only.
*
* @return Properties representing the {@link JobExecution} objects passed
* up from the underlying execution. If there are no jobs running it
* will be empty.
* up from the underlying execution. If there are no jobs running it will be
* empty.
*/
public Properties getStatistics() {
if (jobExecutorFacade instanceof StatisticsProvider) {
return ((StatisticsProvider) jobExecutorFacade).getStatistics();
} else {
}
else {
return new Properties();
}
}
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
private class JobExecutionHolder {
private static final int NEW = 0;
private static final int STARTED = 1;
private static final int STOPPED = 2;
private JobExecution execution;
private int status = NEW;
public JobExecutionHolder(JobExecution execution) {
@@ -566,7 +449,7 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean,
}
boolean isRunning() {
return status==STARTED;
return status == STARTED;
}
void start() {

View File

@@ -3,6 +3,7 @@ DROP TABLE $!{IFEXISTSBEFORE} BATCH_STEP_EXECUTION $!{IFEXISTS};
DROP TABLE $!{IFEXISTSBEFORE} BATCH_JOB_EXECUTION $!{IFEXISTS};
DROP TABLE $!{IFEXISTSBEFORE} BATCH_STEP_INSTANCE $!{IFEXISTS};
DROP TABLE $!{IFEXISTSBEFORE} BATCH_JOB_INSTANCE $!{IFEXISTS};
DROP TABLE $!{IFEXISTSBEFORE} BATCH_JOB_INSTANCE_PROPERTIES $!{IFEXISTS};
DROP ${SEQUENCE} $!{IFEXISTSBEFORE} BATCH_STEP_EXECUTION_SEQ $!{IFEXISTS};
DROP ${SEQUENCE} $!{IFEXISTSBEFORE} BATCH_STEP_SEQ $!{IFEXISTS};

View File

@@ -91,7 +91,7 @@ public class BatchCommandLineLauncherTests extends TestCase {
assertEquals(ExitCodeMapper.JVM_EXITCODE_COMPLETED, systemExiter
.getStatus());
assertEquals(jobLauncher.getLastRunCalled(),
StubJobLauncher.RUN_NO_ARGS);
StubJobLauncher.RUN_JOB_IDENTIFIER);
}
/**
@@ -113,7 +113,7 @@ public class BatchCommandLineLauncherTests extends TestCase {
assertEquals(ExitCodeMapper.JVM_EXITCODE_COMPLETED, systemExiter
.getStatus());
assertEquals(jobLauncher.getLastRunCalled(),
StubJobLauncher.RUN_JOB_NAME);
StubJobLauncher.RUN_JOB_IDENTIFIER);
}
private void setReturnValue(ExitStatus status) {

View File

@@ -29,17 +29,6 @@ public class StubJobLauncher implements JobLauncher {
return isRunning;
}
public JobExecution run() throws NoSuchJobException {
lastRunCalled = RUN_NO_ARGS;
return returnValue;
}
public JobExecution run(String jobName)
throws NoSuchJobException {
lastRunCalled = RUN_JOB_NAME;
return returnValue;
}
public JobExecution run(JobIdentifier jobIdentifier)
throws NoSuchJobException {
lastRunCalled = RUN_JOB_IDENTIFIER;

View File

@@ -1,7 +1,9 @@
package org.springframework.batch.execution.bootstrap.support;
import java.beans.PropertyEditorSupport;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import junit.framework.TestCase;
@@ -70,6 +72,21 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
assertEquals("FOO:true", proxy.getBean());
}
public void testInvokeWithMethodParameterConversionToBoolean() throws Exception {
ProxyFactory factory = new ProxyFactory(Test.class, interceptor);
factory.setTarget(new TestBean(true));
Test proxy = (Test) factory.getProxy();
assertEquals("flag:true", proxy.grab("true"));
}
public void testInvokeWithMethodParameterConversionToPattern() throws Exception {
ProxyFactory factory = new ProxyFactory(Test.class, interceptor);
factory.setTarget(new TestBean(true));
Test proxy = (Test) factory.getProxy();
Pattern pattern = Pattern.compile("[a-z]*");
assertEquals(pattern.toString(), proxy.relayPattern("[a-z]*"));
}
public void testInvalidConversion() throws Exception {
ProxyFactory factory = new ProxyFactory(Test.class, interceptor);
factory.setTarget(new TestBean(true));
@@ -95,16 +112,37 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
assertEquals(testCase, proxy.getInvalid());
}
public void testInvokeWithMethodParameterConversionWithPropertyEditor() throws Exception {
final TestBean bean = new TestBean(false);
SimpleTypeConverter converter = new SimpleTypeConverter();
converter.registerCustomEditor(TestBean.class, new PropertyEditorSupport() {
public void setAsText(String text) throws IllegalArgumentException {
setValue(bean);
}
});
interceptor.setTypeConverter(converter);
ProxyFactory factory = new ProxyFactory(Test.class, interceptor);
factory.setTarget(new TestBean(true));
Test proxy = (Test) factory.getProxy();
assertEquals(bean.toString(), proxy.relayBean("foo"));
}
public interface Test {
boolean isTest();
String getBean();
String relayPattern(String pattern);
TestCase getInvalid();
int getValue();
void operate();
String grab(String value);
String relayBean(String value);
}
// N.B. TestBean intentionally does not implement Test!
@@ -124,6 +162,14 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
return this;
}
public TestBean relayBean(TestBean bean) {
return bean;
}
public Pattern relayPattern(Pattern pattern) {
return pattern;
}
public TestBean getInvalid() {
return this;
}
@@ -135,6 +181,10 @@ public class TypeConverterMethodInterceptorTests extends TestCase {
public void operate() {
list.add("FOO");
}
public String grab(boolean flag) {
return "flag:"+flag;
}
public String toString() {
return "FOO:" + test;

View File

@@ -18,11 +18,12 @@ package org.springframework.batch.execution.launch;
import junit.framework.TestCase;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.NoSuchJobException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.runtime.SimpleJobIdentifier;
import org.springframework.batch.core.runtime.SimpleJobIdentifierFactory;
public class SimpleJobLauncherTests extends TestCase {
@@ -40,8 +41,14 @@ public class SimpleJobLauncherTests extends TestCase {
public void testInitializeWithNoConfiguration() throws Exception {
final SimpleJobLauncher launcher = new SimpleJobLauncher();
launcher.setJobExecutorFacade(new SimpleJobExecutorFacade() {
public JobExecution createExecutionFrom(JobIdentifier jobIdentifier) throws NoSuchJobException,
JobExecutionAlreadyRunningException {
throw new NoSuchJobException("No null job, stupid!");
}
});
try {
launcher.run();
launcher.run(new SimpleJobIdentifier(null));
// should do nothing
fail("Expected NoSuchJobConfigurationException");
} catch (NoSuchJobException e) {
@@ -56,10 +63,9 @@ public class SimpleJobLauncherTests extends TestCase {
launcher.setJobIdentifierFactory(new SimpleJobIdentifierFactory());
InterruptibleFacade jobExecutorFacade = new InterruptibleFacade();
launcher.setJobExecutorFacade(jobExecutorFacade);
launcher.setJobName(new Job("foo").getName());
launcher.run();
launcher.run(new SimpleJobIdentifier("foo"));
assertFalse(launcher.isRunning());
launcher.run();
launcher.run(new SimpleJobIdentifier("foo"));
// Both jobs finished running because they were not launched in a new
// Thread
assertFalse(launcher.isRunning());

View File

@@ -25,7 +25,6 @@ import java.util.TimerTask;
import junit.framework.TestCase;
import org.easymock.MockControl;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobIdentifier;
import org.springframework.batch.core.domain.JobInstance;
@@ -57,9 +56,8 @@ public class TaskExecutorJobLauncherTests extends TestCase {
InterruptibleContainer container = new InterruptibleContainer();
launcher.setJobExecutorFacade(container);
launcher.setJobName(new Job("foo").getName());
JobExecution execution = launcher.run();
JobExecution execution = launcher.run(new SimpleJobIdentifier("foo"));
// give the thread some time to start up...
Thread.sleep(100);
assertTrue(launcher.isRunning());
@@ -91,9 +89,8 @@ public class TaskExecutorJobLauncherTests extends TestCase {
InterruptibleContainer container = new InterruptibleContainer();
launcher.setJobExecutorFacade(container);
launcher.setJobName("foo");
JobExecution execution = launcher.run();
JobExecution execution = launcher.run(new SimpleJobIdentifier("foo"));
// give the thread some time to start up...
Thread.sleep(100);
// The launcher thinks it has started the job...
@@ -117,14 +114,13 @@ public class TaskExecutorJobLauncherTests extends TestCase {
InterruptibleContainer container = new InterruptibleContainer();
launcher.setJobExecutorFacade(container);
launcher.setJobName(new Job("foo").getName());
launcher.run();
launcher.run(new SimpleJobIdentifier("foo"));
// give the thread some time to start up:
Thread.sleep(100);
assertTrue(launcher.isRunning());
try {
launcher.run();
launcher.run(new SimpleJobIdentifier("foo"));
fail("Expected JobExecutionAlreadyRunningException");
} catch (JobExecutionAlreadyRunningException e) {
// expected
@@ -137,12 +133,6 @@ public class TaskExecutorJobLauncherTests extends TestCase {
assertFalse(launcher.isRunning());
}
public void testNormalApplicationEventNotRecognized() throws Exception {
launcher.onApplicationEvent(new ApplicationEvent("foo") {
});
// nothing happens
}
public void testStatisticsRetrieved() throws Exception {
MockControl control = MockControl
.createControl(JobExecutorFacadeWithStatistics.class);

View File

@@ -3,6 +3,7 @@ DROP TABLE BATCH_STEP_EXECUTION IF EXISTS;
DROP TABLE BATCH_JOB_EXECUTION IF EXISTS;
DROP TABLE BATCH_STEP_INSTANCE IF EXISTS;
DROP TABLE BATCH_JOB_INSTANCE IF EXISTS;
DROP TABLE BATCH_JOB_INSTANCE_PROPERTIES IF EXISTS;
DROP TABLE BATCH_STEP_EXECUTION_SEQ IF EXISTS;
DROP TABLE BATCH_STEP_SEQ IF EXISTS;