Change svn:eol-style to LF

This commit is contained in:
dsyer
2008-03-06 21:59:13 +00:00
parent ee5746a860
commit f5e85d5f63
372 changed files with 33472 additions and 33470 deletions

View File

@@ -1,113 +1,113 @@
/*
* 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.configuration;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import org.springframework.batch.core.configuration.JobLocator;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.repository.DuplicateJobException;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.Assert;
/**
* A {@link BeanPostProcessor} that registers {@link Job} beans
* with a {@link JobRegistry}. Include a bean of this type along
* with your job configuration, and use the same
* {@link JobRegistry} as a {@link JobLocator} when
* you need to locate a {@link JobLocator} to launch.
*
* @author Dave Syer
*
*/
public class JobRegistryBeanPostProcessor implements BeanPostProcessor, InitializingBean, DisposableBean {
// It doesn't make sense for this to have a default value...
private JobRegistry jobConfigurationRegistry = null;
private Collection jobNames = new HashSet();
/**
* Injection setter for {@link JobRegistry}.
*
* @param jobRegistry the jobConfigurationRegistry to set
*/
public void setJobRegistry(JobRegistry jobRegistry) {
this.jobConfigurationRegistry = jobRegistry;
}
/**
* Make sure the registry is set before use.
*
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(jobConfigurationRegistry, "JobConfigurationRegistry must not be null");
}
/**
* De-register all the {@link Job} instances that were
* regsistered by this post processor.
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
for (Iterator iter = jobNames.iterator(); iter.hasNext();) {
String name = (String) iter.next();
jobConfigurationRegistry.unregister(name);
}
jobNames.clear();
}
/**
* If the bean is an instance of {@link Job} then register it.
* @throws FatalBeanException if there is a
* {@link DuplicateJobException}.
*
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object,
* java.lang.String)
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Job) {
Job job = (Job) bean;
try {
jobConfigurationRegistry.register(new ReferenceJobFactory(job));
jobNames.add(job.getName());
}
catch (DuplicateJobException e) {
throw new FatalBeanException("Cannot register job configuration", e);
}
}
return bean;
}
/**
* Do nothing.
*
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object,
* java.lang.String)
*/
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
/*
* 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.configuration;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import org.springframework.batch.core.configuration.JobLocator;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.repository.DuplicateJobException;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.Assert;
/**
* A {@link BeanPostProcessor} that registers {@link Job} beans
* with a {@link JobRegistry}. Include a bean of this type along
* with your job configuration, and use the same
* {@link JobRegistry} as a {@link JobLocator} when
* you need to locate a {@link JobLocator} to launch.
*
* @author Dave Syer
*
*/
public class JobRegistryBeanPostProcessor implements BeanPostProcessor, InitializingBean, DisposableBean {
// It doesn't make sense for this to have a default value...
private JobRegistry jobConfigurationRegistry = null;
private Collection jobNames = new HashSet();
/**
* Injection setter for {@link JobRegistry}.
*
* @param jobRegistry the jobConfigurationRegistry to set
*/
public void setJobRegistry(JobRegistry jobRegistry) {
this.jobConfigurationRegistry = jobRegistry;
}
/**
* Make sure the registry is set before use.
*
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception {
Assert.notNull(jobConfigurationRegistry, "JobConfigurationRegistry must not be null");
}
/**
* De-register all the {@link Job} instances that were
* regsistered by this post processor.
* @see org.springframework.beans.factory.DisposableBean#destroy()
*/
public void destroy() throws Exception {
for (Iterator iter = jobNames.iterator(); iter.hasNext();) {
String name = (String) iter.next();
jobConfigurationRegistry.unregister(name);
}
jobNames.clear();
}
/**
* If the bean is an instance of {@link Job} then register it.
* @throws FatalBeanException if there is a
* {@link DuplicateJobException}.
*
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object,
* java.lang.String)
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Job) {
Job job = (Job) bean;
try {
jobConfigurationRegistry.register(new ReferenceJobFactory(job));
jobNames.add(job.getName());
}
catch (DuplicateJobException e) {
throw new FatalBeanException("Cannot register job configuration", e);
}
}
return bean;
}
/**
* Do nothing.
*
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object,
* java.lang.String)
*/
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}

View File

@@ -1,95 +1,95 @@
/*
* 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.configuration;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.springframework.batch.core.configuration.JobFactory;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.ListableJobRegistry;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.repository.DuplicateJobException;
import org.springframework.batch.core.repository.NoSuchJobException;
import org.springframework.util.Assert;
/**
* Simple map-based implementation of {@link JobRegistry}. Access to the map is
* synchronized, guarded by an internal lock.
*
* @author Dave Syer
*
*/
public class MapJobRegistry implements ListableJobRegistry {
private Map map = new HashMap();
/*
* (non-Javadoc)
* @see org.springframework.batch.container.common.configuration.JobConfigurationRegistry#registerJobConfiguration(org.springframework.batch.container.common.configuration.JobConfiguration)
*/
public void register(JobFactory jobFactory) throws DuplicateJobException {
Assert.notNull(jobFactory);
String name = jobFactory.getJobName();
Assert.notNull(name, "Job configuration must have a name.");
synchronized (map) {
if (map.containsKey(name)) {
throw new DuplicateJobException("A job configuration with this name [" + name
+ "] was already registered");
}
map.put(name, jobFactory);
}
}
/*
* (non-Javadoc)
* @see org.springframework.batch.container.common.configuration.JobConfigurationRegistry#unregister(org.springframework.batch.container.common.configuration.JobConfiguration)
*/
public void unregister(String name) {
Assert.notNull(name, "Job configuration must have a name.");
synchronized (map) {
map.remove(name);
}
}
/*
* (non-Javadoc)
* @see org.springframework.batch.container.common.configuration.JobConfigurationLocator#getJobConfiguration(java.lang.String)
*/
public Job getJob(String name) throws NoSuchJobException {
synchronized (map) {
if (!map.containsKey(name)) {
throw new NoSuchJobException("No job configuration with the name [" + name + "] was registered");
}
return (Job) ((JobFactory) map.get(name)).createJob();
}
}
/*
* (non-Javadoc)
* @see org.springframework.batch.container.common.configuration.ListableJobConfigurationRegistry#getJobConfigurations()
*/
public Collection getJobNames() {
synchronized (map) {
return Collections.unmodifiableCollection(new HashSet(map.keySet()));
}
}
}
/*
* 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.configuration;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.springframework.batch.core.configuration.JobFactory;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.ListableJobRegistry;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.repository.DuplicateJobException;
import org.springframework.batch.core.repository.NoSuchJobException;
import org.springframework.util.Assert;
/**
* Simple map-based implementation of {@link JobRegistry}. Access to the map is
* synchronized, guarded by an internal lock.
*
* @author Dave Syer
*
*/
public class MapJobRegistry implements ListableJobRegistry {
private Map map = new HashMap();
/*
* (non-Javadoc)
* @see org.springframework.batch.container.common.configuration.JobConfigurationRegistry#registerJobConfiguration(org.springframework.batch.container.common.configuration.JobConfiguration)
*/
public void register(JobFactory jobFactory) throws DuplicateJobException {
Assert.notNull(jobFactory);
String name = jobFactory.getJobName();
Assert.notNull(name, "Job configuration must have a name.");
synchronized (map) {
if (map.containsKey(name)) {
throw new DuplicateJobException("A job configuration with this name [" + name
+ "] was already registered");
}
map.put(name, jobFactory);
}
}
/*
* (non-Javadoc)
* @see org.springframework.batch.container.common.configuration.JobConfigurationRegistry#unregister(org.springframework.batch.container.common.configuration.JobConfiguration)
*/
public void unregister(String name) {
Assert.notNull(name, "Job configuration must have a name.");
synchronized (map) {
map.remove(name);
}
}
/*
* (non-Javadoc)
* @see org.springframework.batch.container.common.configuration.JobConfigurationLocator#getJobConfiguration(java.lang.String)
*/
public Job getJob(String name) throws NoSuchJobException {
synchronized (map) {
if (!map.containsKey(name)) {
throw new NoSuchJobException("No job configuration with the name [" + name + "] was registered");
}
return (Job) ((JobFactory) map.get(name)).createJob();
}
}
/*
* (non-Javadoc)
* @see org.springframework.batch.container.common.configuration.ListableJobConfigurationRegistry#getJobConfigurations()
*/
public Collection getJobNames() {
synchronized (map) {
return Collections.unmodifiableCollection(new HashSet(map.keySet()));
}
}
}

View File

@@ -1,54 +1,54 @@
/*
* 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.launch;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
/**
* Simple interface for controlling jobs, including possible ad-hoc executions,
* based on different runtime identifiers. It is extremely important to note
* that this interface makes absolutely no guarantees about whether or not calls
* to it are executed synchronously or asynchronously. The javadocs for specific
* implementations should be checked to ensure callers fully understand how the
* job will be run.
*
* @author Lucas Ward
* @author Dave Syer
*/
public interface JobLauncher {
/**
* Start a job execution for the given {@link Job} and {@link JobParameters}.
*
* @return the exit code from the job if it returns synchronously. If the
* implementation is asynchronous, the status might well be unknown.
*
* @throws JobExecutionAlreadyRunningException if the JobInstance identified
* by the properties already has an execution running.
* @throws IllegalArgumentException if the job or jobInstanceProperties are
* null.
* @throws JobRestartException if the job has been run before and
* circumstances that preclude a re-start.
*/
public JobExecution run(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException,
JobRestartException;
}
/*
* 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.launch;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
/**
* Simple interface for controlling jobs, including possible ad-hoc executions,
* based on different runtime identifiers. It is extremely important to note
* that this interface makes absolutely no guarantees about whether or not calls
* to it are executed synchronously or asynchronously. The javadocs for specific
* implementations should be checked to ensure callers fully understand how the
* job will be run.
*
* @author Lucas Ward
* @author Dave Syer
*/
public interface JobLauncher {
/**
* Start a job execution for the given {@link Job} and {@link JobParameters}.
*
* @return the exit code from the job if it returns synchronously. If the
* implementation is asynchronous, the status might well be unknown.
*
* @throws JobExecutionAlreadyRunningException if the JobInstance identified
* by the properties already has an execution running.
* @throws IllegalArgumentException if the job or jobInstanceProperties are
* null.
* @throws JobRestartException if the job has been run before and
* circumstances that preclude a re-start.
*/
public JobExecution run(Job job, JobParameters jobParameters) throws JobExecutionAlreadyRunningException,
JobRestartException;
}

View File

@@ -1,30 +1,30 @@
package org.springframework.batch.execution.launch.support;
/**
*
* This interface should be implemented when an environment calling the batch famework has specific
* requirements regarding the process return codes.
*
* @param The type of returncode expected by the environment
* @author Stijn Maller
* @author Lucas Ward
* @author Dave Syer
*/
public interface ExitCodeMapper {
static int JVM_EXITCODE_COMPLETED = 0;
static int JVM_EXITCODE_GENERIC_ERROR = 1;
static int JVM_EXITCODE_JOB_ERROR = 2;
public static final String NO_SUCH_JOB = "NO_SUCH_JOB";
public static final String JOB_NOT_PROVIDED = "JOB_NOT_PROVIDED";
/**
* Transform the exitcode known by the batchframework into an exitcode in the
* format of the calling environment.
* @param exitCode The exitcode which is used internally by the batch framework.
* @return The corresponding exitcode as known by the calling environment.
*/
public int getExitCode(String exitCode);
}
package org.springframework.batch.execution.launch.support;
/**
*
* This interface should be implemented when an environment calling the batch famework has specific
* requirements regarding the process return codes.
*
* @param The type of returncode expected by the environment
* @author Stijn Maller
* @author Lucas Ward
* @author Dave Syer
*/
public interface ExitCodeMapper {
static int JVM_EXITCODE_COMPLETED = 0;
static int JVM_EXITCODE_GENERIC_ERROR = 1;
static int JVM_EXITCODE_JOB_ERROR = 2;
public static final String NO_SUCH_JOB = "NO_SUCH_JOB";
public static final String JOB_NOT_PROVIDED = "JOB_NOT_PROVIDED";
/**
* Transform the exitcode known by the batchframework into an exitcode in the
* format of the calling environment.
* @param exitCode The exitcode which is used internally by the batch framework.
* @return The corresponding exitcode as known by the calling environment.
*/
public int getExitCode(String exitCode);
}

View File

@@ -1,74 +1,74 @@
/*
* 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.launch.support;
import java.util.Properties;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.execution.launch.JobLauncher;
/**
* Interface to expose for remote management of jobs. Similar to
* {@link JobLauncher}, but replaces {@link JobExecution} and
* {@link JobIdentifier} with Strings in return types and method parameters, so
* it can be inspected by remote clients like the jconsole from the JRE without
* any links to Spring Batch.
*
* @author Dave Syer
*
*/
public interface ExportedJobLauncher {
/**
* Launch a job with the given name.
*
* @param name the name of the job to launch
* @return a representation of the {@link JobExecution} returned by a
* {@link JobLauncher}.
*/
String run(String name);
/**
* Launch a job with the given name and parameters.
*
* @param name the name of the job to launch
* @return a representation of the {@link JobExecution} returned by a
* {@link JobLauncher}.
*/
String run(String name, String params);
/**
* Stop all running jobs.
*/
void stop();
/**
* Enquire if any jobs launched here are still running.
*
* @return true if any jobs are running.
*/
boolean isRunning();
/**
* Query statistics of currently executing jobs.
*
* @return properties representing last known state of currently executing
* jobs
*/
public Properties getStatistics();
}
/*
* 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.launch.support;
import java.util.Properties;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.execution.launch.JobLauncher;
/**
* Interface to expose for remote management of jobs. Similar to
* {@link JobLauncher}, but replaces {@link JobExecution} and
* {@link JobIdentifier} with Strings in return types and method parameters, so
* it can be inspected by remote clients like the jconsole from the JRE without
* any links to Spring Batch.
*
* @author Dave Syer
*
*/
public interface ExportedJobLauncher {
/**
* Launch a job with the given name.
*
* @param name the name of the job to launch
* @return a representation of the {@link JobExecution} returned by a
* {@link JobLauncher}.
*/
String run(String name);
/**
* Launch a job with the given name and parameters.
*
* @param name the name of the job to launch
* @return a representation of the {@link JobExecution} returned by a
* {@link JobLauncher}.
*/
String run(String name, String params);
/**
* Stop all running jobs.
*/
void stop();
/**
* Enquire if any jobs launched here are still running.
*
* @return true if any jobs are running.
*/
boolean isRunning();
/**
* Query statistics of currently executing jobs.
*
* @return properties representing last known state of currently executing
* jobs
*/
public Properties getStatistics();
}

View File

@@ -1,40 +1,40 @@
/*
* 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.launch.support;
/**
* Implementation of the {@link SystemExiter} interface that calls the standards
* System.exit method. It should be noted that there will be no unit tests for
* this class, since there is only one line of actual code, that would only be
* testable by mocking System or Runtime.
*
* @author Lucas Ward
* @author Dave Syer
*
*/
public class JvmSystemExiter implements SystemExiter {
/**
* Delegate call to System.exit() with the argument provided. Do not use
* this at home children!
*
* @see org.springframework.batch.execution.launch.support.SystemExiter#exit(int)
*/
public void exit(int status) {
System.exit(status);
}
}
/*
* 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.launch.support;
/**
* Implementation of the {@link SystemExiter} interface that calls the standards
* System.exit method. It should be noted that there will be no unit tests for
* this class, since there is only one line of actual code, that would only be
* testable by mocking System or Runtime.
*
* @author Lucas Ward
* @author Dave Syer
*
*/
public class JvmSystemExiter implements SystemExiter {
/**
* Delegate call to System.exit() with the argument provided. Do not use
* this at home children!
*
* @see org.springframework.batch.execution.launch.support.SystemExiter#exit(int)
*/
public void exit(int status) {
System.exit(status);
}
}

View File

@@ -1,88 +1,88 @@
/*
* 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.launch.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.repeat.ExitStatus;
/**
* An implementation of {@link ExitCodeMapper} that can be configured
* through a map from batch exit codes (String) to integer results.
*
* @author Stijn Maller
* @author Lucas Ward
* @author Dave Syer
*/
public class SimpleJvmExitCodeMapper implements ExitCodeMapper {
protected Log logger = LogFactory.getLog(getClass());
private Map mapping;
public SimpleJvmExitCodeMapper(){
mapping = new HashMap();
mapping.put(ExitStatus.FINISHED.getExitCode(),
new Integer(JVM_EXITCODE_COMPLETED));
mapping.put(ExitStatus.FAILED.getExitCode(),
new Integer(JVM_EXITCODE_GENERIC_ERROR));
mapping.put(ExitCodeMapper.JOB_NOT_PROVIDED,
new Integer(JVM_EXITCODE_JOB_ERROR));
mapping.put(ExitCodeMapper.NO_SUCH_JOB,
new Integer(JVM_EXITCODE_JOB_ERROR));
}
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;
}
}
/*
* 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.launch.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.repeat.ExitStatus;
/**
* An implementation of {@link ExitCodeMapper} that can be configured
* through a map from batch exit codes (String) to integer results.
*
* @author Stijn Maller
* @author Lucas Ward
* @author Dave Syer
*/
public class SimpleJvmExitCodeMapper implements ExitCodeMapper {
protected Log logger = LogFactory.getLog(getClass());
private Map mapping;
public SimpleJvmExitCodeMapper(){
mapping = new HashMap();
mapping.put(ExitStatus.FINISHED.getExitCode(),
new Integer(JVM_EXITCODE_COMPLETED));
mapping.put(ExitStatus.FAILED.getExitCode(),
new Integer(JVM_EXITCODE_GENERIC_ERROR));
mapping.put(ExitCodeMapper.JOB_NOT_PROVIDED,
new Integer(JVM_EXITCODE_JOB_ERROR));
mapping.put(ExitCodeMapper.NO_SUCH_JOB,
new Integer(JVM_EXITCODE_JOB_ERROR));
}
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

@@ -1,39 +1,39 @@
/*
* 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.launch.support;
/**
* Interface for exiting the JVM. This abstraction is only
* useful in order to allow classes that make System.exit calls
* to be testable, since calling System.exit during a unit
* test would cause the entire jvm to finish.
*
* @author Lucas Ward
*
*/
public interface SystemExiter {
/**
* Terminate the currently running Java Virtual Machine.
*
* @param status exit status.
* @throws SecurityException
* if a security manager exists and its <code>checkExit</code>
* method doesn't allow exit with the specified status.
* @see System.exit
*/
void exit(int status);
}
/*
* 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.launch.support;
/**
* Interface for exiting the JVM. This abstraction is only
* useful in order to allow classes that make System.exit calls
* to be testable, since calling System.exit during a unit
* test would cause the entire jvm to finish.
*
* @author Lucas Ward
*
*/
public interface SystemExiter {
/**
* Terminate the currently running Java Virtual Machine.
*
* @param status exit status.
* @throws SecurityException
* if a security manager exists and its <code>checkExit</code>
* method doesn't allow exit with the specified status.
* @see System.exit
*/
void exit(int status);
}

View File

@@ -1,52 +1,52 @@
package org.springframework.batch.execution.repository.dao;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Encapsulates common functionality needed by JDBC batch metadata DAOs -
* provides jdbcTemplate for subclasses and handles table prefixes.
*
* @author Robert Kasanicky
*/
public abstract class AbstractJdbcBatchMetadataDao implements InitializingBean {
/**
* Default value for the table prefix property.
*/
public static final String DEFAULT_TABLE_PREFIX = "BATCH_";
private String tablePrefix = DEFAULT_TABLE_PREFIX;
private JdbcOperations jdbcTemplate;
protected String getQuery(String base) {
return StringUtils.replace(base, "%PREFIX%", tablePrefix);
}
/**
* Public setter for the table prefix property. This will be prefixed to all
* the table names before queries are executed. Defaults to
* {@value #DEFAULT_TABLE_PREFIX}.
*
* @param tablePrefix the tablePrefix to set
*/
public void setTablePrefix(String tablePrefix) {
this.tablePrefix = tablePrefix;
}
public void setJdbcTemplate(JdbcOperations jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
protected JdbcOperations getJdbcTemplate() {
return jdbcTemplate;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(jdbcTemplate);
}
}
package org.springframework.batch.execution.repository.dao;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Encapsulates common functionality needed by JDBC batch metadata DAOs -
* provides jdbcTemplate for subclasses and handles table prefixes.
*
* @author Robert Kasanicky
*/
public abstract class AbstractJdbcBatchMetadataDao implements InitializingBean {
/**
* Default value for the table prefix property.
*/
public static final String DEFAULT_TABLE_PREFIX = "BATCH_";
private String tablePrefix = DEFAULT_TABLE_PREFIX;
private JdbcOperations jdbcTemplate;
protected String getQuery(String base) {
return StringUtils.replace(base, "%PREFIX%", tablePrefix);
}
/**
* Public setter for the table prefix property. This will be prefixed to all
* the table names before queries are executed. Defaults to
* {@value #DEFAULT_TABLE_PREFIX}.
*
* @param tablePrefix the tablePrefix to set
*/
public void setTablePrefix(String tablePrefix) {
this.tablePrefix = tablePrefix;
}
public void setJdbcTemplate(JdbcOperations jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
protected JdbcOperations getJdbcTemplate() {
return jdbcTemplate;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(jdbcTemplate);
}
}

View File

@@ -1,219 +1,219 @@
package org.springframework.batch.execution.repository.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.repeat.ExitStatus;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.util.Assert;
/**
* Jdbc implementation of {@link JobExecutionDao}. Uses sequences (via Spring's
* {@link DataFieldMaxValueIncrementer} abstraction) to create all primary keys
* before inserting a new row. Objects are checked to ensure all mandatory
* fields to be stored are not null. If any are found to be null, an
* IllegalArgumentException will be thrown. This could be left to JdbcTemplate,
* however, the exception will be fairly vague, and fails to highlight which
* field caused the exception.
*
* @author Lucas Ward
* @author Dave Syer
* @author Robert Kasanicky
*/
public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements JobExecutionDao, InitializingBean {
private static final Log logger = LogFactory.getLog(JdbcJobExecutionDao.class);
private static final int EXIT_MESSAGE_LENGTH = 250;
private static final String GET_JOB_EXECUTION_COUNT = "SELECT count(JOB_EXECUTION_ID) from %PREFIX%JOB_EXECUTION "
+ "where JOB_INSTANCE_ID = ?";
private static final String SAVE_JOB_EXECUTION = "INSERT into %PREFIX%JOB_EXECUTION(JOB_EXECUTION_ID, JOB_INSTANCE_ID, START_TIME, "
+ "END_TIME, STATUS, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE) values (?, ?, ?, ?, ?, ?, ?, ?)";
private static final String CHECK_JOB_EXECUTION_EXISTS = "SELECT COUNT(*) FROM %PREFIX%JOB_EXECUTION WHERE JOB_EXECUTION_ID = ?";
private static final String UPDATE_JOB_EXECUTION = "UPDATE %PREFIX%JOB_EXECUTION set START_TIME = ?, END_TIME = ?, "
+ " STATUS = ?, CONTINUABLE = ?, EXIT_CODE = ?, EXIT_MESSAGE = ? where JOB_EXECUTION_ID = ?";
private static final String FIND_JOB_EXECUTIONS = "SELECT JOB_EXECUTION_ID, START_TIME, END_TIME, STATUS, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE from %PREFIX%JOB_EXECUTION"
+ " where JOB_INSTANCE_ID = ?";
private static final String GET_LAST_EXECUTION = "SELECT JOB_EXECUTION_ID, START_TIME, END_TIME, STATUS, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE from %PREFIX%JOB_EXECUTION"
+ " where JOB_INSTANCE_ID = ? and START_TIME = (SELECT max(START_TIME) from %PREFIX%JOB_EXECUTION where JOB_INSTANCE_ID = ?)";
private DataFieldMaxValueIncrementer jobExecutionIncrementer;
public List findJobExecutions(final JobInstance job) {
Assert.notNull(job, "Job cannot be null.");
Assert.notNull(job.getId(), "Job Id cannot be null.");
return getJdbcTemplate().query(getQuery(FIND_JOB_EXECUTIONS), new Object[] { job.getId() },
new JobExecutionRowMapper(job));
}
/**
* @see JobDao#getJobExecutionCount(JobInstance)
* @throws IllegalArgumentException if jobId is null.
*/
public int getJobExecutionCount(JobInstance jobInstance) {
Long jobId = jobInstance.getId();
Assert.notNull(jobId, "JobId cannot be null");
Object[] parameters = new Object[] { jobId };
return getJdbcTemplate().queryForInt(getQuery(GET_JOB_EXECUTION_COUNT), parameters);
}
/**
*
* SQL implementation using Sequences via the Spring incrementer
* abstraction. Once a new id has been obtained, the JobExecution is saved
* via a SQL INSERT statement.
*
* @see JobDao#saveJobExecution(JobExecution)
* @throws IllegalArgumentException if jobExecution is null, as well as any
* of it's fields to be persisted.
*/
public void saveJobExecution(JobExecution jobExecution) {
validateJobExecution(jobExecution);
jobExecution.setId(new Long(jobExecutionIncrementer.nextLongValue()));
Object[] parameters = new Object[] { jobExecution.getId(), jobExecution.getJobId(),
jobExecution.getStartTime(), jobExecution.getEndTime(), jobExecution.getStatus().toString(),
jobExecution.getExitStatus().isContinuable() ? "Y" : "N", jobExecution.getExitStatus().getExitCode(),
jobExecution.getExitStatus().getExitDescription() };
getJdbcTemplate().update(
getQuery(SAVE_JOB_EXECUTION),
parameters,
new int[] { Types.INTEGER, Types.INTEGER, Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.CHAR,
Types.VARCHAR, Types.VARCHAR });
}
/**
* Validate JobExecution. At a minimum, JobId, StartTime, EndTime, and
* Status cannot be null.
*
* @param jobExecution
* @throws IllegalArgumentException
*/
private void validateJobExecution(JobExecution jobExecution) {
Assert.notNull(jobExecution);
Assert.notNull(jobExecution.getJobId(), "JobExecution Job-Id cannot be null.");
Assert.notNull(jobExecution.getStatus(), "JobExecution status cannot be null.");
}
/**
* Update given JobExecution using a SQL UPDATE statement. The JobExecution
* is first checked to ensure all fields are not null, and that it has an
* ID. The database is then queried to ensure that the ID exists, which
* ensures that it is valid.
*
* @see JobDao#updateJobExecution(JobExecution)
*/
public void updateJobExecution(JobExecution jobExecution) {
validateJobExecution(jobExecution);
String exitDescription = jobExecution.getExitStatus().getExitDescription();
if (exitDescription != null && exitDescription.length() > EXIT_MESSAGE_LENGTH) {
exitDescription = exitDescription.substring(0, EXIT_MESSAGE_LENGTH);
logger.debug("Truncating long message before update of JobExecution: " + jobExecution);
}
Object[] parameters = new Object[] { jobExecution.getStartTime(), jobExecution.getEndTime(),
jobExecution.getStatus().toString(), jobExecution.getExitStatus().isContinuable() ? "Y" : "N",
jobExecution.getExitStatus().getExitCode(), exitDescription, jobExecution.getId() };
if (jobExecution.getId() == null) {
throw new IllegalArgumentException("JobExecution ID cannot be null. JobExecution must be saved "
+ "before it can be updated.");
}
// Check if given JobExecution's Id already exists, if none is found it
// is invalid and
// an exception should be thrown.
if (getJdbcTemplate().queryForInt(getQuery(CHECK_JOB_EXECUTION_EXISTS), new Object[] { jobExecution.getId() }) != 1) {
throw new NoSuchObjectException("Invalid JobExecution, ID " + jobExecution.getId()
+ " not found.");
}
getJdbcTemplate().update(
getQuery(UPDATE_JOB_EXECUTION),
parameters,
new int[] { Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.CHAR, Types.VARCHAR, Types.VARCHAR,
Types.INTEGER });
}
/**
* Setter for {@link DataFieldMaxValueIncrementer} to be used when
* generating primary keys for {@link JobExecution} instances.
*
* @param jobExecutionIncrementer the {@link DataFieldMaxValueIncrementer}
*/
public void setJobExecutionIncrementer(DataFieldMaxValueIncrementer jobExecutionIncrementer) {
this.jobExecutionIncrementer = jobExecutionIncrementer;
}
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
Assert.notNull(jobExecutionIncrementer);
}
/**
* Re-usable mapper for {@link JobExecution} instances.
*
* @author Dave Syer
*
*/
private static class JobExecutionRowMapper implements RowMapper {
private JobInstance job;
public JobExecutionRowMapper(JobInstance job) {
super();
this.job = job;
}
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
JobExecution jobExecution = new JobExecution(job);
jobExecution.setId(new Long(rs.getLong(1)));
jobExecution.setStartTime(rs.getTimestamp(2));
jobExecution.setEndTime(rs.getTimestamp(3));
jobExecution.setStatus(BatchStatus.getStatus(rs.getString(4)));
jobExecution.setExitStatus(new ExitStatus("Y".equals(rs.getString(5)), rs.getString(6), rs.getString(7)));
return jobExecution;
}
}
public JobExecution getLastJobExecution(JobInstance jobInstance) {
Long id = jobInstance.getId();
List executions = getJdbcTemplate().query(getQuery(GET_LAST_EXECUTION), new Object[] { id, id },
new JobExecutionRowMapper(jobInstance));
Assert.state(executions.size() <= 1, "There must be at most one latest job execution");
if (executions.isEmpty()) {
return null;
}
else {
return (JobExecution) executions.get(0);
}
}
}
package org.springframework.batch.execution.repository.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.repeat.ExitStatus;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.util.Assert;
/**
* Jdbc implementation of {@link JobExecutionDao}. Uses sequences (via Spring's
* {@link DataFieldMaxValueIncrementer} abstraction) to create all primary keys
* before inserting a new row. Objects are checked to ensure all mandatory
* fields to be stored are not null. If any are found to be null, an
* IllegalArgumentException will be thrown. This could be left to JdbcTemplate,
* however, the exception will be fairly vague, and fails to highlight which
* field caused the exception.
*
* @author Lucas Ward
* @author Dave Syer
* @author Robert Kasanicky
*/
public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements JobExecutionDao, InitializingBean {
private static final Log logger = LogFactory.getLog(JdbcJobExecutionDao.class);
private static final int EXIT_MESSAGE_LENGTH = 250;
private static final String GET_JOB_EXECUTION_COUNT = "SELECT count(JOB_EXECUTION_ID) from %PREFIX%JOB_EXECUTION "
+ "where JOB_INSTANCE_ID = ?";
private static final String SAVE_JOB_EXECUTION = "INSERT into %PREFIX%JOB_EXECUTION(JOB_EXECUTION_ID, JOB_INSTANCE_ID, START_TIME, "
+ "END_TIME, STATUS, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE) values (?, ?, ?, ?, ?, ?, ?, ?)";
private static final String CHECK_JOB_EXECUTION_EXISTS = "SELECT COUNT(*) FROM %PREFIX%JOB_EXECUTION WHERE JOB_EXECUTION_ID = ?";
private static final String UPDATE_JOB_EXECUTION = "UPDATE %PREFIX%JOB_EXECUTION set START_TIME = ?, END_TIME = ?, "
+ " STATUS = ?, CONTINUABLE = ?, EXIT_CODE = ?, EXIT_MESSAGE = ? where JOB_EXECUTION_ID = ?";
private static final String FIND_JOB_EXECUTIONS = "SELECT JOB_EXECUTION_ID, START_TIME, END_TIME, STATUS, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE from %PREFIX%JOB_EXECUTION"
+ " where JOB_INSTANCE_ID = ?";
private static final String GET_LAST_EXECUTION = "SELECT JOB_EXECUTION_ID, START_TIME, END_TIME, STATUS, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE from %PREFIX%JOB_EXECUTION"
+ " where JOB_INSTANCE_ID = ? and START_TIME = (SELECT max(START_TIME) from %PREFIX%JOB_EXECUTION where JOB_INSTANCE_ID = ?)";
private DataFieldMaxValueIncrementer jobExecutionIncrementer;
public List findJobExecutions(final JobInstance job) {
Assert.notNull(job, "Job cannot be null.");
Assert.notNull(job.getId(), "Job Id cannot be null.");
return getJdbcTemplate().query(getQuery(FIND_JOB_EXECUTIONS), new Object[] { job.getId() },
new JobExecutionRowMapper(job));
}
/**
* @see JobDao#getJobExecutionCount(JobInstance)
* @throws IllegalArgumentException if jobId is null.
*/
public int getJobExecutionCount(JobInstance jobInstance) {
Long jobId = jobInstance.getId();
Assert.notNull(jobId, "JobId cannot be null");
Object[] parameters = new Object[] { jobId };
return getJdbcTemplate().queryForInt(getQuery(GET_JOB_EXECUTION_COUNT), parameters);
}
/**
*
* SQL implementation using Sequences via the Spring incrementer
* abstraction. Once a new id has been obtained, the JobExecution is saved
* via a SQL INSERT statement.
*
* @see JobDao#saveJobExecution(JobExecution)
* @throws IllegalArgumentException if jobExecution is null, as well as any
* of it's fields to be persisted.
*/
public void saveJobExecution(JobExecution jobExecution) {
validateJobExecution(jobExecution);
jobExecution.setId(new Long(jobExecutionIncrementer.nextLongValue()));
Object[] parameters = new Object[] { jobExecution.getId(), jobExecution.getJobId(),
jobExecution.getStartTime(), jobExecution.getEndTime(), jobExecution.getStatus().toString(),
jobExecution.getExitStatus().isContinuable() ? "Y" : "N", jobExecution.getExitStatus().getExitCode(),
jobExecution.getExitStatus().getExitDescription() };
getJdbcTemplate().update(
getQuery(SAVE_JOB_EXECUTION),
parameters,
new int[] { Types.INTEGER, Types.INTEGER, Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.CHAR,
Types.VARCHAR, Types.VARCHAR });
}
/**
* Validate JobExecution. At a minimum, JobId, StartTime, EndTime, and
* Status cannot be null.
*
* @param jobExecution
* @throws IllegalArgumentException
*/
private void validateJobExecution(JobExecution jobExecution) {
Assert.notNull(jobExecution);
Assert.notNull(jobExecution.getJobId(), "JobExecution Job-Id cannot be null.");
Assert.notNull(jobExecution.getStatus(), "JobExecution status cannot be null.");
}
/**
* Update given JobExecution using a SQL UPDATE statement. The JobExecution
* is first checked to ensure all fields are not null, and that it has an
* ID. The database is then queried to ensure that the ID exists, which
* ensures that it is valid.
*
* @see JobDao#updateJobExecution(JobExecution)
*/
public void updateJobExecution(JobExecution jobExecution) {
validateJobExecution(jobExecution);
String exitDescription = jobExecution.getExitStatus().getExitDescription();
if (exitDescription != null && exitDescription.length() > EXIT_MESSAGE_LENGTH) {
exitDescription = exitDescription.substring(0, EXIT_MESSAGE_LENGTH);
logger.debug("Truncating long message before update of JobExecution: " + jobExecution);
}
Object[] parameters = new Object[] { jobExecution.getStartTime(), jobExecution.getEndTime(),
jobExecution.getStatus().toString(), jobExecution.getExitStatus().isContinuable() ? "Y" : "N",
jobExecution.getExitStatus().getExitCode(), exitDescription, jobExecution.getId() };
if (jobExecution.getId() == null) {
throw new IllegalArgumentException("JobExecution ID cannot be null. JobExecution must be saved "
+ "before it can be updated.");
}
// Check if given JobExecution's Id already exists, if none is found it
// is invalid and
// an exception should be thrown.
if (getJdbcTemplate().queryForInt(getQuery(CHECK_JOB_EXECUTION_EXISTS), new Object[] { jobExecution.getId() }) != 1) {
throw new NoSuchObjectException("Invalid JobExecution, ID " + jobExecution.getId()
+ " not found.");
}
getJdbcTemplate().update(
getQuery(UPDATE_JOB_EXECUTION),
parameters,
new int[] { Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.CHAR, Types.VARCHAR, Types.VARCHAR,
Types.INTEGER });
}
/**
* Setter for {@link DataFieldMaxValueIncrementer} to be used when
* generating primary keys for {@link JobExecution} instances.
*
* @param jobExecutionIncrementer the {@link DataFieldMaxValueIncrementer}
*/
public void setJobExecutionIncrementer(DataFieldMaxValueIncrementer jobExecutionIncrementer) {
this.jobExecutionIncrementer = jobExecutionIncrementer;
}
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
Assert.notNull(jobExecutionIncrementer);
}
/**
* Re-usable mapper for {@link JobExecution} instances.
*
* @author Dave Syer
*
*/
private static class JobExecutionRowMapper implements RowMapper {
private JobInstance job;
public JobExecutionRowMapper(JobInstance job) {
super();
this.job = job;
}
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
JobExecution jobExecution = new JobExecution(job);
jobExecution.setId(new Long(rs.getLong(1)));
jobExecution.setStartTime(rs.getTimestamp(2));
jobExecution.setEndTime(rs.getTimestamp(3));
jobExecution.setStatus(BatchStatus.getStatus(rs.getString(4)));
jobExecution.setExitStatus(new ExitStatus("Y".equals(rs.getString(5)), rs.getString(6), rs.getString(7)));
return jobExecution;
}
}
public JobExecution getLastJobExecution(JobInstance jobInstance) {
Long id = jobInstance.getId();
List executions = getJdbcTemplate().query(getQuery(GET_LAST_EXECUTION), new Object[] { id, id },
new JobExecutionRowMapper(jobInstance));
Assert.state(executions.size() <= 1, "There must be at most one latest job execution");
if (executions.isEmpty()) {
return null;
}
else {
return (JobExecution) executions.get(0);
}
}
}

View File

@@ -1,236 +1,236 @@
package org.springframework.batch.execution.repository.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.util.Assert;
/**
* Jdbc implementation of {@link JobInstanceDao}. Uses sequences (via Spring's
* {@link DataFieldMaxValueIncrementer} abstraction) to create all primary keys
* before inserting a new row. Objects are checked to ensure all mandatory
* fields to be stored are not null. If any are found to be null, an
* IllegalArgumentException will be thrown. This could be left to JdbcTemplate,
* however, the exception will be fairly vague, and fails to highlight which
* field caused the exception.
*
* @author Lucas Ward
* @author Dave Syer
* @author Robert Kasanicky
*/
public class JdbcJobInstanceDao extends AbstractJdbcBatchMetadataDao implements JobInstanceDao, InitializingBean {
private static final String CREATE_JOB_INSTANCE = "INSERT into %PREFIX%JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, JOB_KEY)"
+ " values (?, ?, ?)";
private static final String CREATE_JOB_PARAMETERS = "INSERT into %PREFIX%JOB_PARAMS(JOB_INSTANCE_ID, KEY_NAME, TYPE_CD, "
+ "STRING_VAL, DATE_VAL, LONG_VAL, DOUBLE_VAL) values (?, ?, ?, ?, ?, ?, ?)";
private static final String FIND_JOBS = "SELECT JOB_INSTANCE_ID from %PREFIX%JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?";
private DataFieldMaxValueIncrementer jobIncrementer;
/**
* In this jdbc implementation a job id is obtained by asking the
* jobIncrementer (which is likely a sequence) for the nextLong, and then
* passing the Id and parameter values into an INSERT statement.
*
* @see JobDao#createJob(JobIdentifier)
* @throws IllegalArgumentException if any {@link JobIdentifier} fields are
* null.
*/
public JobInstance createJobInstance(Job job, JobParameters jobParameters) {
Assert.notNull(job, "Job must not be null.");
Assert.hasLength(job.getName(), "Job must have a name");
Assert.notNull(jobParameters, "JobParameters must not be null.");
Assert.state(getJobInstance(job, jobParameters) == null, "JobInstance must not already exist");
Long jobId = new Long(jobIncrementer.nextLongValue());
Object[] parameters = new Object[] { jobId, job.getName(), createJobKey(jobParameters) };
getJdbcTemplate().update(getQuery(CREATE_JOB_INSTANCE), parameters,
new int[] { Types.INTEGER, Types.VARCHAR, Types.VARCHAR });
insertJobParameters(jobId, jobParameters);
JobInstance jobInstance = new JobInstance(jobId, jobParameters, job);
return jobInstance;
}
private String createJobKey(JobParameters jobParameters) {
Map props = jobParameters.getParameters();
StringBuffer stringBuffer = new StringBuffer();
for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
stringBuffer.append(entry.toString() + ";");
}
return stringBuffer.toString();
}
/**
* Convenience method that inserts all parameters from the provided
* JobParameters.
*
*/
private void insertJobParameters(Long jobId, JobParameters jobParameters) {
Map parameters = jobParameters.getStringParameters();
if (!parameters.isEmpty()) {
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
insertParameter(jobId, ParameterType.STRING, entry.getKey().toString(), entry.getValue());
}
}
parameters = jobParameters.getLongParameters();
if (!parameters.isEmpty()) {
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
insertParameter(jobId, ParameterType.LONG, entry.getKey().toString(), entry.getValue());
}
}
parameters = jobParameters.getDoubleParameters();
if (!parameters.isEmpty()) {
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
insertParameter(jobId, ParameterType.DOUBLE, entry.getKey().toString(), entry.getValue());
}
}
parameters = jobParameters.getDateParameters();
if (!parameters.isEmpty()) {
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
insertParameter(jobId, ParameterType.DATE, entry.getKey().toString(), entry.getValue());
}
}
}
/**
* Convenience method that inserts an individual records into the
* JobParameters table.
*/
private void insertParameter(Long jobId, ParameterType type, String key, Object value) {
Object[] args = new Object[0];
int[] argTypes = new int[] { Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP,
Types.INTEGER, Types.DOUBLE };
if (type == ParameterType.STRING) {
args = new Object[] { jobId, key, type, value, new Timestamp(0L), new Long(0), new Double(0) };
}
else if (type == ParameterType.LONG) {
args = new Object[] { jobId, key, type, "", new Timestamp(0L), value, new Double(0) };
}
else if (type == ParameterType.DOUBLE) {
args = new Object[] { jobId, key, type, "", new Timestamp(0L), new Long(0), value };
}
else if (type == ParameterType.DATE) {
args = new Object[] { jobId, key, type, "", value, new Long(0), new Double(0) };
}
getJdbcTemplate().update(getQuery(CREATE_JOB_PARAMETERS), args, argTypes);
}
/**
* The job table is queried for <strong>any</strong> jobs that match the
* given identifier, adding them to a list via the RowMapper callback.
*
* @see JobDao#findJobInstances(JobIdentifier)
* @throws IllegalArgumentException if any {@link JobIdentifier} fields are
* null.
*/
public JobInstance getJobInstance(final Job job, final JobParameters jobParameters) {
Assert.notNull(job, "Job must not be null.");
Assert.hasLength(job.getName(), "Job must have a name");
Assert.notNull(jobParameters, "JobParameters must not be null.");
Object[] parameters = new Object[] { job.getName(), createJobKey(jobParameters) };
RowMapper rowMapper = new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
JobInstance jobInstance = new JobInstance(new Long(rs.getLong(1)), jobParameters, job);
return jobInstance;
}
};
List instances = getJdbcTemplate().query(getQuery(FIND_JOBS), parameters, rowMapper);
if (instances.isEmpty()) {
return null;
} else {
Assert.state(instances.size() == 1);
return (JobInstance) instances.get(0);
}
}
/**
* Setter for {@link DataFieldMaxValueIncrementer} to be used when
* generating primary keys for {@link JobInstance} instances.
*
* @param jobIncrementer the {@link DataFieldMaxValueIncrementer}
*/
public void setJobIncrementer(DataFieldMaxValueIncrementer jobIncrementer) {
this.jobIncrementer = jobIncrementer;
}
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
Assert.notNull(jobIncrementer);
}
private static class ParameterType {
private final String type;
private ParameterType(String type) {
this.type = type;
}
public String toString() {
return type;
}
public static final ParameterType STRING = new ParameterType("STRING");
public static final ParameterType DATE = new ParameterType("DATE");
public static final ParameterType LONG = new ParameterType("LONG");
public static final ParameterType DOUBLE = new ParameterType("DOUBLE");
private static final ParameterType[] VALUES = { STRING, DATE, LONG, DOUBLE };
public static ParameterType getType(String typeAsString) {
for (int i = 0; i < VALUES.length; i++) {
if (VALUES[i].toString().equals(typeAsString)) {
return (ParameterType) VALUES[i];
}
}
return null;
}
}
}
package org.springframework.batch.execution.repository.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.util.Assert;
/**
* Jdbc implementation of {@link JobInstanceDao}. Uses sequences (via Spring's
* {@link DataFieldMaxValueIncrementer} abstraction) to create all primary keys
* before inserting a new row. Objects are checked to ensure all mandatory
* fields to be stored are not null. If any are found to be null, an
* IllegalArgumentException will be thrown. This could be left to JdbcTemplate,
* however, the exception will be fairly vague, and fails to highlight which
* field caused the exception.
*
* @author Lucas Ward
* @author Dave Syer
* @author Robert Kasanicky
*/
public class JdbcJobInstanceDao extends AbstractJdbcBatchMetadataDao implements JobInstanceDao, InitializingBean {
private static final String CREATE_JOB_INSTANCE = "INSERT into %PREFIX%JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, JOB_KEY)"
+ " values (?, ?, ?)";
private static final String CREATE_JOB_PARAMETERS = "INSERT into %PREFIX%JOB_PARAMS(JOB_INSTANCE_ID, KEY_NAME, TYPE_CD, "
+ "STRING_VAL, DATE_VAL, LONG_VAL, DOUBLE_VAL) values (?, ?, ?, ?, ?, ?, ?)";
private static final String FIND_JOBS = "SELECT JOB_INSTANCE_ID from %PREFIX%JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?";
private DataFieldMaxValueIncrementer jobIncrementer;
/**
* In this jdbc implementation a job id is obtained by asking the
* jobIncrementer (which is likely a sequence) for the nextLong, and then
* passing the Id and parameter values into an INSERT statement.
*
* @see JobDao#createJob(JobIdentifier)
* @throws IllegalArgumentException if any {@link JobIdentifier} fields are
* null.
*/
public JobInstance createJobInstance(Job job, JobParameters jobParameters) {
Assert.notNull(job, "Job must not be null.");
Assert.hasLength(job.getName(), "Job must have a name");
Assert.notNull(jobParameters, "JobParameters must not be null.");
Assert.state(getJobInstance(job, jobParameters) == null, "JobInstance must not already exist");
Long jobId = new Long(jobIncrementer.nextLongValue());
Object[] parameters = new Object[] { jobId, job.getName(), createJobKey(jobParameters) };
getJdbcTemplate().update(getQuery(CREATE_JOB_INSTANCE), parameters,
new int[] { Types.INTEGER, Types.VARCHAR, Types.VARCHAR });
insertJobParameters(jobId, jobParameters);
JobInstance jobInstance = new JobInstance(jobId, jobParameters, job);
return jobInstance;
}
private String createJobKey(JobParameters jobParameters) {
Map props = jobParameters.getParameters();
StringBuffer stringBuffer = new StringBuffer();
for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
stringBuffer.append(entry.toString() + ";");
}
return stringBuffer.toString();
}
/**
* Convenience method that inserts all parameters from the provided
* JobParameters.
*
*/
private void insertJobParameters(Long jobId, JobParameters jobParameters) {
Map parameters = jobParameters.getStringParameters();
if (!parameters.isEmpty()) {
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
insertParameter(jobId, ParameterType.STRING, entry.getKey().toString(), entry.getValue());
}
}
parameters = jobParameters.getLongParameters();
if (!parameters.isEmpty()) {
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
insertParameter(jobId, ParameterType.LONG, entry.getKey().toString(), entry.getValue());
}
}
parameters = jobParameters.getDoubleParameters();
if (!parameters.isEmpty()) {
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
insertParameter(jobId, ParameterType.DOUBLE, entry.getKey().toString(), entry.getValue());
}
}
parameters = jobParameters.getDateParameters();
if (!parameters.isEmpty()) {
for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
insertParameter(jobId, ParameterType.DATE, entry.getKey().toString(), entry.getValue());
}
}
}
/**
* Convenience method that inserts an individual records into the
* JobParameters table.
*/
private void insertParameter(Long jobId, ParameterType type, String key, Object value) {
Object[] args = new Object[0];
int[] argTypes = new int[] { Types.INTEGER, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP,
Types.INTEGER, Types.DOUBLE };
if (type == ParameterType.STRING) {
args = new Object[] { jobId, key, type, value, new Timestamp(0L), new Long(0), new Double(0) };
}
else if (type == ParameterType.LONG) {
args = new Object[] { jobId, key, type, "", new Timestamp(0L), value, new Double(0) };
}
else if (type == ParameterType.DOUBLE) {
args = new Object[] { jobId, key, type, "", new Timestamp(0L), new Long(0), value };
}
else if (type == ParameterType.DATE) {
args = new Object[] { jobId, key, type, "", value, new Long(0), new Double(0) };
}
getJdbcTemplate().update(getQuery(CREATE_JOB_PARAMETERS), args, argTypes);
}
/**
* The job table is queried for <strong>any</strong> jobs that match the
* given identifier, adding them to a list via the RowMapper callback.
*
* @see JobDao#findJobInstances(JobIdentifier)
* @throws IllegalArgumentException if any {@link JobIdentifier} fields are
* null.
*/
public JobInstance getJobInstance(final Job job, final JobParameters jobParameters) {
Assert.notNull(job, "Job must not be null.");
Assert.hasLength(job.getName(), "Job must have a name");
Assert.notNull(jobParameters, "JobParameters must not be null.");
Object[] parameters = new Object[] { job.getName(), createJobKey(jobParameters) };
RowMapper rowMapper = new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
JobInstance jobInstance = new JobInstance(new Long(rs.getLong(1)), jobParameters, job);
return jobInstance;
}
};
List instances = getJdbcTemplate().query(getQuery(FIND_JOBS), parameters, rowMapper);
if (instances.isEmpty()) {
return null;
} else {
Assert.state(instances.size() == 1);
return (JobInstance) instances.get(0);
}
}
/**
* Setter for {@link DataFieldMaxValueIncrementer} to be used when
* generating primary keys for {@link JobInstance} instances.
*
* @param jobIncrementer the {@link DataFieldMaxValueIncrementer}
*/
public void setJobIncrementer(DataFieldMaxValueIncrementer jobIncrementer) {
this.jobIncrementer = jobIncrementer;
}
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
Assert.notNull(jobIncrementer);
}
private static class ParameterType {
private final String type;
private ParameterType(String type) {
this.type = type;
}
public String toString() {
return type;
}
public static final ParameterType STRING = new ParameterType("STRING");
public static final ParameterType DATE = new ParameterType("DATE");
public static final ParameterType LONG = new ParameterType("LONG");
public static final ParameterType DOUBLE = new ParameterType("DOUBLE");
private static final ParameterType[] VALUES = { STRING, DATE, LONG, DOUBLE };
public static ParameterType getType(String typeAsString) {
for (int i = 0; i < VALUES.length; i++) {
if (VALUES[i].toString().equals(typeAsString)) {
return (ParameterType) VALUES[i];
}
}
return null;
}
}
}

View File

@@ -1,425 +1,425 @@
package org.springframework.batch.execution.repository.dao;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.domain.BatchStatus;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.io.exception.InfrastructureException;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.util.Assert;
/**
* Jdbc implementation of {@link StepExecutionDao}.<br/>
*
* Allows customization of the tables names used by Spring Batch for step meta
* data via a prefix property.<br/>
*
* Uses sequences or tables (via Spring's {@link DataFieldMaxValueIncrementer}
* abstraction) to create all primary keys before inserting a new row. All
* objects are checked to ensure all fields to be stored are not null. If any
* are found to be null, an IllegalArgumentException will be thrown. This could
* be left to JdbcTemplate, however, the exception will be fairly vague, and
* fails to highlight which field caused the exception.<br/>
*
* @author Lucas Ward
* @author Dave Syer
* @author Robert Kasanicky
*
* @see StepExecutionDao
*/
public class JdbcStepExecutionDao extends AbstractJdbcBatchMetadataDao implements StepExecutionDao, InitializingBean {
private static final Log logger = LogFactory.getLog(JdbcStepExecutionDao.class);
private static final String FIND_STEP_EXECUTION_CONTEXT = "SELECT TYPE_CD, KEY_NAME, STRING_VAL, DOUBLE_VAL, LONG_VAL, OBJECT_VAL "
+ "from %PREFIX%STEP_EXECUTION_CONTEXT where STEP_EXECUTION_ID = ?";
private static final String INSERT_STEP_EXECUTION_CONTEXT = "INSERT into %PREFIX%STEP_EXECUTION_CONTEXT(STEP_EXECUTION_ID, TYPE_CD,"
+ " KEY_NAME, STRING_VAL, DOUBLE_VAL, LONG_VAL, OBJECT_VAL) values(?,?,?,?,?,?,?)";
private static final String SAVE_STEP_EXECUTION = "INSERT into %PREFIX%STEP_EXECUTION(STEP_EXECUTION_ID, VERSION, STEP_NAME, JOB_EXECUTION_ID, START_TIME, "
+ "END_TIME, STATUS, COMMIT_COUNT, TASK_COUNT, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE) "
+ "values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
private static final String UPDATE_STEP_EXECUTION_CONTEXT = "UPDATE %PREFIX%STEP_EXECUTION_CONTEXT set "
+ "TYPE_CD = ?, STRING_VAL = ?, DOUBLE_VAL = ?, LONG_VAL = ?, OBJECT_VAL = ? where STEP_EXECUTION_ID = ? and KEY_NAME = ?";
private static final String UPDATE_STEP_EXECUTION = "UPDATE %PREFIX%STEP_EXECUTION set START_TIME = ?, END_TIME = ?, "
+ "STATUS = ?, COMMIT_COUNT = ?, TASK_COUNT = ?, CONTINUABLE = ? , EXIT_CODE = ?, "
+ "EXIT_MESSAGE = ?, VERSION = ? where STEP_EXECUTION_ID = ? and VERSION = ?";
private static final String GET_STEP_EXECUTION = "SELECT STEP_EXECUTION_ID, STEP_NAME, START_TIME, END_TIME, STATUS, COMMIT_COUNT,"
+ " TASK_COUNT, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE from %PREFIX%STEP_EXECUTION where STEP_NAME = ? and JOB_EXECUTION_ID = ?";
private static final String CURRENT_VERSION_STEP_EXECUTION = "SELECT VERSION FROM %PREFIX%STEP_EXECUTION WHERE STEP_EXECUTION_ID=?";
private static final int EXIT_MESSAGE_LENGTH = 250;
private LobHandler lobHandler = new DefaultLobHandler();
private DataFieldMaxValueIncrementer stepExecutionIncrementer;
public ExecutionContext findExecutionContext(final StepExecution stepExecution) {
final Long executionId = stepExecution.getId();
Assert.notNull(executionId, "ExecutionId must not be null.");
final ExecutionContext executionContext = new ExecutionContext();
RowCallbackHandler callback = new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
String typeCd = rs.getString("TYPE_CD");
AttributeType type = AttributeType.getType(typeCd);
String key = rs.getString("KEY_NAME");
if (type == AttributeType.STRING) {
executionContext.putString(key, rs.getString("STRING_VAL"));
}
else if (type == AttributeType.LONG) {
executionContext.putLong(key, rs.getLong("LONG_VAL"));
}
else if (type == AttributeType.DOUBLE) {
executionContext.putDouble(key, rs.getDouble("DOUBLE_VAL"));
}
else if (type == AttributeType.OBJECT) {
executionContext.put(key, rs.getObject("OBJECT_VAL"));
}
else {
throw new InfrastructureException("Invalid type found: [" + typeCd + "] for execution id: ["
+ executionId + "]");
}
}
};
getJdbcTemplate().query(getQuery(FIND_STEP_EXECUTION_CONTEXT), new Object[] { executionId }, callback);
return executionContext;
}
private void insertExecutionAttribute(final Long executionId, final String key, final Object value,
final AttributeType type) {
PreparedStatementCallback callback = new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException,
DataAccessException {
ps.setLong(1, executionId.longValue());
ps.setString(3, key);
if (type == AttributeType.STRING) {
ps.setString(2, AttributeType.STRING.toString());
ps.setString(4, value.toString());
ps.setDouble(5, 0.0);
ps.setLong(6, 0);
lobCreator.setBlobAsBytes(ps, 7, null);
}
else if (type == AttributeType.DOUBLE) {
ps.setString(2, AttributeType.DOUBLE.toString());
ps.setString(4, null);
ps.setDouble(5, ((Double) value).doubleValue());
ps.setLong(6, 0);
lobCreator.setBlobAsBytes(ps, 7, null);
}
else if (type == AttributeType.LONG) {
ps.setString(2, AttributeType.LONG.toString());
ps.setString(4, null);
ps.setDouble(5, 0.0);
ps.setLong(6, ((Long) value).longValue());
lobCreator.setBlobAsBytes(ps, 7, null);
}
else {
ps.setString(2, AttributeType.OBJECT.toString());
ps.setString(4, null);
ps.setDouble(5, 0.0);
ps.setLong(6, 0);
lobCreator.setBlobAsBytes(ps, 7, SerializationUtils.serialize((Serializable) value));
}
}
};
getJdbcTemplate().execute(getQuery(INSERT_STEP_EXECUTION_CONTEXT), callback);
}
/**
* Save a StepExecution. A unique id will be generated by the
* stepExecutionIncrementor, and then set in the StepExecution. All values
* will then be stored via an INSERT statement.
*
* @see StepDao#saveStepExecution(StepExecution)
*/
public void saveStepExecution(StepExecution stepExecution) {
validateStepExecution(stepExecution);
stepExecution.setId(new Long(stepExecutionIncrementer.nextLongValue()));
stepExecution.incrementVersion(); // should be 0 now
Object[] parameters = new Object[] { stepExecution.getId(), stepExecution.getVersion(),
stepExecution.getStepName(), stepExecution.getJobExecutionId(), stepExecution.getStartTime(),
stepExecution.getEndTime(), stepExecution.getStatus().toString(), stepExecution.getCommitCount(),
stepExecution.getTaskCount(), stepExecution.getExitStatus().isContinuable() ? "Y" : "N",
stepExecution.getExitStatus().getExitCode(), stepExecution.getExitStatus().getExitDescription() };
getJdbcTemplate().update(
getQuery(SAVE_STEP_EXECUTION),
parameters,
new int[] { Types.INTEGER, Types.INTEGER, Types.VARCHAR, Types.INTEGER, Types.TIMESTAMP,
Types.TIMESTAMP, Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.CHAR, Types.VARCHAR,
Types.VARCHAR });
}
/**
* Validate StepExecution. At a minimum, JobId, StartTime, and Status cannot
* be null. EndTime can be null for an unfinished job.
*
* @param jobExecution
* @throws IllegalArgumentException
*/
private void validateStepExecution(StepExecution stepExecution) {
Assert.notNull(stepExecution);
Assert.notNull(stepExecution.getStepName(), "StepExecution step name cannot be null.");
Assert.notNull(stepExecution.getStartTime(), "StepExecution start time cannot be null.");
Assert.notNull(stepExecution.getStatus(), "StepExecution status cannot be null.");
}
/**
* Save or update execution attributes. A lob creator must be used, since
* any attributes that don't match a provided type must be serialized into a
* blob.
*
* @see {@link LobCreator}
*/
public void saveOrUpdateExecutionContext(final StepExecution stepExecution) {
Long executionId = stepExecution.getId();
ExecutionContext executionContext = stepExecution.getExecutionContext();
Assert.notNull(executionId, "ExecutionId must not be null.");
Assert.notNull(executionContext, "The ExecutionContext must not be null.");
for (Iterator it = executionContext.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
final String key = entry.getKey().toString();
final Object value = entry.getValue();
if (value instanceof String) {
updateExecutionAttribute(executionId, key, value, AttributeType.STRING);
}
else if (value instanceof Double) {
updateExecutionAttribute(executionId, key, value, AttributeType.DOUBLE);
}
else if (value instanceof Long) {
updateExecutionAttribute(executionId, key, value, AttributeType.LONG);
}
else {
updateExecutionAttribute(executionId, key, value, AttributeType.OBJECT);
}
}
}
private void updateExecutionAttribute(final Long executionId, final String key, final Object value,
final AttributeType type) {
PreparedStatementCallback callback = new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException,
DataAccessException {
ps.setLong(6, executionId.longValue());
ps.setString(7, key);
if (type == AttributeType.STRING) {
ps.setString(1, AttributeType.STRING.toString());
ps.setString(2, value.toString());
ps.setDouble(3, 0.0);
ps.setLong(4, 0);
lobCreator.setBlobAsBytes(ps, 5, null);
}
else if (type == AttributeType.DOUBLE) {
ps.setString(1, AttributeType.DOUBLE.toString());
ps.setString(2, null);
ps.setDouble(3, ((Double) value).doubleValue());
ps.setLong(4, 0);
lobCreator.setBlobAsBytes(ps, 5, null);
}
else if (type == AttributeType.LONG) {
ps.setString(1, AttributeType.LONG.toString());
ps.setString(2, null);
ps.setDouble(3, 0.0);
ps.setLong(4, ((Long) value).longValue());
lobCreator.setBlobAsBytes(ps, 5, null);
}
else {
ps.setString(1, AttributeType.OBJECT.toString());
ps.setString(2, null);
ps.setDouble(3, 0.0);
ps.setLong(4, 0);
lobCreator.setBlobAsBytes(ps, 5, SerializationUtils.serialize((Serializable) value));
}
}
};
// LobCreating callbacks always return the affect row count for SQL DML
// statements, if less than 1 row
// is affected, then this row is new and should be inserted.
Integer affectedRows = (Integer) getJdbcTemplate().execute(getQuery(UPDATE_STEP_EXECUTION_CONTEXT), callback);
if (affectedRows.intValue() < 1) {
insertExecutionAttribute(executionId, key, value, type);
}
}
/*
* (non-Javadoc)
* @see org.springframework.batch.execution.repository.dao.StepExecutionDao#updateStepExecution(org.springframework.batch.core.domain.StepExecution)
*/
public void updateStepExecution(StepExecution stepExecution) {
validateStepExecution(stepExecution);
Assert.notNull(stepExecution.getId(), "StepExecution Id cannot be null. StepExecution must saved"
+ " before it can be updated.");
// Do not check for existence of step execution considering
// it is saved at every commit point.
String exitDescription = stepExecution.getExitStatus().getExitDescription();
if (exitDescription != null && exitDescription.length() > EXIT_MESSAGE_LENGTH) {
exitDescription = exitDescription.substring(0, EXIT_MESSAGE_LENGTH);
logger.debug("Truncating long message before update of StepExecution: " + stepExecution);
}
// Attempt to prevent concurrent modification errors by blocking here if
// someone is already trying to do it.
synchronized (stepExecution) {
Integer version = new Integer(stepExecution.getVersion().intValue() + 1);
Object[] parameters = new Object[] { stepExecution.getStartTime(), stepExecution.getEndTime(),
stepExecution.getStatus().toString(), stepExecution.getCommitCount(), stepExecution.getTaskCount(),
stepExecution.getExitStatus().isContinuable() ? "Y" : "N",
stepExecution.getExitStatus().getExitCode(), exitDescription, version, stepExecution.getId(),
stepExecution.getVersion() };
int count = getJdbcTemplate().update(
getQuery(UPDATE_STEP_EXECUTION),
parameters,
new int[] { Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.INTEGER, Types.INTEGER,
Types.CHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.INTEGER });
// Avoid concurrent modifications...
if (count == 0) {
int curentVersion = getJdbcTemplate().queryForInt(
getQuery(CURRENT_VERSION_STEP_EXECUTION),
new Object[] { stepExecution.getId() });
throw new OptimisticLockingFailureException("Attempt to update step execution id="
+ stepExecution.getId() + " with wrong version (" + stepExecution.getVersion() + "), where current version is "+curentVersion);
}
stepExecution.incrementVersion();
}
}
private class StepExecutionRowMapper implements RowMapper {
private final JobExecution jobExecution;
private final Step step;
public StepExecutionRowMapper(JobExecution jobExecution, Step step) {
this.jobExecution = jobExecution;
this.step = step;
}
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
StepExecution stepExecution = new StepExecution(step, jobExecution, new Long(rs.getLong(1)));
stepExecution.setStartTime(rs.getTimestamp(3));
stepExecution.setEndTime(rs.getTimestamp(4));
stepExecution.setStatus(BatchStatus.getStatus(rs.getString(5)));
stepExecution.setCommitCount(rs.getInt(6));
stepExecution.setTaskCount(rs.getInt(7));
stepExecution.setExitStatus(new ExitStatus("Y".equals(rs.getString(8)), rs.getString(9), rs.getString(10)));
stepExecution.setExecutionContext(findExecutionContext(stepExecution));
return stepExecution;
}
}
public void setLobHandler(LobHandler lobHandler) {
this.lobHandler = lobHandler;
}
public void setStepExecutionIncrementer(DataFieldMaxValueIncrementer stepExecutionIncrementer) {
this.stepExecutionIncrementer = stepExecutionIncrementer;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(stepExecutionIncrementer, "StepExecutionIncrementer cannot be null.");
}
public static class AttributeType {
private final String type;
private AttributeType(String type) {
this.type = type;
}
public String toString() {
return type;
}
public static final AttributeType STRING = new AttributeType("STRING");
public static final AttributeType LONG = new AttributeType("LONG");
public static final AttributeType OBJECT = new AttributeType("OBJECT");
public static final AttributeType DOUBLE = new AttributeType("DOUBLE");
private static final AttributeType[] VALUES = { STRING, OBJECT, LONG, DOUBLE };
public static AttributeType getType(String typeAsString) {
for (int i = 0; i < VALUES.length; i++) {
if (VALUES[i].toString().equals(typeAsString)) {
return (AttributeType) VALUES[i];
}
}
return null;
}
}
public StepExecution getStepExecution(JobExecution jobExecution, Step step) {
List executions = getJdbcTemplate().query(getQuery(GET_STEP_EXECUTION),
new Object[] { step.getName(), jobExecution.getId() }, new StepExecutionRowMapper(jobExecution, step));
Assert.state(executions.size() <= 1,
"There can be at most one step execution with given name for single job execution");
if (executions.isEmpty()) {
return null;
}
else {
return (StepExecution) executions.get(0);
}
}
}
package org.springframework.batch.execution.repository.dao;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.domain.BatchStatus;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.io.exception.InfrastructureException;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.jdbc.core.PreparedStatementCallback;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.util.Assert;
/**
* Jdbc implementation of {@link StepExecutionDao}.<br/>
*
* Allows customization of the tables names used by Spring Batch for step meta
* data via a prefix property.<br/>
*
* Uses sequences or tables (via Spring's {@link DataFieldMaxValueIncrementer}
* abstraction) to create all primary keys before inserting a new row. All
* objects are checked to ensure all fields to be stored are not null. If any
* are found to be null, an IllegalArgumentException will be thrown. This could
* be left to JdbcTemplate, however, the exception will be fairly vague, and
* fails to highlight which field caused the exception.<br/>
*
* @author Lucas Ward
* @author Dave Syer
* @author Robert Kasanicky
*
* @see StepExecutionDao
*/
public class JdbcStepExecutionDao extends AbstractJdbcBatchMetadataDao implements StepExecutionDao, InitializingBean {
private static final Log logger = LogFactory.getLog(JdbcStepExecutionDao.class);
private static final String FIND_STEP_EXECUTION_CONTEXT = "SELECT TYPE_CD, KEY_NAME, STRING_VAL, DOUBLE_VAL, LONG_VAL, OBJECT_VAL "
+ "from %PREFIX%STEP_EXECUTION_CONTEXT where STEP_EXECUTION_ID = ?";
private static final String INSERT_STEP_EXECUTION_CONTEXT = "INSERT into %PREFIX%STEP_EXECUTION_CONTEXT(STEP_EXECUTION_ID, TYPE_CD,"
+ " KEY_NAME, STRING_VAL, DOUBLE_VAL, LONG_VAL, OBJECT_VAL) values(?,?,?,?,?,?,?)";
private static final String SAVE_STEP_EXECUTION = "INSERT into %PREFIX%STEP_EXECUTION(STEP_EXECUTION_ID, VERSION, STEP_NAME, JOB_EXECUTION_ID, START_TIME, "
+ "END_TIME, STATUS, COMMIT_COUNT, TASK_COUNT, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE) "
+ "values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
private static final String UPDATE_STEP_EXECUTION_CONTEXT = "UPDATE %PREFIX%STEP_EXECUTION_CONTEXT set "
+ "TYPE_CD = ?, STRING_VAL = ?, DOUBLE_VAL = ?, LONG_VAL = ?, OBJECT_VAL = ? where STEP_EXECUTION_ID = ? and KEY_NAME = ?";
private static final String UPDATE_STEP_EXECUTION = "UPDATE %PREFIX%STEP_EXECUTION set START_TIME = ?, END_TIME = ?, "
+ "STATUS = ?, COMMIT_COUNT = ?, TASK_COUNT = ?, CONTINUABLE = ? , EXIT_CODE = ?, "
+ "EXIT_MESSAGE = ?, VERSION = ? where STEP_EXECUTION_ID = ? and VERSION = ?";
private static final String GET_STEP_EXECUTION = "SELECT STEP_EXECUTION_ID, STEP_NAME, START_TIME, END_TIME, STATUS, COMMIT_COUNT,"
+ " TASK_COUNT, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE from %PREFIX%STEP_EXECUTION where STEP_NAME = ? and JOB_EXECUTION_ID = ?";
private static final String CURRENT_VERSION_STEP_EXECUTION = "SELECT VERSION FROM %PREFIX%STEP_EXECUTION WHERE STEP_EXECUTION_ID=?";
private static final int EXIT_MESSAGE_LENGTH = 250;
private LobHandler lobHandler = new DefaultLobHandler();
private DataFieldMaxValueIncrementer stepExecutionIncrementer;
public ExecutionContext findExecutionContext(final StepExecution stepExecution) {
final Long executionId = stepExecution.getId();
Assert.notNull(executionId, "ExecutionId must not be null.");
final ExecutionContext executionContext = new ExecutionContext();
RowCallbackHandler callback = new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
String typeCd = rs.getString("TYPE_CD");
AttributeType type = AttributeType.getType(typeCd);
String key = rs.getString("KEY_NAME");
if (type == AttributeType.STRING) {
executionContext.putString(key, rs.getString("STRING_VAL"));
}
else if (type == AttributeType.LONG) {
executionContext.putLong(key, rs.getLong("LONG_VAL"));
}
else if (type == AttributeType.DOUBLE) {
executionContext.putDouble(key, rs.getDouble("DOUBLE_VAL"));
}
else if (type == AttributeType.OBJECT) {
executionContext.put(key, rs.getObject("OBJECT_VAL"));
}
else {
throw new InfrastructureException("Invalid type found: [" + typeCd + "] for execution id: ["
+ executionId + "]");
}
}
};
getJdbcTemplate().query(getQuery(FIND_STEP_EXECUTION_CONTEXT), new Object[] { executionId }, callback);
return executionContext;
}
private void insertExecutionAttribute(final Long executionId, final String key, final Object value,
final AttributeType type) {
PreparedStatementCallback callback = new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException,
DataAccessException {
ps.setLong(1, executionId.longValue());
ps.setString(3, key);
if (type == AttributeType.STRING) {
ps.setString(2, AttributeType.STRING.toString());
ps.setString(4, value.toString());
ps.setDouble(5, 0.0);
ps.setLong(6, 0);
lobCreator.setBlobAsBytes(ps, 7, null);
}
else if (type == AttributeType.DOUBLE) {
ps.setString(2, AttributeType.DOUBLE.toString());
ps.setString(4, null);
ps.setDouble(5, ((Double) value).doubleValue());
ps.setLong(6, 0);
lobCreator.setBlobAsBytes(ps, 7, null);
}
else if (type == AttributeType.LONG) {
ps.setString(2, AttributeType.LONG.toString());
ps.setString(4, null);
ps.setDouble(5, 0.0);
ps.setLong(6, ((Long) value).longValue());
lobCreator.setBlobAsBytes(ps, 7, null);
}
else {
ps.setString(2, AttributeType.OBJECT.toString());
ps.setString(4, null);
ps.setDouble(5, 0.0);
ps.setLong(6, 0);
lobCreator.setBlobAsBytes(ps, 7, SerializationUtils.serialize((Serializable) value));
}
}
};
getJdbcTemplate().execute(getQuery(INSERT_STEP_EXECUTION_CONTEXT), callback);
}
/**
* Save a StepExecution. A unique id will be generated by the
* stepExecutionIncrementor, and then set in the StepExecution. All values
* will then be stored via an INSERT statement.
*
* @see StepDao#saveStepExecution(StepExecution)
*/
public void saveStepExecution(StepExecution stepExecution) {
validateStepExecution(stepExecution);
stepExecution.setId(new Long(stepExecutionIncrementer.nextLongValue()));
stepExecution.incrementVersion(); // should be 0 now
Object[] parameters = new Object[] { stepExecution.getId(), stepExecution.getVersion(),
stepExecution.getStepName(), stepExecution.getJobExecutionId(), stepExecution.getStartTime(),
stepExecution.getEndTime(), stepExecution.getStatus().toString(), stepExecution.getCommitCount(),
stepExecution.getTaskCount(), stepExecution.getExitStatus().isContinuable() ? "Y" : "N",
stepExecution.getExitStatus().getExitCode(), stepExecution.getExitStatus().getExitDescription() };
getJdbcTemplate().update(
getQuery(SAVE_STEP_EXECUTION),
parameters,
new int[] { Types.INTEGER, Types.INTEGER, Types.VARCHAR, Types.INTEGER, Types.TIMESTAMP,
Types.TIMESTAMP, Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.CHAR, Types.VARCHAR,
Types.VARCHAR });
}
/**
* Validate StepExecution. At a minimum, JobId, StartTime, and Status cannot
* be null. EndTime can be null for an unfinished job.
*
* @param jobExecution
* @throws IllegalArgumentException
*/
private void validateStepExecution(StepExecution stepExecution) {
Assert.notNull(stepExecution);
Assert.notNull(stepExecution.getStepName(), "StepExecution step name cannot be null.");
Assert.notNull(stepExecution.getStartTime(), "StepExecution start time cannot be null.");
Assert.notNull(stepExecution.getStatus(), "StepExecution status cannot be null.");
}
/**
* Save or update execution attributes. A lob creator must be used, since
* any attributes that don't match a provided type must be serialized into a
* blob.
*
* @see {@link LobCreator}
*/
public void saveOrUpdateExecutionContext(final StepExecution stepExecution) {
Long executionId = stepExecution.getId();
ExecutionContext executionContext = stepExecution.getExecutionContext();
Assert.notNull(executionId, "ExecutionId must not be null.");
Assert.notNull(executionContext, "The ExecutionContext must not be null.");
for (Iterator it = executionContext.entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
final String key = entry.getKey().toString();
final Object value = entry.getValue();
if (value instanceof String) {
updateExecutionAttribute(executionId, key, value, AttributeType.STRING);
}
else if (value instanceof Double) {
updateExecutionAttribute(executionId, key, value, AttributeType.DOUBLE);
}
else if (value instanceof Long) {
updateExecutionAttribute(executionId, key, value, AttributeType.LONG);
}
else {
updateExecutionAttribute(executionId, key, value, AttributeType.OBJECT);
}
}
}
private void updateExecutionAttribute(final Long executionId, final String key, final Object value,
final AttributeType type) {
PreparedStatementCallback callback = new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException,
DataAccessException {
ps.setLong(6, executionId.longValue());
ps.setString(7, key);
if (type == AttributeType.STRING) {
ps.setString(1, AttributeType.STRING.toString());
ps.setString(2, value.toString());
ps.setDouble(3, 0.0);
ps.setLong(4, 0);
lobCreator.setBlobAsBytes(ps, 5, null);
}
else if (type == AttributeType.DOUBLE) {
ps.setString(1, AttributeType.DOUBLE.toString());
ps.setString(2, null);
ps.setDouble(3, ((Double) value).doubleValue());
ps.setLong(4, 0);
lobCreator.setBlobAsBytes(ps, 5, null);
}
else if (type == AttributeType.LONG) {
ps.setString(1, AttributeType.LONG.toString());
ps.setString(2, null);
ps.setDouble(3, 0.0);
ps.setLong(4, ((Long) value).longValue());
lobCreator.setBlobAsBytes(ps, 5, null);
}
else {
ps.setString(1, AttributeType.OBJECT.toString());
ps.setString(2, null);
ps.setDouble(3, 0.0);
ps.setLong(4, 0);
lobCreator.setBlobAsBytes(ps, 5, SerializationUtils.serialize((Serializable) value));
}
}
};
// LobCreating callbacks always return the affect row count for SQL DML
// statements, if less than 1 row
// is affected, then this row is new and should be inserted.
Integer affectedRows = (Integer) getJdbcTemplate().execute(getQuery(UPDATE_STEP_EXECUTION_CONTEXT), callback);
if (affectedRows.intValue() < 1) {
insertExecutionAttribute(executionId, key, value, type);
}
}
/*
* (non-Javadoc)
* @see org.springframework.batch.execution.repository.dao.StepExecutionDao#updateStepExecution(org.springframework.batch.core.domain.StepExecution)
*/
public void updateStepExecution(StepExecution stepExecution) {
validateStepExecution(stepExecution);
Assert.notNull(stepExecution.getId(), "StepExecution Id cannot be null. StepExecution must saved"
+ " before it can be updated.");
// Do not check for existence of step execution considering
// it is saved at every commit point.
String exitDescription = stepExecution.getExitStatus().getExitDescription();
if (exitDescription != null && exitDescription.length() > EXIT_MESSAGE_LENGTH) {
exitDescription = exitDescription.substring(0, EXIT_MESSAGE_LENGTH);
logger.debug("Truncating long message before update of StepExecution: " + stepExecution);
}
// Attempt to prevent concurrent modification errors by blocking here if
// someone is already trying to do it.
synchronized (stepExecution) {
Integer version = new Integer(stepExecution.getVersion().intValue() + 1);
Object[] parameters = new Object[] { stepExecution.getStartTime(), stepExecution.getEndTime(),
stepExecution.getStatus().toString(), stepExecution.getCommitCount(), stepExecution.getTaskCount(),
stepExecution.getExitStatus().isContinuable() ? "Y" : "N",
stepExecution.getExitStatus().getExitCode(), exitDescription, version, stepExecution.getId(),
stepExecution.getVersion() };
int count = getJdbcTemplate().update(
getQuery(UPDATE_STEP_EXECUTION),
parameters,
new int[] { Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.INTEGER, Types.INTEGER,
Types.CHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.INTEGER });
// Avoid concurrent modifications...
if (count == 0) {
int curentVersion = getJdbcTemplate().queryForInt(
getQuery(CURRENT_VERSION_STEP_EXECUTION),
new Object[] { stepExecution.getId() });
throw new OptimisticLockingFailureException("Attempt to update step execution id="
+ stepExecution.getId() + " with wrong version (" + stepExecution.getVersion() + "), where current version is "+curentVersion);
}
stepExecution.incrementVersion();
}
}
private class StepExecutionRowMapper implements RowMapper {
private final JobExecution jobExecution;
private final Step step;
public StepExecutionRowMapper(JobExecution jobExecution, Step step) {
this.jobExecution = jobExecution;
this.step = step;
}
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
StepExecution stepExecution = new StepExecution(step, jobExecution, new Long(rs.getLong(1)));
stepExecution.setStartTime(rs.getTimestamp(3));
stepExecution.setEndTime(rs.getTimestamp(4));
stepExecution.setStatus(BatchStatus.getStatus(rs.getString(5)));
stepExecution.setCommitCount(rs.getInt(6));
stepExecution.setTaskCount(rs.getInt(7));
stepExecution.setExitStatus(new ExitStatus("Y".equals(rs.getString(8)), rs.getString(9), rs.getString(10)));
stepExecution.setExecutionContext(findExecutionContext(stepExecution));
return stepExecution;
}
}
public void setLobHandler(LobHandler lobHandler) {
this.lobHandler = lobHandler;
}
public void setStepExecutionIncrementer(DataFieldMaxValueIncrementer stepExecutionIncrementer) {
this.stepExecutionIncrementer = stepExecutionIncrementer;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(stepExecutionIncrementer, "StepExecutionIncrementer cannot be null.");
}
public static class AttributeType {
private final String type;
private AttributeType(String type) {
this.type = type;
}
public String toString() {
return type;
}
public static final AttributeType STRING = new AttributeType("STRING");
public static final AttributeType LONG = new AttributeType("LONG");
public static final AttributeType OBJECT = new AttributeType("OBJECT");
public static final AttributeType DOUBLE = new AttributeType("DOUBLE");
private static final AttributeType[] VALUES = { STRING, OBJECT, LONG, DOUBLE };
public static AttributeType getType(String typeAsString) {
for (int i = 0; i < VALUES.length; i++) {
if (VALUES[i].toString().equals(typeAsString)) {
return (AttributeType) VALUES[i];
}
}
return null;
}
}
public StepExecution getStepExecution(JobExecution jobExecution, Step step) {
List executions = getJdbcTemplate().query(getQuery(GET_STEP_EXECUTION),
new Object[] { step.getName(), jobExecution.getId() }, new StepExecutionRowMapper(jobExecution, step));
Assert.state(executions.size() <= 1,
"There can be at most one step execution with given name for single job execution");
if (executions.isEmpty()) {
return null;
}
else {
return (StepExecution) executions.get(0);
}
}
}

View File

@@ -1,55 +1,55 @@
package org.springframework.batch.execution.repository.dao;
import java.util.List;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
/**
* Data Access Object for job executions.
*
* @author Lucas Ward
* @author Robert Kasanicky
*/
public interface JobExecutionDao {
/**
* Save a new JobExecution.
*
* Preconditions: jobInstance the jobExecution belongs to must have a jobInstanceId.
*
* @param jobExecution
*/
void saveJobExecution(JobExecution jobExecution);
/**
* Update and existing JobExecution.
*
* Preconditions: jobExecution must have an Id (which can be obtained by the
* save method) and a jobInstanceId.
*
* @param jobExecution
*/
void updateJobExecution(JobExecution jobExecution);
/**
* Return the number of JobExecutions for the given JobInstance
*
* Preconditions: jobInstance must have an id.
*/
int getJobExecutionCount(JobInstance jobInstance);
/**
* Return list of JobExecutions for given JobInstance.
*
* @param jobInstance
* @return list of jobExecutions.
*/
List findJobExecutions(JobInstance jobInstance);
/**
* @return last JobExecution for given JobInstance.
*/
JobExecution getLastJobExecution(JobInstance jobInstance);
}
package org.springframework.batch.execution.repository.dao;
import java.util.List;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
/**
* Data Access Object for job executions.
*
* @author Lucas Ward
* @author Robert Kasanicky
*/
public interface JobExecutionDao {
/**
* Save a new JobExecution.
*
* Preconditions: jobInstance the jobExecution belongs to must have a jobInstanceId.
*
* @param jobExecution
*/
void saveJobExecution(JobExecution jobExecution);
/**
* Update and existing JobExecution.
*
* Preconditions: jobExecution must have an Id (which can be obtained by the
* save method) and a jobInstanceId.
*
* @param jobExecution
*/
void updateJobExecution(JobExecution jobExecution);
/**
* Return the number of JobExecutions for the given JobInstance
*
* Preconditions: jobInstance must have an id.
*/
int getJobExecutionCount(JobInstance jobInstance);
/**
* Return list of JobExecutions for given JobInstance.
*
* @param jobInstance
* @return list of jobExecutions.
*/
List findJobExecutions(JobInstance jobInstance);
/**
* @return last JobExecution for given JobInstance.
*/
JobExecution getLastJobExecution(JobInstance jobInstance);
}

View File

@@ -1,42 +1,42 @@
package org.springframework.batch.execution.repository.dao;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
/**
* Data Access Object for job instances.
*
* @author Lucas Ward
* @author Robert Kasanicky
*
*/
public interface JobInstanceDao {
/**
* Create a JobInstance with given name and parameters.
*
* PreConditions: JobInstance for given name and parameters must not already exist
*
* PostConditions: A valid job instancewill be returned which has been persisted and
* contains an unique Id.
*
* @param jobName
* @param jobParameters
* @return JobInstance
*/
JobInstance createJobInstance(Job job, JobParameters jobParameters);
/**
* Find all job instances that match the given name and parameters. If no
* matching job instances are found, then a list of size 0 will be
* returned.
*
* @param jobName
* @param jobParameters
* @return List of {@link JobInstance} objects matching
* {@link JobIdentifier}
*/
JobInstance getJobInstance(Job job, JobParameters jobParameters);
}
package org.springframework.batch.execution.repository.dao;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
/**
* Data Access Object for job instances.
*
* @author Lucas Ward
* @author Robert Kasanicky
*
*/
public interface JobInstanceDao {
/**
* Create a JobInstance with given name and parameters.
*
* PreConditions: JobInstance for given name and parameters must not already exist
*
* PostConditions: A valid job instancewill be returned which has been persisted and
* contains an unique Id.
*
* @param jobName
* @param jobParameters
* @return JobInstance
*/
JobInstance createJobInstance(Job job, JobParameters jobParameters);
/**
* Find all job instances that match the given name and parameters. If no
* matching job instances are found, then a list of size 0 will be
* returned.
*
* @param jobName
* @param jobParameters
* @return List of {@link JobInstance} objects matching
* {@link JobIdentifier}
*/
JobInstance getJobInstance(Job job, JobParameters jobParameters);
}

View File

@@ -1,76 +1,76 @@
package org.springframework.batch.execution.repository.dao;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
/**
* In-memory implementation of {@link JobExecutionDao}.
*
*/
public class MapJobExecutionDao implements JobExecutionDao {
private static Map executionsByJobInstanceId = TransactionAwareProxyFactory.createTransactionalMap();
private static long currentId;
public static void clear() {
executionsByJobInstanceId.clear();
}
public int getJobExecutionCount(JobInstance jobInstance) {
Set executions = (Set) executionsByJobInstanceId.get(jobInstance.getId());
if (executions == null) {
return 0;
}
return executions.size();
}
public void saveJobExecution(JobExecution jobExecution) {
Set executions = (Set) executionsByJobInstanceId.get(jobExecution.getJobId());
if (executions == null) {
executions = TransactionAwareProxyFactory.createTransactionalSet();
executionsByJobInstanceId.put(jobExecution.getJobId(), executions);
}
executions.add(jobExecution);
jobExecution.setId(new Long(currentId++));
}
public List findJobExecutions(JobInstance jobInstance) {
Set executions = (Set) executionsByJobInstanceId.get(jobInstance.getId());
if (executions == null) {
return new ArrayList();
}
else {
return new ArrayList(executions);
}
}
public void updateJobExecution(JobExecution jobExecution) {
// no-op
}
public JobExecution getLastJobExecution(JobInstance jobInstance) {
Set executions = (Set) executionsByJobInstanceId.get(jobInstance.getId());
if (executions == null) {
return null;
}
JobExecution lastExec = null;
for (Iterator iterator = executions.iterator(); iterator.hasNext();) {
JobExecution exec = (JobExecution) iterator.next();
if (lastExec == null) {
lastExec = exec;
}
if (lastExec.getStartTime().getTime() < exec.getStartTime().getTime()) {
lastExec = exec;
}
}
return lastExec;
}
}
package org.springframework.batch.execution.repository.dao;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
/**
* In-memory implementation of {@link JobExecutionDao}.
*
*/
public class MapJobExecutionDao implements JobExecutionDao {
private static Map executionsByJobInstanceId = TransactionAwareProxyFactory.createTransactionalMap();
private static long currentId;
public static void clear() {
executionsByJobInstanceId.clear();
}
public int getJobExecutionCount(JobInstance jobInstance) {
Set executions = (Set) executionsByJobInstanceId.get(jobInstance.getId());
if (executions == null) {
return 0;
}
return executions.size();
}
public void saveJobExecution(JobExecution jobExecution) {
Set executions = (Set) executionsByJobInstanceId.get(jobExecution.getJobId());
if (executions == null) {
executions = TransactionAwareProxyFactory.createTransactionalSet();
executionsByJobInstanceId.put(jobExecution.getJobId(), executions);
}
executions.add(jobExecution);
jobExecution.setId(new Long(currentId++));
}
public List findJobExecutions(JobInstance jobInstance) {
Set executions = (Set) executionsByJobInstanceId.get(jobInstance.getId());
if (executions == null) {
return new ArrayList();
}
else {
return new ArrayList(executions);
}
}
public void updateJobExecution(JobExecution jobExecution) {
// no-op
}
public JobExecution getLastJobExecution(JobInstance jobInstance) {
Set executions = (Set) executionsByJobInstanceId.get(jobInstance.getId());
if (executions == null) {
return null;
}
JobExecution lastExec = null;
for (Iterator iterator = executions.iterator(); iterator.hasNext();) {
JobExecution exec = (JobExecution) iterator.next();
if (lastExec == null) {
lastExec = exec;
}
if (lastExec.getStartTime().getTime() < exec.getStartTime().getTime()) {
lastExec = exec;
}
}
return lastExec;
}
}

View File

@@ -1,44 +1,44 @@
package org.springframework.batch.execution.repository.dao;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
import org.springframework.util.Assert;
public class MapJobInstanceDao implements JobInstanceDao {
private static Collection jobInstances = TransactionAwareProxyFactory.createTransactionalList();
private long currentId = 0;
public static void clear() {
jobInstances.clear();
}
public JobInstance createJobInstance(Job job, JobParameters jobParameters) {
Assert.state(getJobInstance(job, jobParameters) == null, "JobInstance must not already exist");
JobInstance jobInstance = new JobInstance(new Long(currentId++), jobParameters, job);
jobInstances.add(jobInstance);
return jobInstance;
}
public JobInstance getJobInstance(Job job, JobParameters jobParameters) {
for (Iterator iterator = jobInstances.iterator(); iterator.hasNext();) {
JobInstance instance = (JobInstance) iterator.next();
if (instance.getJobName().equals(job.getName()) && instance.getJobParameters().equals(jobParameters)) {
return instance;
}
}
return null;
}
}
package org.springframework.batch.execution.repository.dao;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
import org.springframework.util.Assert;
public class MapJobInstanceDao implements JobInstanceDao {
private static Collection jobInstances = TransactionAwareProxyFactory.createTransactionalList();
private long currentId = 0;
public static void clear() {
jobInstances.clear();
}
public JobInstance createJobInstance(Job job, JobParameters jobParameters) {
Assert.state(getJobInstance(job, jobParameters) == null, "JobInstance must not already exist");
JobInstance jobInstance = new JobInstance(new Long(currentId++), jobParameters, job);
jobInstances.add(jobInstance);
return jobInstance;
}
public JobInstance getJobInstance(Job job, JobParameters jobParameters) {
for (Iterator iterator = jobInstances.iterator(); iterator.hasNext();) {
JobInstance instance = (JobInstance) iterator.next();
if (instance.getJobName().equals(job.getName()) && instance.getJobParameters().equals(jobParameters)) {
return instance;
}
}
return null;
}
}

View File

@@ -1,48 +1,48 @@
package org.springframework.batch.execution.repository.dao;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.item.ExecutionContext;
public interface StepExecutionDao {
/**
* Save the given StepExecution.
*
* Preconditions: Id must be null.
*
* Postconditions: Id will be set to a unique Long.
*
* @param stepExecution
*/
void saveStepExecution(StepExecution stepExecution);
/**
* Update the given StepExecution
*
* Preconditions: Id must not be null.
*
* @param stepExecution
*/
void updateStepExecution(StepExecution stepExecution);
/**
* Find all {@link ExecutionContext} for the given {@link StepExecution}.
*
* @throws IllegalArgumentException if the id is null.
*/
ExecutionContext findExecutionContext(StepExecution stepExecution);
/**
* Save the {@link ExecutionContext} of the given {@link StepExecution}.
*
* @param stepExecution the {@link StepExecution} containing the
* {@link ExecutionContext} to be saved.
* @throws IllegalArgumentException if the attributes are null.
*/
void saveOrUpdateExecutionContext(StepExecution stepExecution);
StepExecution getStepExecution(JobExecution jobExecution, Step step);
}
package org.springframework.batch.execution.repository.dao;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.item.ExecutionContext;
public interface StepExecutionDao {
/**
* Save the given StepExecution.
*
* Preconditions: Id must be null.
*
* Postconditions: Id will be set to a unique Long.
*
* @param stepExecution
*/
void saveStepExecution(StepExecution stepExecution);
/**
* Update the given StepExecution
*
* Preconditions: Id must not be null.
*
* @param stepExecution
*/
void updateStepExecution(StepExecution stepExecution);
/**
* Find all {@link ExecutionContext} for the given {@link StepExecution}.
*
* @throws IllegalArgumentException if the id is null.
*/
ExecutionContext findExecutionContext(StepExecution stepExecution);
/**
* Save the {@link ExecutionContext} of the given {@link StepExecution}.
*
* @param stepExecution the {@link StepExecution} containing the
* {@link ExecutionContext} to be saved.
* @throws IllegalArgumentException if the attributes are null.
*/
void saveOrUpdateExecutionContext(StepExecution stepExecution);
StepExecution getStepExecution(JobExecution jobExecution, Step step);
}

View File

@@ -1,94 +1,94 @@
/*
* 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.step;
import org.springframework.batch.core.domain.JobInterruptedException;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.io.exception.InfrastructureException;
/**
* A {@link Step} implementation that provides common behaviour to subclasses.
*
* @author Dave Syer
* @author Ben Hale
*/
public abstract class AbstractStep implements Step {
protected String name;
protected int startLimit = Integer.MAX_VALUE;
protected boolean allowStartIfComplete;
/**
* Default constructor.
*/
public AbstractStep() {
super();
}
public String getName() {
return this.name;
}
/**
* Set the name property. Always overrides the default value if this object
* is a Spring bean.
*
* @see #setBeanName(java.lang.String)
*/
public void setName(String name) {
this.name = name;
}
public int getStartLimit() {
return this.startLimit;
}
/**
* Public setter for the startLimit.
*
* @param startLimit the startLimit to set
*/
public void setStartLimit(int startLimit) {
this.startLimit = startLimit;
}
public boolean isAllowStartIfComplete() {
return this.allowStartIfComplete;
}
/**
* Public setter for the shouldAllowStartIfComplete.
*
* @param allowStartIfComplete the shouldAllowStartIfComplete to set
*/
public void setAllowStartIfComplete(boolean allowStartIfComplete) {
this.allowStartIfComplete = allowStartIfComplete;
}
/**
* Convenient constructor for setting only the name property.
*
* @param name
*/
public AbstractStep(String name) {
this.name = name;
}
public abstract void execute(StepExecution stepExecution) throws JobInterruptedException, InfrastructureException;
/*
* 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.step;
import org.springframework.batch.core.domain.JobInterruptedException;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.io.exception.InfrastructureException;
/**
* A {@link Step} implementation that provides common behaviour to subclasses.
*
* @author Dave Syer
* @author Ben Hale
*/
public abstract class AbstractStep implements Step {
protected String name;
protected int startLimit = Integer.MAX_VALUE;
protected boolean allowStartIfComplete;
/**
* Default constructor.
*/
public AbstractStep() {
super();
}
public String getName() {
return this.name;
}
/**
* Set the name property. Always overrides the default value if this object
* is a Spring bean.
*
* @see #setBeanName(java.lang.String)
*/
public void setName(String name) {
this.name = name;
}
public int getStartLimit() {
return this.startLimit;
}
/**
* Public setter for the startLimit.
*
* @param startLimit the startLimit to set
*/
public void setStartLimit(int startLimit) {
this.startLimit = startLimit;
}
public boolean isAllowStartIfComplete() {
return this.allowStartIfComplete;
}
/**
* Public setter for the shouldAllowStartIfComplete.
*
* @param allowStartIfComplete the shouldAllowStartIfComplete to set
*/
public void setAllowStartIfComplete(boolean allowStartIfComplete) {
this.allowStartIfComplete = allowStartIfComplete;
}
/**
* Convenient constructor for setting only the name property.
*
* @param name
*/
public AbstractStep(String name) {
this.name = name;
}
public abstract void execute(StepExecution stepExecution) throws JobInterruptedException, InfrastructureException;
}

View File

@@ -1,78 +1,78 @@
/*
* 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.step;
import org.springframework.batch.core.domain.StepContribution;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.exception.ClearFailedException;
import org.springframework.batch.item.exception.FlushFailedException;
import org.springframework.batch.item.exception.MarkFailedException;
import org.springframework.batch.item.exception.ResetFailedException;
import org.springframework.batch.repeat.ExitStatus;
/**
* Strategy for processing a single item in an item-oriented step. Extends
* {@link ItemReader} and {@link ItemWriter} because part of the contract of the
* processor is that it should delegate calls to those interfaces.
*
* @author Dave Syer
*
*/
public interface ItemHandler {
/**
* Given the current context in the form of a step contribution, do whatever
* is necessary to process this unit inside a chunk. Implementations obtain
* the item and return {@link ExitStatus#FINISHED} if it is null. If it is
* not null process the item and return {@link ExitStatus#CONTINUABLE}. On
* failure throws an exception.
*
* @param contribution the current step context
* @return an {@link ExitStatus} indicating whether processing is
* continuable.
*/
ExitStatus handle(StepContribution contribution) throws Exception;
/**
* Implementations should delegate to an {@link ItemReader}.
*
* @see org.springframework.batch.item.ItemReader#mark()
*/
void mark() throws MarkFailedException;
/**
* Implementations should delegate to an {@link ItemReader}.
*
* @see org.springframework.batch.item.ItemReader#reset()
*/
void reset() throws ResetFailedException;
/**
* Implementations should delegate to an {@link ItemWriter}.
*
* @see org.springframework.batch.item.ItemWriter#flush()
*/
public void flush() throws FlushFailedException;
/**
* Implementations should delegate to an {@link ItemWriter}.
*
* @see org.springframework.batch.item.ItemWriter#clear()
*/
public void clear() throws ClearFailedException;
}
/*
* 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.step;
import org.springframework.batch.core.domain.StepContribution;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.exception.ClearFailedException;
import org.springframework.batch.item.exception.FlushFailedException;
import org.springframework.batch.item.exception.MarkFailedException;
import org.springframework.batch.item.exception.ResetFailedException;
import org.springframework.batch.repeat.ExitStatus;
/**
* Strategy for processing a single item in an item-oriented step. Extends
* {@link ItemReader} and {@link ItemWriter} because part of the contract of the
* processor is that it should delegate calls to those interfaces.
*
* @author Dave Syer
*
*/
public interface ItemHandler {
/**
* Given the current context in the form of a step contribution, do whatever
* is necessary to process this unit inside a chunk. Implementations obtain
* the item and return {@link ExitStatus#FINISHED} if it is null. If it is
* not null process the item and return {@link ExitStatus#CONTINUABLE}. On
* failure throws an exception.
*
* @param contribution the current step context
* @return an {@link ExitStatus} indicating whether processing is
* continuable.
*/
ExitStatus handle(StepContribution contribution) throws Exception;
/**
* Implementations should delegate to an {@link ItemReader}.
*
* @see org.springframework.batch.item.ItemReader#mark()
*/
void mark() throws MarkFailedException;
/**
* Implementations should delegate to an {@link ItemReader}.
*
* @see org.springframework.batch.item.ItemReader#reset()
*/
void reset() throws ResetFailedException;
/**
* Implementations should delegate to an {@link ItemWriter}.
*
* @see org.springframework.batch.item.ItemWriter#flush()
*/
public void flush() throws FlushFailedException;
/**
* Implementations should delegate to an {@link ItemWriter}.
*
* @see org.springframework.batch.item.ItemWriter#clear()
*/
public void clear() throws ClearFailedException;
}

View File

@@ -1,198 +1,198 @@
/*
* 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.step.support;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.execution.step.ItemOrientedStep;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.Assert;
/**
* Base class for factory beans for {@link ItemOrientedStep}. Ensures that all
* the mandatory properties are set, and provides basic support for the
* {@link Step} interface responsibilities like start limit.
*
* @author Dave Syer
*
*/
public abstract class AbstractStepFactoryBean implements FactoryBean, BeanNameAware {
private String name;
private int startLimit = Integer.MAX_VALUE;
private boolean allowStartIfComplete;
private ItemReader itemReader;
private ItemWriter itemWriter;
private PlatformTransactionManager transactionManager;
private JobRepository jobRepository;
private boolean singleton = true;
/**
*
*/
public AbstractStepFactoryBean() {
super();
}
/**
* Set the bean name property, which will become the name of the
* {@link Step} when it is created.
*
* @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
*/
public void setBeanName(String name) {
this.name = name;
}
/**
* Public getter for the String.
* @return the name
*/
public String getName() {
return name;
}
/**
* Public setter for the startLimit.
*
* @param startLimit the startLimit to set
*/
public void setStartLimit(int startLimit) {
this.startLimit = startLimit;
}
/**
* Public setter for the shouldAllowStartIfComplete.
*
* @param allowStartIfComplete the shouldAllowStartIfComplete to set
*/
public void setAllowStartIfComplete(boolean allowStartIfComplete) {
this.allowStartIfComplete = allowStartIfComplete;
}
/**
* @param itemReader the itemReader to set
*/
public void setItemReader(ItemReader itemReader) {
this.itemReader = itemReader;
}
/**
* @param itemWriter the itemWriter to set
*/
public void setItemWriter(ItemWriter itemWriter) {
this.itemWriter = itemWriter;
}
/**
* Protected getter for the {@link ItemReader} for subclasses to use.
* @return the itemReader
*/
protected ItemReader getItemReader() {
return itemReader;
}
/**
* Protected getter for the {@link ItemWriter} for subclasses to use
* @return the itemWriter
*/
protected ItemWriter getItemWriter() {
return itemWriter;
}
/**
* Public setter for {@link JobRepository}.
*
* @param jobRepository is a mandatory dependence (no default).
*/
public void setJobRepository(JobRepository jobRepository) {
this.jobRepository = jobRepository;
}
/**
* Public setter for the {@link PlatformTransactionManager}.
*
* @param transactionManager the transaction manager to set
*/
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* Create a {@link Step} from the configuration provided.
*
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
public final Object getObject() throws Exception {
ItemOrientedStep step = new ItemOrientedStep(getName());
applyConfiguration(step);
return step;
}
/**
* @param step
*
*/
protected void applyConfiguration(ItemOrientedStep step) {
Assert.notNull(getItemReader(), "ItemReader must be provided");
Assert.notNull(getItemWriter(), "ItemWriter must be provided");
Assert.notNull(jobRepository, "JobRepository must be provided");
Assert.notNull(transactionManager, "TransactionManager must be provided");
step.setItemHandler(new SimpleItemHandler(itemReader, itemWriter));
step.setTransactionManager(transactionManager);
step.setJobRepository(jobRepository);
step.setStartLimit(startLimit);
step.setAllowStartIfComplete(allowStartIfComplete);
}
public Class getObjectType() {
return Step.class;
}
/**
* Returns true by default, but in most cases a {@link Step} should not be
* treated as thread safe. Clients are recommended to create a new step for
* each job execution.
*
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
public boolean isSingleton() {
return this.singleton;
}
/**
* Public setter for the singleton flag.
* @param singleton the value to set. Defaults to true.
*/
public void setSingleton(boolean singleton) {
this.singleton = singleton;
}
/*
* 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.step.support;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.execution.step.ItemOrientedStep;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.Assert;
/**
* Base class for factory beans for {@link ItemOrientedStep}. Ensures that all
* the mandatory properties are set, and provides basic support for the
* {@link Step} interface responsibilities like start limit.
*
* @author Dave Syer
*
*/
public abstract class AbstractStepFactoryBean implements FactoryBean, BeanNameAware {
private String name;
private int startLimit = Integer.MAX_VALUE;
private boolean allowStartIfComplete;
private ItemReader itemReader;
private ItemWriter itemWriter;
private PlatformTransactionManager transactionManager;
private JobRepository jobRepository;
private boolean singleton = true;
/**
*
*/
public AbstractStepFactoryBean() {
super();
}
/**
* Set the bean name property, which will become the name of the
* {@link Step} when it is created.
*
* @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
*/
public void setBeanName(String name) {
this.name = name;
}
/**
* Public getter for the String.
* @return the name
*/
public String getName() {
return name;
}
/**
* Public setter for the startLimit.
*
* @param startLimit the startLimit to set
*/
public void setStartLimit(int startLimit) {
this.startLimit = startLimit;
}
/**
* Public setter for the shouldAllowStartIfComplete.
*
* @param allowStartIfComplete the shouldAllowStartIfComplete to set
*/
public void setAllowStartIfComplete(boolean allowStartIfComplete) {
this.allowStartIfComplete = allowStartIfComplete;
}
/**
* @param itemReader the itemReader to set
*/
public void setItemReader(ItemReader itemReader) {
this.itemReader = itemReader;
}
/**
* @param itemWriter the itemWriter to set
*/
public void setItemWriter(ItemWriter itemWriter) {
this.itemWriter = itemWriter;
}
/**
* Protected getter for the {@link ItemReader} for subclasses to use.
* @return the itemReader
*/
protected ItemReader getItemReader() {
return itemReader;
}
/**
* Protected getter for the {@link ItemWriter} for subclasses to use
* @return the itemWriter
*/
protected ItemWriter getItemWriter() {
return itemWriter;
}
/**
* Public setter for {@link JobRepository}.
*
* @param jobRepository is a mandatory dependence (no default).
*/
public void setJobRepository(JobRepository jobRepository) {
this.jobRepository = jobRepository;
}
/**
* Public setter for the {@link PlatformTransactionManager}.
*
* @param transactionManager the transaction manager to set
*/
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* Create a {@link Step} from the configuration provided.
*
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
public final Object getObject() throws Exception {
ItemOrientedStep step = new ItemOrientedStep(getName());
applyConfiguration(step);
return step;
}
/**
* @param step
*
*/
protected void applyConfiguration(ItemOrientedStep step) {
Assert.notNull(getItemReader(), "ItemReader must be provided");
Assert.notNull(getItemWriter(), "ItemWriter must be provided");
Assert.notNull(jobRepository, "JobRepository must be provided");
Assert.notNull(transactionManager, "TransactionManager must be provided");
step.setItemHandler(new SimpleItemHandler(itemReader, itemWriter));
step.setTransactionManager(transactionManager);
step.setJobRepository(jobRepository);
step.setStartLimit(startLimit);
step.setAllowStartIfComplete(allowStartIfComplete);
}
public Class getObjectType() {
return Step.class;
}
/**
* Returns true by default, but in most cases a {@link Step} should not be
* treated as thread safe. Clients are recommended to create a new step for
* each job execution.
*
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
public boolean isSingleton() {
return this.singleton;
}
/**
* Public setter for the singleton flag.
* @param singleton the value to set. Defaults to true.
*/
public void setSingleton(boolean singleton) {
this.singleton = singleton;
}
}

View File

@@ -1,102 +1,102 @@
/*
* 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.step.support;
import org.springframework.batch.core.domain.ItemSkipPolicy;
import org.springframework.batch.core.domain.StepContribution;
import org.springframework.batch.io.Skippable;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.repeat.ExitStatus;
/**
* @author Dave Syer
*
*/
public class ItemSkipPolicyItemHandler extends SimpleItemHandler {
private ItemSkipPolicy itemSkipPolicy = new NeverSkipItemSkipPolicy();
/**
* @param itemReader
* @param itemWriter
*/
public ItemSkipPolicyItemHandler(ItemReader itemReader, ItemWriter itemWriter) {
super(itemReader, itemWriter);
}
/**
* @param itemSkipPolicy
*/
public void setItemSkipPolicy(ItemSkipPolicy itemSkipPolicy) {
this.itemSkipPolicy = itemSkipPolicy;
}
/**
* Execute the business logic, delegating to the reader and writer.
* Subclasses could extend the behaviour as long as they always return the
* value of this method call in their superclass.<br/>
*
* Read from the {@link ItemReader} and process (if not null) with the
* {@link ItemWriter}.<br/>
*
* If there is an exception and the reader or writer implements
* {@link Skippable} then the skip method is called.
*
* @param contribution the current step
* @return {@link ExitStatus#CONTINUABLE} if there is more processing to do
* @throws Exception if there is an error
*/
public ExitStatus handle(StepContribution contribution) throws Exception {
ExitStatus exitStatus = ExitStatus.CONTINUABLE;
try {
exitStatus = super.handle(contribution);
}
catch (Exception e) {
if (itemSkipPolicy.shouldSkip(e, contribution.getSkipCount())) {
contribution.incrementSkipCount();
skip();
}
else {
// Rethrow so that outer transaction is rolled back properly
throw e;
}
}
return exitStatus;
}
/**
* Mark the current item as skipped if possible. If the reader and / or
* writer are {@link Skippable} then delegate to them in that order.
*
* @see org.springframework.batch.io.Skippable#skip()
*/
private void skip() {
if (getItemReader() instanceof Skippable) {
((Skippable) getItemReader()).skip();
}
if (getItemWriter() instanceof Skippable) {
((Skippable) getItemWriter()).skip();
}
}
}
/*
* 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.step.support;
import org.springframework.batch.core.domain.ItemSkipPolicy;
import org.springframework.batch.core.domain.StepContribution;
import org.springframework.batch.io.Skippable;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.repeat.ExitStatus;
/**
* @author Dave Syer
*
*/
public class ItemSkipPolicyItemHandler extends SimpleItemHandler {
private ItemSkipPolicy itemSkipPolicy = new NeverSkipItemSkipPolicy();
/**
* @param itemReader
* @param itemWriter
*/
public ItemSkipPolicyItemHandler(ItemReader itemReader, ItemWriter itemWriter) {
super(itemReader, itemWriter);
}
/**
* @param itemSkipPolicy
*/
public void setItemSkipPolicy(ItemSkipPolicy itemSkipPolicy) {
this.itemSkipPolicy = itemSkipPolicy;
}
/**
* Execute the business logic, delegating to the reader and writer.
* Subclasses could extend the behaviour as long as they always return the
* value of this method call in their superclass.<br/>
*
* Read from the {@link ItemReader} and process (if not null) with the
* {@link ItemWriter}.<br/>
*
* If there is an exception and the reader or writer implements
* {@link Skippable} then the skip method is called.
*
* @param contribution the current step
* @return {@link ExitStatus#CONTINUABLE} if there is more processing to do
* @throws Exception if there is an error
*/
public ExitStatus handle(StepContribution contribution) throws Exception {
ExitStatus exitStatus = ExitStatus.CONTINUABLE;
try {
exitStatus = super.handle(contribution);
}
catch (Exception e) {
if (itemSkipPolicy.shouldSkip(e, contribution.getSkipCount())) {
contribution.incrementSkipCount();
skip();
}
else {
// Rethrow so that outer transaction is rolled back properly
throw e;
}
}
return exitStatus;
}
/**
* Mark the current item as skipped if possible. If the reader and / or
* writer are {@link Skippable} then delegate to them in that order.
*
* @see org.springframework.batch.io.Skippable#skip()
*/
private void skip() {
if (getItemReader() instanceof Skippable) {
((Skippable) getItemReader()).skip();
}
if (getItemWriter() instanceof Skippable) {
((Skippable) getItemWriter()).skip();
}
}
}

View File

@@ -1,140 +1,140 @@
/*
* 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.step.support;
import org.springframework.batch.core.domain.BatchListener;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepListener;
import org.springframework.batch.execution.step.ItemOrientedStep;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.repeat.RepeatOperations;
import org.springframework.batch.repeat.support.RepeatTemplate;
/**
* Factory bean for {@link Step} implementations allowing registration of
* listeners and also direct injection of the {@link RepeatOperations} needed at
* step and chunk level.
*
* @author Dave Syer
*
*/
public class RepeatOperationsStepFactoryBean extends AbstractStepFactoryBean {
private ItemStream[] streams = new ItemStream[0];
private BatchListener[] listeners = new BatchListener[0];
private RepeatOperations chunkOperations = new RepeatTemplate();
private RepeatOperations stepOperations = new RepeatTemplate();
/**
* The streams to inject into the {@link Step}. Any instance of
* {@link ItemStream} can be used, and will then receive callbacks at the
* appropriate stage in the step.
*
* @param streams an array of listeners
*/
public void setStreams(ItemStream[] streams) {
this.streams = streams;
}
/**
* The listeners to inject into the {@link Step}. Any instance of
* {@link BatchListener} can be used, and will then receive callbacks at the
* appropriate stage in the step.
*
* @param listeners an array of listeners
*/
public void setListeners(BatchListener[] listeners) {
this.listeners = listeners;
}
/**
* The {@link RepeatOperations} to use for the outer loop of the batch
* processing. Should be set up by the caller through a factory. Defaults to
* a plain {@link RepeatTemplate}.
*
* @param stepOperations a {@link RepeatOperations} instance.
*/
public void setStepOperations(RepeatOperations stepOperations) {
this.stepOperations = stepOperations;
}
/**
* The {@link RepeatOperations} to use for the inner loop of the batch
* processing. should be set up by the caller through a factory. defaults to
* a plain {@link RepeatTemplate}.
*
* @param chunkOperations a {@link RepeatOperations} instance.
*/
public void setChunkOperations(RepeatOperations chunkOperations) {
this.chunkOperations = chunkOperations;
}
/**
* @param step
*
*/
protected void applyConfiguration(ItemOrientedStep step) {
super.applyConfiguration(step);
step.setStreams(streams);
ItemReader itemReader = getItemReader();
ItemWriter itemWriter = getItemWriter();
/*
* Since we are going to wrap these things with listener callbacks we
* need to register them here because the step will not know we did
* that.
*/
if (itemReader instanceof ItemStream) {
step.registerStream((ItemStream) itemReader);
}
if (itemReader instanceof StepListener) {
step.registerStepListener((StepListener) itemReader);
}
if (itemWriter instanceof ItemStream) {
step.registerStream((ItemStream) itemWriter);
}
if (itemWriter instanceof StepListener) {
step.registerStepListener((StepListener) itemWriter);
}
BatchListenerFactoryHelper helper = new BatchListenerFactoryHelper();
StepListener[] stepListeners = helper.getStepListeners(listeners);
itemReader = helper.getItemReader(itemReader, listeners);
itemWriter = helper.getItemWriter(itemWriter, listeners);
RepeatOperations stepOperations = helper.addChunkListeners(this.stepOperations, listeners);
// In case they are used by subclasses:
setItemReader(itemReader);
setItemWriter(itemWriter);
step.setStepListeners(stepListeners);
step.setItemHandler(new SimpleItemHandler(itemReader, itemWriter));
step.setChunkOperations(chunkOperations);
step.setStepOperations(stepOperations);
}
}
/*
* 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.step.support;
import org.springframework.batch.core.domain.BatchListener;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepListener;
import org.springframework.batch.execution.step.ItemOrientedStep;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemStream;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.repeat.RepeatOperations;
import org.springframework.batch.repeat.support.RepeatTemplate;
/**
* Factory bean for {@link Step} implementations allowing registration of
* listeners and also direct injection of the {@link RepeatOperations} needed at
* step and chunk level.
*
* @author Dave Syer
*
*/
public class RepeatOperationsStepFactoryBean extends AbstractStepFactoryBean {
private ItemStream[] streams = new ItemStream[0];
private BatchListener[] listeners = new BatchListener[0];
private RepeatOperations chunkOperations = new RepeatTemplate();
private RepeatOperations stepOperations = new RepeatTemplate();
/**
* The streams to inject into the {@link Step}. Any instance of
* {@link ItemStream} can be used, and will then receive callbacks at the
* appropriate stage in the step.
*
* @param streams an array of listeners
*/
public void setStreams(ItemStream[] streams) {
this.streams = streams;
}
/**
* The listeners to inject into the {@link Step}. Any instance of
* {@link BatchListener} can be used, and will then receive callbacks at the
* appropriate stage in the step.
*
* @param listeners an array of listeners
*/
public void setListeners(BatchListener[] listeners) {
this.listeners = listeners;
}
/**
* The {@link RepeatOperations} to use for the outer loop of the batch
* processing. Should be set up by the caller through a factory. Defaults to
* a plain {@link RepeatTemplate}.
*
* @param stepOperations a {@link RepeatOperations} instance.
*/
public void setStepOperations(RepeatOperations stepOperations) {
this.stepOperations = stepOperations;
}
/**
* The {@link RepeatOperations} to use for the inner loop of the batch
* processing. should be set up by the caller through a factory. defaults to
* a plain {@link RepeatTemplate}.
*
* @param chunkOperations a {@link RepeatOperations} instance.
*/
public void setChunkOperations(RepeatOperations chunkOperations) {
this.chunkOperations = chunkOperations;
}
/**
* @param step
*
*/
protected void applyConfiguration(ItemOrientedStep step) {
super.applyConfiguration(step);
step.setStreams(streams);
ItemReader itemReader = getItemReader();
ItemWriter itemWriter = getItemWriter();
/*
* Since we are going to wrap these things with listener callbacks we
* need to register them here because the step will not know we did
* that.
*/
if (itemReader instanceof ItemStream) {
step.registerStream((ItemStream) itemReader);
}
if (itemReader instanceof StepListener) {
step.registerStepListener((StepListener) itemReader);
}
if (itemWriter instanceof ItemStream) {
step.registerStream((ItemStream) itemWriter);
}
if (itemWriter instanceof StepListener) {
step.registerStepListener((StepListener) itemWriter);
}
BatchListenerFactoryHelper helper = new BatchListenerFactoryHelper();
StepListener[] stepListeners = helper.getStepListeners(listeners);
itemReader = helper.getItemReader(itemReader, listeners);
itemWriter = helper.getItemWriter(itemWriter, listeners);
RepeatOperations stepOperations = helper.addChunkListeners(this.stepOperations, listeners);
// In case they are used by subclasses:
setItemReader(itemReader);
setItemWriter(itemWriter);
step.setStepListeners(stepListeners);
step.setItemHandler(new SimpleItemHandler(itemReader, itemWriter));
step.setChunkOperations(chunkOperations);
step.setStepOperations(stepOperations);
}
}

View File

@@ -1,79 +1,79 @@
/*
* 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.step.support;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.springframework.batch.core.domain.JobInterruptedException;
import org.springframework.batch.core.runtime.ExitStatusExceptionClassifier;
import org.springframework.batch.repeat.ExitStatus;
/**
* <p>
* Simple implementation of {@link ExitStatusExceptionClassifier} 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.
* </p>
*
* @author Lucas Ward
*
*/
public class SimpleExitStatusExceptionClassifier implements
ExitStatusExceptionClassifier {
/* (non-Javadoc)
* @see org.springframework.batch.core.executor.ExitCodeExceptionClassifier#classifyForExitCode(java.lang.Throwable)
*/
public ExitStatus classifyForExitCode(Throwable throwable) {
return (ExitStatus) classify(throwable);
}
/* (non-Javadoc)
* @see org.springframework.batch.common.ExceptionClassifier#classify(java.lang.Throwable)
*/
public Object classify(Throwable throwable) {
ExitStatus exitStatus = ExitStatus.FAILED;
if (throwable instanceof JobInterruptedException) {
exitStatus = new ExitStatus(false, JOB_INTERRUPTED,
JobInterruptedException.class.getName());
} else {
String message = "";
if (throwable!=null) {
StringWriter writer = new StringWriter();
throwable.printStackTrace(new PrintWriter(writer));
message = writer.toString();
}
exitStatus = new ExitStatus(false, FATAL_EXCEPTION, message);
}
return exitStatus;
}
/*
* (non-Javadoc)
*
* @see org.springframework.batch.common.ExceptionClassifier#getDefault()
*/
public Object getDefault() {
// return without message since we don't know what the exception is
return new ExitStatus(false, FATAL_EXCEPTION);
}
}
/*
* 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.step.support;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.springframework.batch.core.domain.JobInterruptedException;
import org.springframework.batch.core.runtime.ExitStatusExceptionClassifier;
import org.springframework.batch.repeat.ExitStatus;
/**
* <p>
* Simple implementation of {@link ExitStatusExceptionClassifier} 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.
* </p>
*
* @author Lucas Ward
*
*/
public class SimpleExitStatusExceptionClassifier implements
ExitStatusExceptionClassifier {
/* (non-Javadoc)
* @see org.springframework.batch.core.executor.ExitCodeExceptionClassifier#classifyForExitCode(java.lang.Throwable)
*/
public ExitStatus classifyForExitCode(Throwable throwable) {
return (ExitStatus) classify(throwable);
}
/* (non-Javadoc)
* @see org.springframework.batch.common.ExceptionClassifier#classify(java.lang.Throwable)
*/
public Object classify(Throwable throwable) {
ExitStatus exitStatus = ExitStatus.FAILED;
if (throwable instanceof JobInterruptedException) {
exitStatus = new ExitStatus(false, JOB_INTERRUPTED,
JobInterruptedException.class.getName());
} else {
String message = "";
if (throwable!=null) {
StringWriter writer = new StringWriter();
throwable.printStackTrace(new PrintWriter(writer));
message = writer.toString();
}
exitStatus = new ExitStatus(false, FATAL_EXCEPTION, message);
}
return exitStatus;
}
/*
* (non-Javadoc)
*
* @see org.springframework.batch.common.ExceptionClassifier#getDefault()
*/
public Object getDefault() {
// return without message since we don't know what the exception is
return new ExitStatus(false, FATAL_EXCEPTION);
}
}

View File

@@ -1,115 +1,115 @@
/*
* 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.step.support;
import org.springframework.batch.core.domain.StepContribution;
import org.springframework.batch.execution.step.ItemHandler;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.exception.ClearFailedException;
import org.springframework.batch.item.exception.FlushFailedException;
import org.springframework.batch.item.exception.MarkFailedException;
import org.springframework.batch.item.exception.ResetFailedException;
import org.springframework.batch.repeat.ExitStatus;
/**
* Simplest possible implementation of {@link ItemHandler} with no skipping or
* recovering. Just delegates all calls to the provided {@link ItemReader} and
* {@link ItemWriter}.
*
* @author Dave Syer
*
*/
public class SimpleItemHandler implements ItemHandler {
private ItemReader itemReader;
private ItemWriter itemWriter;
/**
* @param itemReader
* @param itemWriter
*/
public SimpleItemHandler(ItemReader itemReader, ItemWriter itemWriter) {
super();
this.itemReader = itemReader;
this.itemWriter = itemWriter;
}
/**
* Public getter for the ItemReader.
* @return the itemReader
*/
public ItemReader getItemReader() {
return itemReader;
}
/**
* Public getter for the ItemWriter.
* @return the itemWriter
*/
public ItemWriter getItemWriter() {
return itemWriter;
}
/**
* Read from the {@link ItemReader} and process (if not null) with the
* {@link ItemWriter}.
*
* @see org.springframework.batch.execution.step.ItemHandler#handle(org.springframework.batch.core.domain.StepContribution)
*/
public ExitStatus handle(StepContribution contribution) throws Exception {
Object item = itemReader.read();
if (item == null) {
return ExitStatus.FINISHED;
}
itemWriter.write(item);
return ExitStatus.CONTINUABLE;
}
/**
* @throws MarkFailedException
* @see org.springframework.batch.item.ItemReader#mark()
*/
public void mark() throws MarkFailedException {
itemReader.mark();
}
/**
* @throws ResetFailedException
* @see org.springframework.batch.item.ItemReader#reset()
*/
public void reset() throws ResetFailedException {
itemReader.reset();
}
/**
* @throws ClearFailedException
* @see org.springframework.batch.item.ItemWriter#clear()
*/
public void clear() throws ClearFailedException {
itemWriter.clear();
}
/**
* @throws FlushFailedException
* @see org.springframework.batch.item.ItemWriter#flush()
*/
public void flush() throws FlushFailedException {
itemWriter.flush();
}
}
/*
* 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.step.support;
import org.springframework.batch.core.domain.StepContribution;
import org.springframework.batch.execution.step.ItemHandler;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.exception.ClearFailedException;
import org.springframework.batch.item.exception.FlushFailedException;
import org.springframework.batch.item.exception.MarkFailedException;
import org.springframework.batch.item.exception.ResetFailedException;
import org.springframework.batch.repeat.ExitStatus;
/**
* Simplest possible implementation of {@link ItemHandler} with no skipping or
* recovering. Just delegates all calls to the provided {@link ItemReader} and
* {@link ItemWriter}.
*
* @author Dave Syer
*
*/
public class SimpleItemHandler implements ItemHandler {
private ItemReader itemReader;
private ItemWriter itemWriter;
/**
* @param itemReader
* @param itemWriter
*/
public SimpleItemHandler(ItemReader itemReader, ItemWriter itemWriter) {
super();
this.itemReader = itemReader;
this.itemWriter = itemWriter;
}
/**
* Public getter for the ItemReader.
* @return the itemReader
*/
public ItemReader getItemReader() {
return itemReader;
}
/**
* Public getter for the ItemWriter.
* @return the itemWriter
*/
public ItemWriter getItemWriter() {
return itemWriter;
}
/**
* Read from the {@link ItemReader} and process (if not null) with the
* {@link ItemWriter}.
*
* @see org.springframework.batch.execution.step.ItemHandler#handle(org.springframework.batch.core.domain.StepContribution)
*/
public ExitStatus handle(StepContribution contribution) throws Exception {
Object item = itemReader.read();
if (item == null) {
return ExitStatus.FINISHED;
}
itemWriter.write(item);
return ExitStatus.CONTINUABLE;
}
/**
* @throws MarkFailedException
* @see org.springframework.batch.item.ItemReader#mark()
*/
public void mark() throws MarkFailedException {
itemReader.mark();
}
/**
* @throws ResetFailedException
* @see org.springframework.batch.item.ItemReader#reset()
*/
public void reset() throws ResetFailedException {
itemReader.reset();
}
/**
* @throws ClearFailedException
* @see org.springframework.batch.item.ItemWriter#clear()
*/
public void clear() throws ClearFailedException {
itemWriter.clear();
}
/**
* @throws FlushFailedException
* @see org.springframework.batch.item.ItemWriter#flush()
*/
public void flush() throws FlushFailedException {
itemWriter.flush();
}
}

View File

@@ -1,197 +1,197 @@
/*
* 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.step.support;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepContribution;
import org.springframework.batch.execution.step.ItemOrientedStep;
import org.springframework.batch.item.ItemKeyGenerator;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemRecoverer;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.retry.RetryOperations;
import org.springframework.batch.retry.RetryPolicy;
import org.springframework.batch.retry.backoff.BackOffPolicy;
import org.springframework.batch.retry.callback.ItemReaderRetryCallback;
import org.springframework.batch.retry.policy.ItemReaderRetryPolicy;
import org.springframework.batch.retry.policy.SimpleRetryPolicy;
import org.springframework.batch.retry.support.RetryTemplate;
/**
* Factory bean for step that executes its item processing with a stateful
* retry. Failed items are never skipped, but always cause a rollback. Before a
* rollback, the {@link Step} makes a record of the failed item, caching it
* under a key given by the {@link ItemKeyGenerator}. Then when it is
* re-presented by the {@link ItemReader} it is recognised and retried up to a
* limit given by the {@link RetryPolicy}. When the retry is exhausted instead
* of the item being skipped it is handled by an {@link ItemRecoverer}.<br/>
*
* The skipLimit property is still used to control the overall exception
* handling policy. Only exhausted retries count against the exception handler,
* instead of counting all exceptions.
*
* @author Dave Syer
*
*/
public class StatefulRetryStepFactoryBean extends DefaultStepFactoryBean {
private ItemKeyGenerator itemKeyGenerator;
private ItemRecoverer itemRecoverer;
private int retryLimit;
private Class[] retryableExceptionClasses;
private BackOffPolicy backOffPolicy;
/**
* Public setter for the retry limit. Each item can be retried up to this
* limit.
* @param retryLimit the retry limit to set
*/
public void setRetryLimit(int retryLimit) {
this.retryLimit = retryLimit;
}
/**
* Public setter for the Class[].
* @param retryableExceptionClasses the retryableExceptionClasses to set
*/
public void setRetryableExceptionClasses(Class[] retryableExceptionClasses) {
this.retryableExceptionClasses = retryableExceptionClasses;
}
/**
* Public setter for the {@link BackOffPolicy}.
* @param backOffPolicy the {@link BackOffPolicy} to set
*/
public void setBackOffPolicy(BackOffPolicy backOffPolicy) {
this.backOffPolicy = backOffPolicy;
}
/**
* Public setter for the {@link ItemKeyGenerator} which will be used to
* cache failed items between transactions. If it is not injected but the
* reader or writer implement {@link ItemKeyGenerator}, one of those will
* be used instead (preferring the reader to the writer if both would be
* appropriate). If neither can be used, then the default will be to just
* use the item itself as a cache key.
*
* @param itemKeyGenerator the {@link ItemKeyGenerator} to set
*/
public void setItemKeyGenerator(ItemKeyGenerator itemKeyGenerator) {
this.itemKeyGenerator = itemKeyGenerator;
}
/**
* Public setter for the {@link ItemRecoverer}. If this is set the
* {@link ItemRecoverer#recover(Object, Throwable)} will be called when
* retry is exhausted, and within the business transaction (which will not
* roll back because of any other item-related errors).
*
* @param itemRecoverer the {@link ItemRecoverer} to set
*/
public void setItemRecoverer(ItemRecoverer itemRecoverer) {
this.itemRecoverer = itemRecoverer;
}
/**
* @param step
*
*/
protected void applyConfiguration(ItemOrientedStep step) {
super.applyConfiguration(step);
if (retryLimit > 0) {
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(retryLimit);
if (retryableExceptionClasses != null) {
retryPolicy.setRetryableExceptionClasses(retryableExceptionClasses);
}
// Co-ordinate the retry policy with the exception handler:
getStepOperations()
.setExceptionHandler(new SimpleRetryExceptionHandler(retryPolicy, getExceptionHandler()));
ItemReaderRetryCallback retryCallback = new ItemReaderRetryCallback(getItemReader(), itemKeyGenerator,
getItemWriter());
retryCallback.setRecoverer(itemRecoverer);
ItemReaderRetryPolicy itemProviderRetryPolicy = new ItemReaderRetryPolicy(retryPolicy);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(itemProviderRetryPolicy);
if (backOffPolicy != null) {
retryTemplate.setBackOffPolicy(backOffPolicy);
}
StatefulRetryItemHandler itemProcessor = new StatefulRetryItemHandler(getItemReader(), getItemWriter(),
retryTemplate, retryCallback);
step.setItemHandler(itemProcessor);
}
}
private static class StatefulRetryItemHandler extends SimpleItemHandler {
final private RetryOperations retryOperations;
final private ItemReaderRetryCallback retryCallback;
/**
* @param itemReader
* @param itemWriter
* @param retryCallback
* @param retryTemplate
*/
public StatefulRetryItemHandler(ItemReader itemReader, ItemWriter itemWriter, RetryOperations retryTemplate,
ItemReaderRetryCallback retryCallback) {
super(itemReader, itemWriter);
this.retryOperations = retryTemplate;
this.retryCallback = retryCallback;
}
/**
* Execute the business logic, delegating to the reader and writer.
* Subclasses could extend the behaviour as long as they always return
* the value of this method call in their superclass.<br/>
*
* Read from the {@link ItemReader} and process (if not null) with the
* {@link ItemWriter}. The call to {@link ItemWriter} is wrapped in a
* stateful retry. In that case the {@link ItemRecoverer} is used (if
* provided) in the case of an exception to apply alternate processing
* to the item. If the stateful retry is in place then the recovery will
* happen in the next transaction automatically, otherwise it might be
* necessary for clients to make the recover method transactional with
* appropriate propagation behaviour (probably REQUIRES_NEW because the
* call will happen in the context of a transaction that is about to
* rollback).<br/>
*
* @param contribution the current step
* @return {@link ExitStatus#CONTINUABLE} if there is more processing to
* do
* @throws Exception if there is an error
*/
public ExitStatus handle(StepContribution contribution) throws Exception {
return new ExitStatus(retryOperations.execute(retryCallback) != null);
}
}
}
/*
* 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.step.support;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepContribution;
import org.springframework.batch.execution.step.ItemOrientedStep;
import org.springframework.batch.item.ItemKeyGenerator;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemRecoverer;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.batch.retry.RetryOperations;
import org.springframework.batch.retry.RetryPolicy;
import org.springframework.batch.retry.backoff.BackOffPolicy;
import org.springframework.batch.retry.callback.ItemReaderRetryCallback;
import org.springframework.batch.retry.policy.ItemReaderRetryPolicy;
import org.springframework.batch.retry.policy.SimpleRetryPolicy;
import org.springframework.batch.retry.support.RetryTemplate;
/**
* Factory bean for step that executes its item processing with a stateful
* retry. Failed items are never skipped, but always cause a rollback. Before a
* rollback, the {@link Step} makes a record of the failed item, caching it
* under a key given by the {@link ItemKeyGenerator}. Then when it is
* re-presented by the {@link ItemReader} it is recognised and retried up to a
* limit given by the {@link RetryPolicy}. When the retry is exhausted instead
* of the item being skipped it is handled by an {@link ItemRecoverer}.<br/>
*
* The skipLimit property is still used to control the overall exception
* handling policy. Only exhausted retries count against the exception handler,
* instead of counting all exceptions.
*
* @author Dave Syer
*
*/
public class StatefulRetryStepFactoryBean extends DefaultStepFactoryBean {
private ItemKeyGenerator itemKeyGenerator;
private ItemRecoverer itemRecoverer;
private int retryLimit;
private Class[] retryableExceptionClasses;
private BackOffPolicy backOffPolicy;
/**
* Public setter for the retry limit. Each item can be retried up to this
* limit.
* @param retryLimit the retry limit to set
*/
public void setRetryLimit(int retryLimit) {
this.retryLimit = retryLimit;
}
/**
* Public setter for the Class[].
* @param retryableExceptionClasses the retryableExceptionClasses to set
*/
public void setRetryableExceptionClasses(Class[] retryableExceptionClasses) {
this.retryableExceptionClasses = retryableExceptionClasses;
}
/**
* Public setter for the {@link BackOffPolicy}.
* @param backOffPolicy the {@link BackOffPolicy} to set
*/
public void setBackOffPolicy(BackOffPolicy backOffPolicy) {
this.backOffPolicy = backOffPolicy;
}
/**
* Public setter for the {@link ItemKeyGenerator} which will be used to
* cache failed items between transactions. If it is not injected but the
* reader or writer implement {@link ItemKeyGenerator}, one of those will
* be used instead (preferring the reader to the writer if both would be
* appropriate). If neither can be used, then the default will be to just
* use the item itself as a cache key.
*
* @param itemKeyGenerator the {@link ItemKeyGenerator} to set
*/
public void setItemKeyGenerator(ItemKeyGenerator itemKeyGenerator) {
this.itemKeyGenerator = itemKeyGenerator;
}
/**
* Public setter for the {@link ItemRecoverer}. If this is set the
* {@link ItemRecoverer#recover(Object, Throwable)} will be called when
* retry is exhausted, and within the business transaction (which will not
* roll back because of any other item-related errors).
*
* @param itemRecoverer the {@link ItemRecoverer} to set
*/
public void setItemRecoverer(ItemRecoverer itemRecoverer) {
this.itemRecoverer = itemRecoverer;
}
/**
* @param step
*
*/
protected void applyConfiguration(ItemOrientedStep step) {
super.applyConfiguration(step);
if (retryLimit > 0) {
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(retryLimit);
if (retryableExceptionClasses != null) {
retryPolicy.setRetryableExceptionClasses(retryableExceptionClasses);
}
// Co-ordinate the retry policy with the exception handler:
getStepOperations()
.setExceptionHandler(new SimpleRetryExceptionHandler(retryPolicy, getExceptionHandler()));
ItemReaderRetryCallback retryCallback = new ItemReaderRetryCallback(getItemReader(), itemKeyGenerator,
getItemWriter());
retryCallback.setRecoverer(itemRecoverer);
ItemReaderRetryPolicy itemProviderRetryPolicy = new ItemReaderRetryPolicy(retryPolicy);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryPolicy(itemProviderRetryPolicy);
if (backOffPolicy != null) {
retryTemplate.setBackOffPolicy(backOffPolicy);
}
StatefulRetryItemHandler itemProcessor = new StatefulRetryItemHandler(getItemReader(), getItemWriter(),
retryTemplate, retryCallback);
step.setItemHandler(itemProcessor);
}
}
private static class StatefulRetryItemHandler extends SimpleItemHandler {
final private RetryOperations retryOperations;
final private ItemReaderRetryCallback retryCallback;
/**
* @param itemReader
* @param itemWriter
* @param retryCallback
* @param retryTemplate
*/
public StatefulRetryItemHandler(ItemReader itemReader, ItemWriter itemWriter, RetryOperations retryTemplate,
ItemReaderRetryCallback retryCallback) {
super(itemReader, itemWriter);
this.retryOperations = retryTemplate;
this.retryCallback = retryCallback;
}
/**
* Execute the business logic, delegating to the reader and writer.
* Subclasses could extend the behaviour as long as they always return
* the value of this method call in their superclass.<br/>
*
* Read from the {@link ItemReader} and process (if not null) with the
* {@link ItemWriter}. The call to {@link ItemWriter} is wrapped in a
* stateful retry. In that case the {@link ItemRecoverer} is used (if
* provided) in the case of an exception to apply alternate processing
* to the item. If the stateful retry is in place then the recovery will
* happen in the next transaction automatically, otherwise it might be
* necessary for clients to make the recover method transactional with
* appropriate propagation behaviour (probably REQUIRES_NEW because the
* call will happen in the context of a transaction that is about to
* rollback).<br/>
*
* @param contribution the current step
* @return {@link ExitStatus#CONTINUABLE} if there is more processing to
* do
* @throws Exception if there is an error
*/
public ExitStatus handle(StepContribution contribution) throws Exception {
return new ExitStatus(retryOperations.execute(retryCallback) != null);
}
}
}

View File

@@ -1,8 +1,8 @@
platform=db2
# SQL language oddities
BIGINT = BIGINT
IDENTITY =
DOUBLE = DOUBLE PRECISION
BLOB = BLOB
# for generating drop statements...
SEQUENCE = SEQUENCE
platform=db2
# SQL language oddities
BIGINT = BIGINT
IDENTITY =
DOUBLE = DOUBLE PRECISION
BLOB = BLOB
# for generating drop statements...
SEQUENCE = SEQUENCE

View File

@@ -1,2 +1,2 @@
#macro (sequence $name)CREATE SEQUENCE ${name};
#end
#macro (sequence $name)CREATE SEQUENCE ${name};
#end

View File

@@ -1,9 +1,9 @@
platform=db2
# SQL language oddities
BIGINT = BIGINT
IDENTITY =
GENERATED = GENERATED BY DEFAULT AS IDENTITY
DOUBLE = DOUBLE PRECISION
BLOB = BLOB
# for generating drop statements...
SEQUENCE = TABLE
platform=db2
# SQL language oddities
BIGINT = BIGINT
IDENTITY =
GENERATED = GENERATED BY DEFAULT AS IDENTITY
DOUBLE = DOUBLE PRECISION
BLOB = BLOB
# for generating drop statements...
SEQUENCE = TABLE

View File

@@ -1,2 +1,2 @@
#macro (sequence $name)CREATE TABLE ${name} (ID BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, DUMMY VARCHAR(1));
#end
#macro (sequence $name)CREATE TABLE ${name} (ID BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, DUMMY VARCHAR(1));
#end

View File

@@ -1,9 +1,9 @@
platform=hsqldb
# SQL language oddities
BIGINT = BIGINT
IDENTITY = IDENTITY
IFEXISTS = IF EXISTS
DOUBLE = DOUBLE PRECISION
BLOB = LONGVARBINARY
# for generating drop statements...
SEQUENCE = TABLE
platform=hsqldb
# SQL language oddities
BIGINT = BIGINT
IDENTITY = IDENTITY
IFEXISTS = IF EXISTS
DOUBLE = DOUBLE PRECISION
BLOB = LONGVARBINARY
# for generating drop statements...
SEQUENCE = TABLE

View File

@@ -1,4 +1,4 @@
#macro (sequence $name)CREATE TABLE ${name} (
ID BIGINT IDENTITY
);
#end
#macro (sequence $name)CREATE TABLE ${name} (
ID BIGINT IDENTITY
);
#end

View File

@@ -1,10 +1,10 @@
platform=oracle10g
# SQL language oddities
BIGINT = BIGINT
IDENTITY = unsigned
GENERATED =
IFEXISTSBEFORE = IF EXISTS
DOUBLE = DOUBLE PRECISION
BLOB = BLOB
# for generating drop statements...
SEQUENCE = TABLE
platform=oracle10g
# SQL language oddities
BIGINT = BIGINT
IDENTITY = unsigned
GENERATED =
IFEXISTSBEFORE = IF EXISTS
DOUBLE = DOUBLE PRECISION
BLOB = BLOB
# for generating drop statements...
SEQUENCE = TABLE

View File

@@ -1,3 +1,3 @@
#macro (sequence $name)CREATE TABLE ${name} (ID BIGINT NOT NULL) type=MYISAM;
INSERT INTO ${name} values(0);
#end
#macro (sequence $name)CREATE TABLE ${name} (ID BIGINT NOT NULL) type=MYISAM;
INSERT INTO ${name} values(0);
#end

View File

@@ -1,9 +1,9 @@
platform=oracle10g
# SQL language oddities
BIGINT = NUMBER(38)
IDENTITY =
GENERATED =
DOUBLE = DOUBLE PRECISION
BLOB = BLOB
# for generating drop statements...
SEQUENCE = SEQUENCE
platform=oracle10g
# SQL language oddities
BIGINT = NUMBER(38)
IDENTITY =
GENERATED =
DOUBLE = DOUBLE PRECISION
BLOB = BLOB
# for generating drop statements...
SEQUENCE = SEQUENCE

View File

@@ -1,2 +1,2 @@
#macro (sequence $name)CREATE SEQUENCE ${name};
#end
#macro (sequence $name)CREATE SEQUENCE ${name};
#end

View File

@@ -1,9 +1,9 @@
platform=postgresql
# SQL language oddities
BIGINT = BIGINT
IDENTITY =
GENERATED =
DOUBLE = DOUBLE PRECISION
BLOB = BYTEA
# for generating drop statements...
SEQUENCE = SEQUENCE
platform=postgresql
# SQL language oddities
BIGINT = BIGINT
IDENTITY =
GENERATED =
DOUBLE = DOUBLE PRECISION
BLOB = BYTEA
# for generating drop statements...
SEQUENCE = SEQUENCE

View File

@@ -1,2 +1,2 @@
#macro (sequence $name)CREATE SEQUENCE ${name};
#end
#macro (sequence $name)CREATE SEQUENCE ${name};
#end

View File

@@ -1,131 +1,131 @@
/*
* 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.configuration;
import java.util.Collection;
import junit.framework.TestCase;
import org.springframework.batch.core.repository.DuplicateJobException;
import org.springframework.batch.core.repository.NoSuchJobException;
import org.springframework.batch.execution.job.JobSupport;
import org.springframework.beans.FatalBeanException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Dave Syer
*
*/
public class JobRegistryBeanPostProcessorTests extends TestCase {
private JobRegistryBeanPostProcessor processor = new JobRegistryBeanPostProcessor();
public void testInitialization() throws Exception {
try {
processor.afterPropertiesSet();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected
assertTrue(e.getMessage().indexOf("JobConfigurationRegistry") >= 0);
}
}
public void testBeforeInitialization() throws Exception {
// should be a no-op
assertEquals("foo", processor.postProcessAfterInitialization("foo",
"bar"));
}
public void testAfterInitializationWithWrongType() throws Exception {
// should be a no-op
assertEquals("foo", processor.postProcessAfterInitialization("foo",
"bar"));
}
public void testAfterInitializationWithCorrectType() throws Exception {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
JobSupport configuration = new JobSupport();
configuration.setBeanName("foo");
assertEquals(configuration, processor.postProcessAfterInitialization(
configuration, "bar"));
assertEquals(configuration, registry.getJob("foo"));
}
public void testAfterInitializationWithDuplicate() throws Exception {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
JobSupport configuration = new JobSupport();
configuration.setBeanName("foo");
processor.postProcessAfterInitialization(configuration, "bar");
try {
processor.postProcessAfterInitialization(configuration, "spam");
fail("Expected FatalBeanException");
} catch (FatalBeanException e) {
// Expected
assertTrue(e.getCause() instanceof DuplicateJobException);
}
}
public void testUnregisterOnDestroy() throws Exception {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
JobSupport configuration = new JobSupport();
configuration.setBeanName("foo");
assertEquals(configuration, processor.postProcessAfterInitialization(
configuration, "bar"));
processor.destroy();
try {
assertEquals(null, registry.getJob("foo"));
fail("Expected NoSuchJobConfigurationException");
} catch (NoSuchJobException e) {
// expected
}
}
public void testExecutionWithApplicationContext() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"test-context.xml", getClass());
MapJobRegistry registry = (MapJobRegistry) context
.getBean("registry");
Collection configurations = registry.getJobNames();
// System.err.println(configurations);
String[] names = context.getBeanNamesForType(JobSupport.class);
int count = names.length;
// Each concrete bean of type JobConfiguration is registered...
assertEquals(count, configurations.size());
// N.B. there is a failure / wonky mode where a parent bean is given an
// explicit name or beanName (using property setter): in this case then
// child beans will have the same name and will be re-registered (and
// override, if the registry supports that).
assertNotNull(registry.getJob("test-job"));
assertEquals(context.getBean("test-job-with-name"), registry
.getJob("foo"));
assertEquals(context.getBean("test-job-with-bean-name"), registry
.getJob("bar"));
assertEquals(context.getBean("test-job-with-parent-and-name"), registry
.getJob("spam"));
assertEquals(context.getBean("test-job-with-parent-and-bean-name"),
registry.getJob("bucket"));
assertEquals(context.getBean("test-job-with-concrete-parent"), registry
.getJob("maps"));
assertEquals(context.getBean("test-job-with-concrete-parent-and-name"),
registry.getJob("oof"));
assertEquals(context
.getBean("test-job-with-concrete-parent-and-bean-name"),
registry.getJob("rab"));
}
}
/*
* 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.configuration;
import java.util.Collection;
import junit.framework.TestCase;
import org.springframework.batch.core.repository.DuplicateJobException;
import org.springframework.batch.core.repository.NoSuchJobException;
import org.springframework.batch.execution.job.JobSupport;
import org.springframework.beans.FatalBeanException;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Dave Syer
*
*/
public class JobRegistryBeanPostProcessorTests extends TestCase {
private JobRegistryBeanPostProcessor processor = new JobRegistryBeanPostProcessor();
public void testInitialization() throws Exception {
try {
processor.afterPropertiesSet();
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
// expected
assertTrue(e.getMessage().indexOf("JobConfigurationRegistry") >= 0);
}
}
public void testBeforeInitialization() throws Exception {
// should be a no-op
assertEquals("foo", processor.postProcessAfterInitialization("foo",
"bar"));
}
public void testAfterInitializationWithWrongType() throws Exception {
// should be a no-op
assertEquals("foo", processor.postProcessAfterInitialization("foo",
"bar"));
}
public void testAfterInitializationWithCorrectType() throws Exception {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
JobSupport configuration = new JobSupport();
configuration.setBeanName("foo");
assertEquals(configuration, processor.postProcessAfterInitialization(
configuration, "bar"));
assertEquals(configuration, registry.getJob("foo"));
}
public void testAfterInitializationWithDuplicate() throws Exception {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
JobSupport configuration = new JobSupport();
configuration.setBeanName("foo");
processor.postProcessAfterInitialization(configuration, "bar");
try {
processor.postProcessAfterInitialization(configuration, "spam");
fail("Expected FatalBeanException");
} catch (FatalBeanException e) {
// Expected
assertTrue(e.getCause() instanceof DuplicateJobException);
}
}
public void testUnregisterOnDestroy() throws Exception {
MapJobRegistry registry = new MapJobRegistry();
processor.setJobRegistry(registry);
JobSupport configuration = new JobSupport();
configuration.setBeanName("foo");
assertEquals(configuration, processor.postProcessAfterInitialization(
configuration, "bar"));
processor.destroy();
try {
assertEquals(null, registry.getJob("foo"));
fail("Expected NoSuchJobConfigurationException");
} catch (NoSuchJobException e) {
// expected
}
}
public void testExecutionWithApplicationContext() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"test-context.xml", getClass());
MapJobRegistry registry = (MapJobRegistry) context
.getBean("registry");
Collection configurations = registry.getJobNames();
// System.err.println(configurations);
String[] names = context.getBeanNamesForType(JobSupport.class);
int count = names.length;
// Each concrete bean of type JobConfiguration is registered...
assertEquals(count, configurations.size());
// N.B. there is a failure / wonky mode where a parent bean is given an
// explicit name or beanName (using property setter): in this case then
// child beans will have the same name and will be re-registered (and
// override, if the registry supports that).
assertNotNull(registry.getJob("test-job"));
assertEquals(context.getBean("test-job-with-name"), registry
.getJob("foo"));
assertEquals(context.getBean("test-job-with-bean-name"), registry
.getJob("bar"));
assertEquals(context.getBean("test-job-with-parent-and-name"), registry
.getJob("spam"));
assertEquals(context.getBean("test-job-with-parent-and-bean-name"),
registry.getJob("bucket"));
assertEquals(context.getBean("test-job-with-concrete-parent"), registry
.getJob("maps"));
assertEquals(context.getBean("test-job-with-concrete-parent-and-name"),
registry.getJob("oof"));
assertEquals(context
.getBean("test-job-with-concrete-parent-and-bean-name"),
registry.getJob("rab"));
}
}

View File

@@ -1,95 +1,95 @@
/*
* 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.configuration;
import java.util.Collection;
import junit.framework.TestCase;
import org.springframework.batch.core.configuration.JobFactory;
import org.springframework.batch.core.repository.DuplicateJobException;
import org.springframework.batch.core.repository.NoSuchJobException;
import org.springframework.batch.execution.job.JobSupport;
/**
* @author Dave Syer
*
*/
public class MapJobRegistryTests extends TestCase {
private MapJobRegistry registry = new MapJobRegistry();
/**
* Test method for {@link org.springframework.batch.execution.configuration.MapJobRegistry#unregister(String)}.
* @throws Exception
*/
public void testUnregister() throws Exception {
registry.register(new ReferenceJobFactory(new JobSupport("foo")));
assertNotNull(registry.getJob("foo"));
registry.unregister("foo");
try {
assertNull(registry.getJob("foo"));
fail("Expected NoSuchJobConfigurationException");
}
catch (NoSuchJobException e) {
// expected
assertTrue(e.getMessage().indexOf("foo")>=0);
}
}
/**
* Test method for {@link org.springframework.batch.execution.configuration.MapJobRegistry#getJob(java.lang.String)}.
*/
public void testReplaceDuplicateConfiguration() throws Exception {
registry.register(new ReferenceJobFactory(new JobSupport("foo")));
try {
registry.register(new ReferenceJobFactory(new JobSupport("foo")));
fail("Expected DuplicateJobConfigurationException");
} catch (DuplicateJobException e) {
// unexpected: even if the job is different we want a DuplicateJobException
assertTrue(e.getMessage().indexOf("foo")>=0);
}
}
/**
* Test method for {@link org.springframework.batch.execution.configuration.MapJobRegistry#getJob(java.lang.String)}.
*/
public void testRealDuplicateConfiguration() throws Exception {
JobFactory jobFactory = new ReferenceJobFactory(new JobSupport("foo"));
registry.register(jobFactory);
try {
registry.register(jobFactory);
fail("Unexpected DuplicateJobConfigurationException");
} catch (DuplicateJobException e) {
// expected
assertTrue(e.getMessage().indexOf("foo")>=0);
}
}
/**
* Test method for {@link org.springframework.batch.execution.configuration.MapJobRegistry#getJobNames()}.
* @throws Exception
*/
public void testGetJobConfigurations() throws Exception {
JobFactory jobFactory = new ReferenceJobFactory(new JobSupport("foo"));
registry.register(jobFactory);
registry.register(new ReferenceJobFactory(new JobSupport("bar")));
Collection configurations = registry.getJobNames();
assertEquals(2, configurations.size());
assertTrue(configurations.contains(jobFactory.getJobName()));
}
}
/*
* 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.configuration;
import java.util.Collection;
import junit.framework.TestCase;
import org.springframework.batch.core.configuration.JobFactory;
import org.springframework.batch.core.repository.DuplicateJobException;
import org.springframework.batch.core.repository.NoSuchJobException;
import org.springframework.batch.execution.job.JobSupport;
/**
* @author Dave Syer
*
*/
public class MapJobRegistryTests extends TestCase {
private MapJobRegistry registry = new MapJobRegistry();
/**
* Test method for {@link org.springframework.batch.execution.configuration.MapJobRegistry#unregister(String)}.
* @throws Exception
*/
public void testUnregister() throws Exception {
registry.register(new ReferenceJobFactory(new JobSupport("foo")));
assertNotNull(registry.getJob("foo"));
registry.unregister("foo");
try {
assertNull(registry.getJob("foo"));
fail("Expected NoSuchJobConfigurationException");
}
catch (NoSuchJobException e) {
// expected
assertTrue(e.getMessage().indexOf("foo")>=0);
}
}
/**
* Test method for {@link org.springframework.batch.execution.configuration.MapJobRegistry#getJob(java.lang.String)}.
*/
public void testReplaceDuplicateConfiguration() throws Exception {
registry.register(new ReferenceJobFactory(new JobSupport("foo")));
try {
registry.register(new ReferenceJobFactory(new JobSupport("foo")));
fail("Expected DuplicateJobConfigurationException");
} catch (DuplicateJobException e) {
// unexpected: even if the job is different we want a DuplicateJobException
assertTrue(e.getMessage().indexOf("foo")>=0);
}
}
/**
* Test method for {@link org.springframework.batch.execution.configuration.MapJobRegistry#getJob(java.lang.String)}.
*/
public void testRealDuplicateConfiguration() throws Exception {
JobFactory jobFactory = new ReferenceJobFactory(new JobSupport("foo"));
registry.register(jobFactory);
try {
registry.register(jobFactory);
fail("Unexpected DuplicateJobConfigurationException");
} catch (DuplicateJobException e) {
// expected
assertTrue(e.getMessage().indexOf("foo")>=0);
}
}
/**
* Test method for {@link org.springframework.batch.execution.configuration.MapJobRegistry#getJobNames()}.
* @throws Exception
*/
public void testGetJobConfigurations() throws Exception {
JobFactory jobFactory = new ReferenceJobFactory(new JobSupport("foo"));
registry.register(jobFactory);
registry.register(new ReferenceJobFactory(new JobSupport("bar")));
Collection configurations = registry.getJobNames();
assertEquals(2, configurations.size());
assertTrue(configurations.contains(jobFactory.getJobName()));
}
}

View File

@@ -1,115 +1,115 @@
/*
* 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.job;
import java.util.Collections;
import org.springframework.batch.execution.step.StepSupport;
import junit.framework.TestCase;
/**
* @author Dave Syer
*
*/
public class AbstractJobTests extends TestCase {
JobSupport job = new JobSupport("job");
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#JobConfiguration()}.
*/
public void testJobConfiguration() {
job = new JobSupport();
assertNull(job.getName());
}
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#setBeanName(java.lang.String)}.
*/
public void testSetBeanName() {
job.setBeanName("foo");
assertEquals("job", job.getName());
}
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#setBeanName(java.lang.String)}.
*/
public void testSetBeanNameWithNullName() {
job = new JobSupport(null);
assertEquals(null, job.getName());
job.setBeanName("foo");
assertEquals("foo", job.getName());
}
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#setStepNames(java.util.List)}.
*/
public void testSetSteps() {
job.setSteps(Collections.singletonList(new StepSupport("step")));
assertEquals(1, job.getSteps().size());
}
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#addStepName(org.springframework.batch.core.configuration.StepConfiguration)}.
*/
public void testAddStep() {
job.addStep(new StepSupport("step"));
assertEquals(1, job.getSteps().size());
}
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#setStartLimit(int)}.
*/
public void testSetStartLimit() {
assertEquals(Integer.MAX_VALUE, job.getStartLimit());
job.setStartLimit(10);
assertEquals(10, job.getStartLimit());
}
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#setRestartable(boolean)}.
*/
public void testSetRestartable() {
assertFalse(job.isRestartable());
job.setRestartable(true);
assertTrue(job.isRestartable());
}
public void testToString() throws Exception {
String value = job.toString();
assertTrue("Should contain name: "+value, value.indexOf("name=")>=0);
}
public void testRunNotSupported() throws Exception {
try {
job.execute(null);
} catch (UnsupportedOperationException e) {
// expected
String message = e.getMessage();
assertTrue("Message should contain JobSupport: "+message, message.contains("JobSupport"));
}
}
}
/*
* 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.job;
import java.util.Collections;
import org.springframework.batch.execution.step.StepSupport;
import junit.framework.TestCase;
/**
* @author Dave Syer
*
*/
public class AbstractJobTests extends TestCase {
JobSupport job = new JobSupport("job");
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#JobConfiguration()}.
*/
public void testJobConfiguration() {
job = new JobSupport();
assertNull(job.getName());
}
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#setBeanName(java.lang.String)}.
*/
public void testSetBeanName() {
job.setBeanName("foo");
assertEquals("job", job.getName());
}
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#setBeanName(java.lang.String)}.
*/
public void testSetBeanNameWithNullName() {
job = new JobSupport(null);
assertEquals(null, job.getName());
job.setBeanName("foo");
assertEquals("foo", job.getName());
}
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#setStepNames(java.util.List)}.
*/
public void testSetSteps() {
job.setSteps(Collections.singletonList(new StepSupport("step")));
assertEquals(1, job.getSteps().size());
}
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#addStepName(org.springframework.batch.core.configuration.StepConfiguration)}.
*/
public void testAddStep() {
job.addStep(new StepSupport("step"));
assertEquals(1, job.getSteps().size());
}
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#setStartLimit(int)}.
*/
public void testSetStartLimit() {
assertEquals(Integer.MAX_VALUE, job.getStartLimit());
job.setStartLimit(10);
assertEquals(10, job.getStartLimit());
}
/**
* Test method for
* {@link org.springframework.batch.execution.job.AbstractJob#setRestartable(boolean)}.
*/
public void testSetRestartable() {
assertFalse(job.isRestartable());
job.setRestartable(true);
assertTrue(job.isRestartable());
}
public void testToString() throws Exception {
String value = job.toString();
assertTrue("Should contain name: "+value, value.indexOf("name=")>=0);
}
public void testRunNotSupported() throws Exception {
try {
job.execute(null);
} catch (UnsupportedOperationException e) {
// expected
String message = e.getMessage();
assertTrue("Message should contain JobSupport: "+message, message.contains("JobSupport"));
}
}
}

View File

@@ -1,90 +1,90 @@
/*
* 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.launch.support;
import java.util.HashMap;
import java.util.Map;
import junit.framework.TestCase;
import org.springframework.batch.execution.launch.support.ExitCodeMapper;
import org.springframework.batch.execution.launch.support.SimpleJvmExitCodeMapper;
import org.springframework.batch.repeat.ExitStatus;
public class SimpleJvmExitCodeMapperTests extends TestCase {
private SimpleJvmExitCodeMapper ecm;
private SimpleJvmExitCodeMapper ecm2;
protected void setUp() throws Exception {
ecm = new SimpleJvmExitCodeMapper();
Map ecmMap = new HashMap();
ecmMap.put("MY_CUSTOM_CODE", new Integer(3));
ecm.setMapping(ecmMap);
ecm2 = new SimpleJvmExitCodeMapper();
Map ecm2Map = new HashMap();
ecm2Map.put(ExitStatus.FINISHED.getExitCode(), new Integer(-1));
ecm2Map.put(ExitStatus.FAILED.getExitCode(), new Integer(-2));
ecm2Map.put(ExitCodeMapper.JOB_NOT_PROVIDED, new Integer(-3));
ecm2Map.put(ExitCodeMapper.NO_SUCH_JOB, new Integer(-3));
ecm2.setMapping(ecm2Map);
}
protected void tearDown() throws Exception {
super.tearDown();
}
public void testGetExitCodeWithpPredefinedCodes() {
assertEquals(
ecm.getExitCode(ExitStatus.FINISHED.getExitCode()),
ExitCodeMapper.JVM_EXITCODE_COMPLETED);
assertEquals(
ecm.getExitCode(ExitStatus.FAILED.getExitCode()),
ExitCodeMapper.JVM_EXITCODE_GENERIC_ERROR);
assertEquals(
ecm.getExitCode(ExitCodeMapper.JOB_NOT_PROVIDED),
ExitCodeMapper.JVM_EXITCODE_JOB_ERROR);
assertEquals(
ecm.getExitCode(ExitCodeMapper.NO_SUCH_JOB),
ExitCodeMapper.JVM_EXITCODE_JOB_ERROR);
}
public void testGetExitCodeWithPredefinedCodesOverridden() {
System.out.println(ecm2.getExitCode(ExitStatus.FINISHED.getExitCode()));
assertEquals(
ecm2.getExitCode(ExitStatus.FINISHED.getExitCode()), -1);
assertEquals(
ecm2.getExitCode(ExitStatus.FAILED.getExitCode()), -2);
assertEquals(
ecm2.getExitCode(ExitCodeMapper.JOB_NOT_PROVIDED), -3);
assertEquals(
ecm2.getExitCode(ExitCodeMapper.NO_SUCH_JOB), -3);
}
public void testGetExitCodeWithCustomCode() {
assertEquals(ecm.getExitCode("MY_CUSTOM_CODE"),3);
}
public void testGetExitCodeWithDefaultCode() {
assertEquals(
ecm.getExitCode("UNDEFINED_CUSTOM_CODE"),
ExitCodeMapper.JVM_EXITCODE_GENERIC_ERROR);
}
}
/*
* 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.launch.support;
import java.util.HashMap;
import java.util.Map;
import junit.framework.TestCase;
import org.springframework.batch.execution.launch.support.ExitCodeMapper;
import org.springframework.batch.execution.launch.support.SimpleJvmExitCodeMapper;
import org.springframework.batch.repeat.ExitStatus;
public class SimpleJvmExitCodeMapperTests extends TestCase {
private SimpleJvmExitCodeMapper ecm;
private SimpleJvmExitCodeMapper ecm2;
protected void setUp() throws Exception {
ecm = new SimpleJvmExitCodeMapper();
Map ecmMap = new HashMap();
ecmMap.put("MY_CUSTOM_CODE", new Integer(3));
ecm.setMapping(ecmMap);
ecm2 = new SimpleJvmExitCodeMapper();
Map ecm2Map = new HashMap();
ecm2Map.put(ExitStatus.FINISHED.getExitCode(), new Integer(-1));
ecm2Map.put(ExitStatus.FAILED.getExitCode(), new Integer(-2));
ecm2Map.put(ExitCodeMapper.JOB_NOT_PROVIDED, new Integer(-3));
ecm2Map.put(ExitCodeMapper.NO_SUCH_JOB, new Integer(-3));
ecm2.setMapping(ecm2Map);
}
protected void tearDown() throws Exception {
super.tearDown();
}
public void testGetExitCodeWithpPredefinedCodes() {
assertEquals(
ecm.getExitCode(ExitStatus.FINISHED.getExitCode()),
ExitCodeMapper.JVM_EXITCODE_COMPLETED);
assertEquals(
ecm.getExitCode(ExitStatus.FAILED.getExitCode()),
ExitCodeMapper.JVM_EXITCODE_GENERIC_ERROR);
assertEquals(
ecm.getExitCode(ExitCodeMapper.JOB_NOT_PROVIDED),
ExitCodeMapper.JVM_EXITCODE_JOB_ERROR);
assertEquals(
ecm.getExitCode(ExitCodeMapper.NO_SUCH_JOB),
ExitCodeMapper.JVM_EXITCODE_JOB_ERROR);
}
public void testGetExitCodeWithPredefinedCodesOverridden() {
System.out.println(ecm2.getExitCode(ExitStatus.FINISHED.getExitCode()));
assertEquals(
ecm2.getExitCode(ExitStatus.FINISHED.getExitCode()), -1);
assertEquals(
ecm2.getExitCode(ExitStatus.FAILED.getExitCode()), -2);
assertEquals(
ecm2.getExitCode(ExitCodeMapper.JOB_NOT_PROVIDED), -3);
assertEquals(
ecm2.getExitCode(ExitCodeMapper.NO_SUCH_JOB), -3);
}
public void testGetExitCodeWithCustomCode() {
assertEquals(ecm.getExitCode("MY_CUSTOM_CODE"),3);
}
public void testGetExitCodeWithDefaultCode() {
assertEquals(
ecm.getExitCode("UNDEFINED_CUSTOM_CODE"),
ExitCodeMapper.JVM_EXITCODE_GENERIC_ERROR);
}
}

View File

@@ -1,54 +1,54 @@
package org.springframework.batch.execution.launch.support;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.execution.launch.JobLauncher;
/**
* Mock Job Launcher. Normally, something like EasyMock would
* be used to mock an interface, however, because of the nature
* of launching a batch job from the command line, the mocked
* class cannot be injected.
*
* @author Lucas Ward
*
*/
public class StubJobLauncher implements JobLauncher {
public static final int RUN_NO_ARGS = 0;
public static final int RUN_JOB_NAME = 1;
public static final int RUN_JOB_IDENTIFIER =2 ;
private int lastRunCalled = RUN_NO_ARGS;
private JobExecution returnValue = null;
private boolean isRunning = false;
public boolean isRunning() {
return isRunning;
}
public JobExecution run(Job job, JobParameters jobParameters)
throws JobExecutionAlreadyRunningException {
lastRunCalled = RUN_JOB_IDENTIFIER;
return returnValue;
}
public void stop() {
}
public void setReturnValue(JobExecution returnValue){
this.returnValue = returnValue;
}
public void setIsRunning(boolean isRunning){
this.isRunning = isRunning;
}
public int getLastRunCalled(){
return lastRunCalled;
}
}
package org.springframework.batch.execution.launch.support;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.execution.launch.JobLauncher;
/**
* Mock Job Launcher. Normally, something like EasyMock would
* be used to mock an interface, however, because of the nature
* of launching a batch job from the command line, the mocked
* class cannot be injected.
*
* @author Lucas Ward
*
*/
public class StubJobLauncher implements JobLauncher {
public static final int RUN_NO_ARGS = 0;
public static final int RUN_JOB_NAME = 1;
public static final int RUN_JOB_IDENTIFIER =2 ;
private int lastRunCalled = RUN_NO_ARGS;
private JobExecution returnValue = null;
private boolean isRunning = false;
public boolean isRunning() {
return isRunning;
}
public JobExecution run(Job job, JobParameters jobParameters)
throws JobExecutionAlreadyRunningException {
lastRunCalled = RUN_JOB_IDENTIFIER;
return returnValue;
}
public void stop() {
}
public void setReturnValue(JobExecution returnValue){
this.returnValue = returnValue;
}
public void setIsRunning(boolean isRunning){
this.isRunning = isRunning;
}
public int getLastRunCalled(){
return lastRunCalled;
}
}

View File

@@ -1,224 +1,224 @@
/*
* 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.repository.dao;
import java.util.Date;
import org.springframework.batch.core.domain.BatchStatus;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.core.runtime.ExitStatusExceptionClassifier;
import org.springframework.batch.execution.job.JobSupport;
import org.springframework.batch.execution.step.StepSupport;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
import org.springframework.util.ClassUtils;
/**
* Tests for step persistence (StepInstanceDao and StepExecutionDao). Because it is very reasonable to assume that there is a
* foreign key constraint on the JobId of a step, the JobDao is used to create
* jobs, to have an id for creating steps.
*
* @author Lucas Ward
* @author Dave Syer
*/
public abstract class AbstractStepDaoTests extends AbstractTransactionalDataSourceSpringContextTests {
protected JobInstanceDao jobInstanceDao;
protected StepExecutionDao stepExecutionDao;
protected JobExecutionDao jobExecutionDao;
protected JobInstance jobInstance;
protected Step step1;
protected Step step2;
protected StepExecution stepExecution;
protected JobExecution jobExecution;
protected JobParameters jobParameters = new JobParameters();
protected ExecutionContext executionContext;
public void setJobInstanceDao(JobInstanceDao jobInstanceDao) {
this.jobInstanceDao = jobInstanceDao;
}
public void setStepExecutionDao(StepExecutionDao stepExecutionDao) {
this.stepExecutionDao = stepExecutionDao;
}
public void setJobExecutionDao(JobExecutionDao jobExecutionDao) {
this.jobExecutionDao = jobExecutionDao;
}
/*
* (non-Javadoc)
* @see org.springframework.test.AbstractSingleSpringContextTests#getConfigLocations()
*/
protected String[] getConfigLocations() {
return new String[] { ClassUtils.addResourcePathToPackagePath(getClass(), "sql-dao-test.xml") };
}
/*
* (non-Javadoc)
* @see org.springframework.test.AbstractTransactionalSpringContextTests#onSetUpInTransaction()
*/
protected void onSetUpInTransaction() throws Exception {
Job job = new JobSupport("TestJob");
jobInstance = jobInstanceDao.createJobInstance(job, jobParameters);
step1 = new StepSupport("TestStep1");
step2 = new StepSupport("TestStep2");
jobExecution = new JobExecution(jobInstance);
jobExecutionDao.saveJobExecution(jobExecution);
stepExecution = new StepExecution(step1, jobExecution, new Long(1));
stepExecution.setStatus(BatchStatus.STARTED);
stepExecution.setStartTime(new Date(System.currentTimeMillis()));
stepExecutionDao.saveStepExecution(stepExecution);
executionContext = new ExecutionContext();
executionContext.putString("1", "testString1");
executionContext.putString("2", "testString2");
executionContext.putLong("3", 3);
executionContext.putDouble("4", 4.4);
}
public void testVersionIsNotNullForStepExecution() throws Exception {
int version = jdbcTemplate.queryForInt("select version from BATCH_STEP_EXECUTION where STEP_EXECUTION_ID="
+ stepExecution.getId());
assertEquals(0, version);
}
public void testUpdateStepWithExecutionContext() {
stepExecution.setExecutionContext(executionContext);
stepExecutionDao.saveOrUpdateExecutionContext(stepExecution);
ExecutionContext tempAttributes = stepExecutionDao.findExecutionContext(stepExecution);
assertEquals(executionContext, tempAttributes);
}
public void testSaveStepExecution() {
StepExecution execution = new StepExecution(step2, jobExecution, null);
execution.setStatus(BatchStatus.STARTED);
execution.setStartTime(new Date(System.currentTimeMillis()));
execution.setExitStatus(new ExitStatus(false, ExitStatusExceptionClassifier.FATAL_EXCEPTION,
"java.lang.Exception"));
stepExecutionDao.saveStepExecution(execution);
StepExecution retrievedExecution = stepExecutionDao.getStepExecution(jobExecution, step2);
assertNotNull(retrievedExecution);
assertEquals(execution, retrievedExecution);
assertEquals(execution.getExitStatus(), retrievedExecution.getExitStatus());
}
public void testSaveStepExecutionAndExecutionContext() {
StepExecution execution = new StepExecution(step2, jobExecution, null);
execution.setStatus(BatchStatus.STARTED);
execution.setStartTime(new Date(System.currentTimeMillis()));
execution.setExecutionContext(executionContext);
execution.setExitStatus(new ExitStatus(false, ExitStatusExceptionClassifier.FATAL_EXCEPTION,
"java.lang.Exception"));
stepExecutionDao.saveStepExecution(execution);
stepExecutionDao.saveOrUpdateExecutionContext(execution);
StepExecution retrievedExecution = stepExecutionDao.getStepExecution(jobExecution, step2);
assertNotNull(retrievedExecution);
assertEquals(execution, retrievedExecution);
assertEquals(execution.getExecutionContext().getString("1"), retrievedExecution.getExecutionContext().getString("1"));
assertEquals(execution.getExecutionContext().getLong("3"), retrievedExecution.getExecutionContext().getLong("3"));
assertEquals(execution.getExitStatus(), retrievedExecution.getExitStatus());
}
public void testUpdateStepExecution() {
stepExecution.setStatus(BatchStatus.COMPLETED);
stepExecution.setEndTime(new Date(System.currentTimeMillis()));
stepExecution.setCommitCount(5);
stepExecution.setTaskCount(5);
stepExecution.setExecutionContext(new ExecutionContext());
stepExecution.setExitStatus(new ExitStatus(false, ExitStatusExceptionClassifier.FATAL_EXCEPTION,
"java.lang.Exception"));
stepExecutionDao.updateStepExecution(stepExecution);
StepExecution retrievedExecution = stepExecutionDao.getStepExecution(jobExecution, step1);
assertNotNull(retrievedExecution);
assertEquals(stepExecution, retrievedExecution);
assertEquals(stepExecution.getExitStatus(), retrievedExecution.getExitStatus());
}
public void testUpdateStepExecutionWithNullId() {
StepExecution stepExecution = new StepExecution(new StepSupport("testStep"), null, null);
try {
stepExecutionDao.updateStepExecution(stepExecution);
fail("Expected IllegalArgumentException");
}
catch (IllegalArgumentException ex) {
// expected
}
}
public void testUpdateStepExecutionVersion() throws Exception {
int before = stepExecution.getVersion().intValue();
stepExecutionDao.updateStepExecution(stepExecution);
int after = stepExecution.getVersion().intValue();
assertEquals("StepExecution version not updated", before + 1, after);
}
public void testUpdateStepExecutionOptimisticLocking() throws Exception {
stepExecution.incrementVersion(); // not really allowed outside dao
// code
try {
stepExecutionDao.updateStepExecution(stepExecution);
fail("Expected OptimisticLockingFailureException");
}
catch (OptimisticLockingFailureException e) {
// expected
assertTrue("Exception message should contain step execution id: " + e.getMessage(), e.getMessage().indexOf(
"" + stepExecution.getId()) >= 0);
assertTrue("Exception message should contain step execution version: " + e.getMessage(), e.getMessage()
.indexOf("" + stepExecution.getVersion()) >= 0);
}
}
public void testSaveExecutionContext(){
stepExecution.setExecutionContext(executionContext);
stepExecutionDao.saveOrUpdateExecutionContext(stepExecution);
ExecutionContext attributes = stepExecutionDao.findExecutionContext(stepExecution);
assertEquals(executionContext, attributes);
executionContext.putString("newString", "newString");
executionContext.putLong("newLong", 1);
executionContext.putDouble("newDouble", 2.5);
executionContext.put("newSerializable", "serializableValue");
stepExecutionDao.saveOrUpdateExecutionContext(stepExecution);
attributes = stepExecutionDao.findExecutionContext(stepExecution);
assertEquals(executionContext, attributes);
}
public void testGetStepExecution() {
assertEquals(stepExecution, stepExecutionDao.getStepExecution(jobExecution, step1));
}
}
/*
* 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.repository.dao;
import java.util.Date;
import org.springframework.batch.core.domain.BatchStatus;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.core.runtime.ExitStatusExceptionClassifier;
import org.springframework.batch.execution.job.JobSupport;
import org.springframework.batch.execution.step.StepSupport;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.ExitStatus;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
import org.springframework.util.ClassUtils;
/**
* Tests for step persistence (StepInstanceDao and StepExecutionDao). Because it is very reasonable to assume that there is a
* foreign key constraint on the JobId of a step, the JobDao is used to create
* jobs, to have an id for creating steps.
*
* @author Lucas Ward
* @author Dave Syer
*/
public abstract class AbstractStepDaoTests extends AbstractTransactionalDataSourceSpringContextTests {
protected JobInstanceDao jobInstanceDao;
protected StepExecutionDao stepExecutionDao;
protected JobExecutionDao jobExecutionDao;
protected JobInstance jobInstance;
protected Step step1;
protected Step step2;
protected StepExecution stepExecution;
protected JobExecution jobExecution;
protected JobParameters jobParameters = new JobParameters();
protected ExecutionContext executionContext;
public void setJobInstanceDao(JobInstanceDao jobInstanceDao) {
this.jobInstanceDao = jobInstanceDao;
}
public void setStepExecutionDao(StepExecutionDao stepExecutionDao) {
this.stepExecutionDao = stepExecutionDao;
}
public void setJobExecutionDao(JobExecutionDao jobExecutionDao) {
this.jobExecutionDao = jobExecutionDao;
}
/*
* (non-Javadoc)
* @see org.springframework.test.AbstractSingleSpringContextTests#getConfigLocations()
*/
protected String[] getConfigLocations() {
return new String[] { ClassUtils.addResourcePathToPackagePath(getClass(), "sql-dao-test.xml") };
}
/*
* (non-Javadoc)
* @see org.springframework.test.AbstractTransactionalSpringContextTests#onSetUpInTransaction()
*/
protected void onSetUpInTransaction() throws Exception {
Job job = new JobSupport("TestJob");
jobInstance = jobInstanceDao.createJobInstance(job, jobParameters);
step1 = new StepSupport("TestStep1");
step2 = new StepSupport("TestStep2");
jobExecution = new JobExecution(jobInstance);
jobExecutionDao.saveJobExecution(jobExecution);
stepExecution = new StepExecution(step1, jobExecution, new Long(1));
stepExecution.setStatus(BatchStatus.STARTED);
stepExecution.setStartTime(new Date(System.currentTimeMillis()));
stepExecutionDao.saveStepExecution(stepExecution);
executionContext = new ExecutionContext();
executionContext.putString("1", "testString1");
executionContext.putString("2", "testString2");
executionContext.putLong("3", 3);
executionContext.putDouble("4", 4.4);
}
public void testVersionIsNotNullForStepExecution() throws Exception {
int version = jdbcTemplate.queryForInt("select version from BATCH_STEP_EXECUTION where STEP_EXECUTION_ID="
+ stepExecution.getId());
assertEquals(0, version);
}
public void testUpdateStepWithExecutionContext() {
stepExecution.setExecutionContext(executionContext);
stepExecutionDao.saveOrUpdateExecutionContext(stepExecution);
ExecutionContext tempAttributes = stepExecutionDao.findExecutionContext(stepExecution);
assertEquals(executionContext, tempAttributes);
}
public void testSaveStepExecution() {
StepExecution execution = new StepExecution(step2, jobExecution, null);
execution.setStatus(BatchStatus.STARTED);
execution.setStartTime(new Date(System.currentTimeMillis()));
execution.setExitStatus(new ExitStatus(false, ExitStatusExceptionClassifier.FATAL_EXCEPTION,
"java.lang.Exception"));
stepExecutionDao.saveStepExecution(execution);
StepExecution retrievedExecution = stepExecutionDao.getStepExecution(jobExecution, step2);
assertNotNull(retrievedExecution);
assertEquals(execution, retrievedExecution);
assertEquals(execution.getExitStatus(), retrievedExecution.getExitStatus());
}
public void testSaveStepExecutionAndExecutionContext() {
StepExecution execution = new StepExecution(step2, jobExecution, null);
execution.setStatus(BatchStatus.STARTED);
execution.setStartTime(new Date(System.currentTimeMillis()));
execution.setExecutionContext(executionContext);
execution.setExitStatus(new ExitStatus(false, ExitStatusExceptionClassifier.FATAL_EXCEPTION,
"java.lang.Exception"));
stepExecutionDao.saveStepExecution(execution);
stepExecutionDao.saveOrUpdateExecutionContext(execution);
StepExecution retrievedExecution = stepExecutionDao.getStepExecution(jobExecution, step2);
assertNotNull(retrievedExecution);
assertEquals(execution, retrievedExecution);
assertEquals(execution.getExecutionContext().getString("1"), retrievedExecution.getExecutionContext().getString("1"));
assertEquals(execution.getExecutionContext().getLong("3"), retrievedExecution.getExecutionContext().getLong("3"));
assertEquals(execution.getExitStatus(), retrievedExecution.getExitStatus());
}
public void testUpdateStepExecution() {
stepExecution.setStatus(BatchStatus.COMPLETED);
stepExecution.setEndTime(new Date(System.currentTimeMillis()));
stepExecution.setCommitCount(5);
stepExecution.setTaskCount(5);
stepExecution.setExecutionContext(new ExecutionContext());
stepExecution.setExitStatus(new ExitStatus(false, ExitStatusExceptionClassifier.FATAL_EXCEPTION,
"java.lang.Exception"));
stepExecutionDao.updateStepExecution(stepExecution);
StepExecution retrievedExecution = stepExecutionDao.getStepExecution(jobExecution, step1);
assertNotNull(retrievedExecution);
assertEquals(stepExecution, retrievedExecution);
assertEquals(stepExecution.getExitStatus(), retrievedExecution.getExitStatus());
}
public void testUpdateStepExecutionWithNullId() {
StepExecution stepExecution = new StepExecution(new StepSupport("testStep"), null, null);
try {
stepExecutionDao.updateStepExecution(stepExecution);
fail("Expected IllegalArgumentException");
}
catch (IllegalArgumentException ex) {
// expected
}
}
public void testUpdateStepExecutionVersion() throws Exception {
int before = stepExecution.getVersion().intValue();
stepExecutionDao.updateStepExecution(stepExecution);
int after = stepExecution.getVersion().intValue();
assertEquals("StepExecution version not updated", before + 1, after);
}
public void testUpdateStepExecutionOptimisticLocking() throws Exception {
stepExecution.incrementVersion(); // not really allowed outside dao
// code
try {
stepExecutionDao.updateStepExecution(stepExecution);
fail("Expected OptimisticLockingFailureException");
}
catch (OptimisticLockingFailureException e) {
// expected
assertTrue("Exception message should contain step execution id: " + e.getMessage(), e.getMessage().indexOf(
"" + stepExecution.getId()) >= 0);
assertTrue("Exception message should contain step execution version: " + e.getMessage(), e.getMessage()
.indexOf("" + stepExecution.getVersion()) >= 0);
}
}
public void testSaveExecutionContext(){
stepExecution.setExecutionContext(executionContext);
stepExecutionDao.saveOrUpdateExecutionContext(stepExecution);
ExecutionContext attributes = stepExecutionDao.findExecutionContext(stepExecution);
assertEquals(executionContext, attributes);
executionContext.putString("newString", "newString");
executionContext.putLong("newLong", 1);
executionContext.putDouble("newDouble", 2.5);
executionContext.put("newSerializable", "serializableValue");
stepExecutionDao.saveOrUpdateExecutionContext(stepExecution);
attributes = stepExecutionDao.findExecutionContext(stepExecution);
assertEquals(executionContext, attributes);
}
public void testGetStepExecution() {
assertEquals(stepExecution, stepExecutionDao.getStepExecution(jobExecution, step1));
}
}

View File

@@ -1,82 +1,82 @@
/*
* 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.repository.dao;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.execution.job.JobSupport;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
/**
* @author Dave Syer
*
*/
public class JdbcJobDaoQueryTests extends TestCase {
JdbcJobExecutionDao jobExecutionDao;
List list = new ArrayList();
/*
* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
jobExecutionDao = new JdbcJobExecutionDao();
jobExecutionDao.setJobExecutionIncrementer(new DataFieldMaxValueIncrementer() {
public int nextIntValue() throws DataAccessException {
return 0;
}
public long nextLongValue() throws DataAccessException {
return 0;
}
public String nextStringValue() throws DataAccessException {
return "bar";
}
});
}
public void testTablePrefix() throws Exception {
jobExecutionDao.setTablePrefix("FOO_");
jobExecutionDao.setJdbcTemplate(new JdbcTemplate() {
public int update(String sql, Object[] args, int[] argTypes) throws DataAccessException {
list.add(sql);
return 1;
}
});
JobExecution jobExecution = new JobExecution(new JobInstance(new Long(11), new JobParameters(), new JobSupport(
"testJob")));
jobExecutionDao.saveJobExecution(jobExecution);
assertEquals(1, list.size());
String query = (String) list.get(0);
assertTrue("Query did not contain FOO_:" + query, query.indexOf("FOO_") >= 0);
}
}
/*
* 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.repository.dao;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.execution.job.JobSupport;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
/**
* @author Dave Syer
*
*/
public class JdbcJobDaoQueryTests extends TestCase {
JdbcJobExecutionDao jobExecutionDao;
List list = new ArrayList();
/*
* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
protected void setUp() throws Exception {
jobExecutionDao = new JdbcJobExecutionDao();
jobExecutionDao.setJobExecutionIncrementer(new DataFieldMaxValueIncrementer() {
public int nextIntValue() throws DataAccessException {
return 0;
}
public long nextLongValue() throws DataAccessException {
return 0;
}
public String nextStringValue() throws DataAccessException {
return "bar";
}
});
}
public void testTablePrefix() throws Exception {
jobExecutionDao.setTablePrefix("FOO_");
jobExecutionDao.setJdbcTemplate(new JdbcTemplate() {
public int update(String sql, Object[] args, int[] argTypes) throws DataAccessException {
list.add(sql);
return 1;
}
});
JobExecution jobExecution = new JobExecution(new JobInstance(new Long(11), new JobParameters(), new JobSupport(
"testJob")));
jobExecutionDao.saveJobExecution(jobExecution);
assertEquals(1, list.size());
String query = (String) list.get(0);
assertTrue("Query did not contain FOO_:" + query, query.indexOf("FOO_") >= 0);
}
}

View File

@@ -1,32 +1,32 @@
package org.springframework.batch.execution.repository.dao;
import java.util.List;
import java.util.Map;
import org.springframework.batch.repeat.ExitStatus;
public class JdbcJobDaoTests extends AbstractJobDaoTests {
public static final String LONG_STRING = "A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String ";
protected void onSetUpBeforeTransaction() throws Exception {
((JdbcJobInstanceDao) jobInstanceDao).setTablePrefix(AbstractJdbcBatchMetadataDao.DEFAULT_TABLE_PREFIX);
((JdbcJobExecutionDao) jobExecutionDao).setTablePrefix(AbstractJdbcBatchMetadataDao.DEFAULT_TABLE_PREFIX);
}
public void testUpdateJobExecutionWithLongExitCode() {
assertTrue(LONG_STRING.length() > 250);
jobExecution.setExitStatus(ExitStatus.FINISHED
.addExitDescription(LONG_STRING));
jobExecutionDao.updateJobExecution(jobExecution);
List executions = jdbcTemplate.queryForList(
"SELECT * FROM BATCH_JOB_EXECUTION where JOB_INSTANCE_ID=?",
new Object[] { jobInstance.getId() });
assertEquals(1, executions.size());
assertEquals(LONG_STRING.substring(0, 250), ((Map) executions.get(0))
.get("EXIT_MESSAGE"));
}
}
package org.springframework.batch.execution.repository.dao;
import java.util.List;
import java.util.Map;
import org.springframework.batch.repeat.ExitStatus;
public class JdbcJobDaoTests extends AbstractJobDaoTests {
public static final String LONG_STRING = "A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String A very long String ";
protected void onSetUpBeforeTransaction() throws Exception {
((JdbcJobInstanceDao) jobInstanceDao).setTablePrefix(AbstractJdbcBatchMetadataDao.DEFAULT_TABLE_PREFIX);
((JdbcJobExecutionDao) jobExecutionDao).setTablePrefix(AbstractJdbcBatchMetadataDao.DEFAULT_TABLE_PREFIX);
}
public void testUpdateJobExecutionWithLongExitCode() {
assertTrue(LONG_STRING.length() > 250);
jobExecution.setExitStatus(ExitStatus.FINISHED
.addExitDescription(LONG_STRING));
jobExecutionDao.updateJobExecution(jobExecution);
List executions = jdbcTemplate.queryForList(
"SELECT * FROM BATCH_JOB_EXECUTION where JOB_INSTANCE_ID=?",
new Object[] { jobInstance.getId() });
assertEquals(1, executions.size());
assertEquals(LONG_STRING.substring(0, 250), ((Map) executions.get(0))
.get("EXIT_MESSAGE"));
}
}

View File

@@ -1,111 +1,111 @@
package org.springframework.batch.execution.repository.dao;
import java.util.List;
import junit.framework.TestCase;
import org.easymock.MockControl;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.execution.job.JobSupport;
import org.springframework.batch.execution.step.StepSupport;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
/**
* Unit Test of SqlStepDao that only tests prefix matching. A
* separate test is needed because all other tests hit hsql,
* while this test needs to mock JdbcTemplate to analyze the
* Sql passed in.
*
* @author Lucas Ward
*
*/
public class JdbcStepDaoPrefixTests extends TestCase {
private JdbcStepExecutionDao stepExecutionDao;
MockJdbcTemplate jdbcTemplate = new MockJdbcTemplate();
JobInstance job = new JobInstance(new Long(1), new JobParameters(), new JobSupport("testJob"));
Step step = new StepSupport("foo");
StepExecution stepExecution = new StepExecution(step, new JobExecution(job), null);
MockControl stepExecutionIncrementerControl = MockControl.createControl(DataFieldMaxValueIncrementer.class);
DataFieldMaxValueIncrementer stepExecutionIncrementer;
MockControl stepIncrementerControl = MockControl.createControl(DataFieldMaxValueIncrementer.class);
DataFieldMaxValueIncrementer stepIncrementer;
protected void setUp() throws Exception {
super.setUp();
stepExecutionDao = new JdbcStepExecutionDao();
stepExecutionIncrementer = (DataFieldMaxValueIncrementer)stepExecutionIncrementerControl.getMock();
stepIncrementer = (DataFieldMaxValueIncrementer)stepIncrementerControl.getMock();
stepExecutionDao.setJdbcTemplate(jdbcTemplate);
stepExecutionDao.setStepExecutionIncrementer(stepExecutionIncrementer);
stepExecution.setId(new Long(1));
stepExecution.incrementVersion();
}
public void testModifiedUpdateStepExecution(){
stepExecutionDao.setTablePrefix("FOO_");
stepExecutionDao.updateStepExecution(stepExecution);
assertTrue(jdbcTemplate.getSqlStatement().indexOf("FOO_STEP_EXECUTION") != -1);
}
public void testModifiedSaveStepExecution(){
stepExecutionDao.setTablePrefix("FOO_");
stepExecutionIncrementer.nextLongValue();
stepExecutionIncrementerControl.setReturnValue(1);
stepExecutionIncrementerControl.replay();
stepExecutionDao.saveStepExecution(stepExecution);
assertTrue(jdbcTemplate.getSqlStatement().indexOf("FOO_STEP_EXECUTION") != -1);
}
public void testDefaultSaveStepExecution(){
stepExecutionIncrementer.nextLongValue();
stepExecutionIncrementerControl.setReturnValue(1);
stepExecutionIncrementerControl.replay();
stepExecutionDao.saveStepExecution(stepExecution);
assertTrue(jdbcTemplate.getSqlStatement().indexOf("BATCH_STEP_EXECUTION") != -1);
}
public void testDefaultUpdateStepExecution(){
stepExecutionDao.updateStepExecution(stepExecution);
assertTrue(jdbcTemplate.getSqlStatement().indexOf("BATCH_STEP_EXECUTION") != -1);
}
private class MockJdbcTemplate extends JdbcTemplate {
private String sql;
public int update(String sql, Object[] args) throws DataAccessException {
this.sql = sql;
return 1;
}
public int update(String sql, Object[] args, int[] argTypes) throws DataAccessException {
this.sql = sql;
return 1;
}
public List query(String sql, Object[] args, RowMapper rowMapper) throws DataAccessException {
this.sql = sql;
return null;
}
public String getSqlStatement() {
return sql;
}
}
}
package org.springframework.batch.execution.repository.dao;
import java.util.List;
import junit.framework.TestCase;
import org.easymock.MockControl;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.execution.job.JobSupport;
import org.springframework.batch.execution.step.StepSupport;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
/**
* Unit Test of SqlStepDao that only tests prefix matching. A
* separate test is needed because all other tests hit hsql,
* while this test needs to mock JdbcTemplate to analyze the
* Sql passed in.
*
* @author Lucas Ward
*
*/
public class JdbcStepDaoPrefixTests extends TestCase {
private JdbcStepExecutionDao stepExecutionDao;
MockJdbcTemplate jdbcTemplate = new MockJdbcTemplate();
JobInstance job = new JobInstance(new Long(1), new JobParameters(), new JobSupport("testJob"));
Step step = new StepSupport("foo");
StepExecution stepExecution = new StepExecution(step, new JobExecution(job), null);
MockControl stepExecutionIncrementerControl = MockControl.createControl(DataFieldMaxValueIncrementer.class);
DataFieldMaxValueIncrementer stepExecutionIncrementer;
MockControl stepIncrementerControl = MockControl.createControl(DataFieldMaxValueIncrementer.class);
DataFieldMaxValueIncrementer stepIncrementer;
protected void setUp() throws Exception {
super.setUp();
stepExecutionDao = new JdbcStepExecutionDao();
stepExecutionIncrementer = (DataFieldMaxValueIncrementer)stepExecutionIncrementerControl.getMock();
stepIncrementer = (DataFieldMaxValueIncrementer)stepIncrementerControl.getMock();
stepExecutionDao.setJdbcTemplate(jdbcTemplate);
stepExecutionDao.setStepExecutionIncrementer(stepExecutionIncrementer);
stepExecution.setId(new Long(1));
stepExecution.incrementVersion();
}
public void testModifiedUpdateStepExecution(){
stepExecutionDao.setTablePrefix("FOO_");
stepExecutionDao.updateStepExecution(stepExecution);
assertTrue(jdbcTemplate.getSqlStatement().indexOf("FOO_STEP_EXECUTION") != -1);
}
public void testModifiedSaveStepExecution(){
stepExecutionDao.setTablePrefix("FOO_");
stepExecutionIncrementer.nextLongValue();
stepExecutionIncrementerControl.setReturnValue(1);
stepExecutionIncrementerControl.replay();
stepExecutionDao.saveStepExecution(stepExecution);
assertTrue(jdbcTemplate.getSqlStatement().indexOf("FOO_STEP_EXECUTION") != -1);
}
public void testDefaultSaveStepExecution(){
stepExecutionIncrementer.nextLongValue();
stepExecutionIncrementerControl.setReturnValue(1);
stepExecutionIncrementerControl.replay();
stepExecutionDao.saveStepExecution(stepExecution);
assertTrue(jdbcTemplate.getSqlStatement().indexOf("BATCH_STEP_EXECUTION") != -1);
}
public void testDefaultUpdateStepExecution(){
stepExecutionDao.updateStepExecution(stepExecution);
assertTrue(jdbcTemplate.getSqlStatement().indexOf("BATCH_STEP_EXECUTION") != -1);
}
private class MockJdbcTemplate extends JdbcTemplate {
private String sql;
public int update(String sql, Object[] args) throws DataAccessException {
this.sql = sql;
return 1;
}
public int update(String sql, Object[] args, int[] argTypes) throws DataAccessException {
this.sql = sql;
return 1;
}
public List query(String sql, Object[] args, RowMapper rowMapper) throws DataAccessException {
this.sql = sql;
return null;
}
public String getSqlStatement() {
return sql;
}
}
}

View File

@@ -1,41 +1,41 @@
package org.springframework.batch.execution.repository.dao;
import java.util.List;
import java.util.Map;
import org.springframework.batch.repeat.ExitStatus;
public class JdbcStepDaoTests extends AbstractStepDaoTests {
private static final String LONG_STRING = JdbcJobDaoTests.LONG_STRING;
protected void onSetUpBeforeTransaction() throws Exception {
((JdbcStepExecutionDao) stepExecutionDao).setTablePrefix(AbstractJdbcBatchMetadataDao.DEFAULT_TABLE_PREFIX);
}
public void testTablePrefix() throws Exception {
// ((JdbcStepInstanceDao) stepInstanceDao).setTablePrefix("FOO_");
// ((JdbcStepExecutionDao) stepExecutionDao).setTablePrefix("FOO_");
// try {
// testCreateStep();
// fail("Expected DataAccessException");
// } catch (DataAccessException e) {
// // expected
// }
}
public void testUpdateStepExecutionWithLongExitCode() {
assertTrue(LONG_STRING.length()>250);
stepExecution.setExitStatus(ExitStatus.FINISHED.addExitDescription(LONG_STRING));
stepExecutionDao.updateStepExecution(stepExecution);
List executions = jdbcTemplate.queryForList(
"SELECT * FROM BATCH_STEP_EXECUTION where STEP_NAME=?",
new Object[] { step1.getName() });
assertEquals(1, executions.size());
assertEquals(LONG_STRING.substring(0, 250), ((Map) executions.get(0))
.get("EXIT_MESSAGE"));
}
}
package org.springframework.batch.execution.repository.dao;
import java.util.List;
import java.util.Map;
import org.springframework.batch.repeat.ExitStatus;
public class JdbcStepDaoTests extends AbstractStepDaoTests {
private static final String LONG_STRING = JdbcJobDaoTests.LONG_STRING;
protected void onSetUpBeforeTransaction() throws Exception {
((JdbcStepExecutionDao) stepExecutionDao).setTablePrefix(AbstractJdbcBatchMetadataDao.DEFAULT_TABLE_PREFIX);
}
public void testTablePrefix() throws Exception {
// ((JdbcStepInstanceDao) stepInstanceDao).setTablePrefix("FOO_");
// ((JdbcStepExecutionDao) stepExecutionDao).setTablePrefix("FOO_");
// try {
// testCreateStep();
// fail("Expected DataAccessException");
// } catch (DataAccessException e) {
// // expected
// }
}
public void testUpdateStepExecutionWithLongExitCode() {
assertTrue(LONG_STRING.length()>250);
stepExecution.setExitStatus(ExitStatus.FINISHED.addExitDescription(LONG_STRING));
stepExecutionDao.updateStepExecution(stepExecution);
List executions = jdbcTemplate.queryForList(
"SELECT * FROM BATCH_STEP_EXECUTION where STEP_NAME=?",
new Object[] { step1.getName() });
assertEquals(1, executions.size());
assertEquals(LONG_STRING.substring(0, 250), ((Map) executions.get(0))
.get("EXIT_MESSAGE"));
}
}

View File

@@ -1,94 +1,94 @@
package org.springframework.batch.execution.repository.dao;
import java.util.Date;
import java.util.List;
import junit.framework.TestCase;
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.JobParameters;
import org.springframework.batch.execution.job.JobSupport;
public class MapJobExecutionDaoTests extends TestCase {
JobExecutionDao dao = new MapJobExecutionDao();
JobInstance jobInstance = new JobInstance(new Long(1), new JobParameters(), new JobSupport("execTestJob"));
JobExecution execution = new JobExecution(jobInstance);
protected void setUp() throws Exception {
MapJobExecutionDao.clear();
}
/**
* Save and find a job execution.
*/
public void testSaveAndFind() {
dao.saveJobExecution(execution);
List executions = dao.findJobExecutions(jobInstance);
assertTrue(executions.size() == 1);
assertEquals(execution, executions.get(0));
}
/**
* Saving sets id to the entity.
*/
public void testSaveAddsId() {
assertNull(execution.getId());
dao.saveJobExecution(execution);
assertNotNull(execution.getId());
}
/**
* Execution count increases by one with every save for the same job
* instance.
*/
public void testGetExecutionCount() {
JobExecution exec1 = new JobExecution(jobInstance);
JobExecution exec2 = new JobExecution(jobInstance);
dao.saveJobExecution(exec1);
assertEquals(1, dao.getJobExecutionCount(jobInstance));
dao.saveJobExecution(exec2);
assertEquals(2, dao.getJobExecutionCount(jobInstance));
}
/**
* Update and retrieve job execution - check attributes have changed as
* expected.
*/
public void testUpdateExecution() {
execution.setStatus(BatchStatus.STARTED);
dao.saveJobExecution(execution);
execution.setStatus(BatchStatus.COMPLETED);
dao.updateJobExecution(execution);
JobExecution updated = (JobExecution) dao.findJobExecutions(jobInstance).get(0);
assertEquals(execution, updated);
assertEquals(BatchStatus.COMPLETED, updated.getStatus());
}
/**
* Check the execution with most recent start time is returned
*/
public void testGetLastExecution() {
JobExecution exec1 = new JobExecution(jobInstance);
exec1.setStartTime(new Date(0));
JobExecution exec2 = new JobExecution(jobInstance);
exec2.setStartTime(new Date(1));
dao.saveJobExecution(exec1);
dao.saveJobExecution(exec2);
assertEquals(exec2, dao.getLastJobExecution(jobInstance));
}
}
package org.springframework.batch.execution.repository.dao;
import java.util.Date;
import java.util.List;
import junit.framework.TestCase;
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.JobParameters;
import org.springframework.batch.execution.job.JobSupport;
public class MapJobExecutionDaoTests extends TestCase {
JobExecutionDao dao = new MapJobExecutionDao();
JobInstance jobInstance = new JobInstance(new Long(1), new JobParameters(), new JobSupport("execTestJob"));
JobExecution execution = new JobExecution(jobInstance);
protected void setUp() throws Exception {
MapJobExecutionDao.clear();
}
/**
* Save and find a job execution.
*/
public void testSaveAndFind() {
dao.saveJobExecution(execution);
List executions = dao.findJobExecutions(jobInstance);
assertTrue(executions.size() == 1);
assertEquals(execution, executions.get(0));
}
/**
* Saving sets id to the entity.
*/
public void testSaveAddsId() {
assertNull(execution.getId());
dao.saveJobExecution(execution);
assertNotNull(execution.getId());
}
/**
* Execution count increases by one with every save for the same job
* instance.
*/
public void testGetExecutionCount() {
JobExecution exec1 = new JobExecution(jobInstance);
JobExecution exec2 = new JobExecution(jobInstance);
dao.saveJobExecution(exec1);
assertEquals(1, dao.getJobExecutionCount(jobInstance));
dao.saveJobExecution(exec2);
assertEquals(2, dao.getJobExecutionCount(jobInstance));
}
/**
* Update and retrieve job execution - check attributes have changed as
* expected.
*/
public void testUpdateExecution() {
execution.setStatus(BatchStatus.STARTED);
dao.saveJobExecution(execution);
execution.setStatus(BatchStatus.COMPLETED);
dao.updateJobExecution(execution);
JobExecution updated = (JobExecution) dao.findJobExecutions(jobInstance).get(0);
assertEquals(execution, updated);
assertEquals(BatchStatus.COMPLETED, updated.getStatus());
}
/**
* Check the execution with most recent start time is returned
*/
public void testGetLastExecution() {
JobExecution exec1 = new JobExecution(jobInstance);
exec1.setStartTime(new Date(0));
JobExecution exec2 = new JobExecution(jobInstance);
exec2.setStartTime(new Date(1));
dao.saveJobExecution(exec1);
dao.saveJobExecution(exec2);
assertEquals(exec2, dao.getLastJobExecution(jobInstance));
}
}

View File

@@ -1,56 +1,56 @@
package org.springframework.batch.execution.repository.dao;
import junit.framework.TestCase;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.domain.JobParametersBuilder;
import org.springframework.batch.execution.job.JobSupport;
public class MapJobInstanceDaoTests extends TestCase {
private JobInstanceDao dao = new MapJobInstanceDao();
private Job fooJob = new JobSupport("foo");
private JobParameters fooParams = new JobParametersBuilder().addString("fooKey", "fooValue").toJobParameters();
protected void setUp() throws Exception {
MapJobInstanceDao.clear();
}
protected void tearDown() throws Exception {
MapJobInstanceDao.clear();
}
/**
* Create and retrieve a job instance.
*/
public void testCreateAndRetrieve() throws Exception {
JobInstance fooInstance = dao.createJobInstance(fooJob, fooParams);
assertNotNull(fooInstance.getId());
assertEquals(fooJob, fooInstance.getJob());
assertEquals(fooParams, fooInstance.getJobParameters());
JobInstance retrievedInstance = dao.getJobInstance(fooJob, fooParams);
assertEquals(fooInstance, retrievedInstance);
}
/**
* Trying to create instance twice for the same job+parameters causes error
*/
public void testCreateDuplicateInstance() {
dao.createJobInstance(fooJob, fooParams);
try {
dao.createJobInstance(fooJob, fooParams);
fail();
}
catch (IllegalStateException e) {
// expected
}
}
}
package org.springframework.batch.execution.repository.dao;
import junit.framework.TestCase;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.domain.JobParametersBuilder;
import org.springframework.batch.execution.job.JobSupport;
public class MapJobInstanceDaoTests extends TestCase {
private JobInstanceDao dao = new MapJobInstanceDao();
private Job fooJob = new JobSupport("foo");
private JobParameters fooParams = new JobParametersBuilder().addString("fooKey", "fooValue").toJobParameters();
protected void setUp() throws Exception {
MapJobInstanceDao.clear();
}
protected void tearDown() throws Exception {
MapJobInstanceDao.clear();
}
/**
* Create and retrieve a job instance.
*/
public void testCreateAndRetrieve() throws Exception {
JobInstance fooInstance = dao.createJobInstance(fooJob, fooParams);
assertNotNull(fooInstance.getId());
assertEquals(fooJob, fooInstance.getJob());
assertEquals(fooParams, fooInstance.getJobParameters());
JobInstance retrievedInstance = dao.getJobInstance(fooJob, fooParams);
assertEquals(fooInstance, retrievedInstance);
}
/**
* Trying to create instance twice for the same job+parameters causes error
*/
public void testCreateDuplicateInstance() {
dao.createJobInstance(fooJob, fooParams);
try {
dao.createJobInstance(fooJob, fooParams);
fail();
}
catch (IllegalStateException e) {
// expected
}
}
}

View File

@@ -1,32 +1,32 @@
/*
* 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.repository.dao;
import org.springframework.batch.execution.repository.dao.NoSuchObjectException;
import junit.framework.TestCase;
/**
* @author Dave Syer
*
*/
public class NoSuchBatchDomainObjectExceptionTests extends TestCase {
public void testCreateException() throws Exception {
NoSuchObjectException e = new NoSuchObjectException("Foo");
assertEquals("Foo", e.getMessage());
}
}
/*
* 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.repository.dao;
import org.springframework.batch.execution.repository.dao.NoSuchObjectException;
import junit.framework.TestCase;
/**
* @author Dave Syer
*
*/
public class NoSuchBatchDomainObjectExceptionTests extends TestCase {
public void testCreateException() throws Exception {
NoSuchObjectException e = new NoSuchObjectException("Foo");
assertEquals("Foo", e.getMessage());
}
}

View File

@@ -1,117 +1,117 @@
/*
* 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.step;
import org.springframework.batch.core.domain.JobInterruptedException;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.io.exception.InfrastructureException;
import org.springframework.beans.factory.BeanNameAware;
/**
* Basic no-op support implementation for use as base class for {@link Step}. Implements {@link BeanNameAware} so that
* if no name is provided explicitly it will be inferred from the bean definition in Spring configuration.
*
* @author Dave Syer
*
*/
public class StepSupport implements Step, BeanNameAware {
private String name;
private int startLimit = Integer.MAX_VALUE;
private boolean allowStartIfComplete;
/**
* Default constructor for {@link StepSupport}.
*/
public StepSupport() {
super();
}
/**
* @param string
*/
public StepSupport(String string) {
super();
this.name = string;
}
public String getName() {
return this.name;
}
/**
* Set the name property if it is not already set. Because of the order of the callbacks in a Spring container the
* name property will be set first if it is present. Care is needed with bean definition inheritance - if a parent
* bean has a name, then its children need an explicit name as well, otherwise they will not be unique.
*
* @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
*/
public void setBeanName(String name) {
if (this.name == null) {
this.name = name;
}
}
/**
* Set the name property. Always overrides the default value if this object is a Spring bean.
*
* @see #setBeanName(java.lang.String)
*/
public void setName(String name) {
this.name = name;
}
public int getStartLimit() {
return this.startLimit;
}
/**
* Public setter for the startLimit.
*
* @param startLimit the startLimit to set
*/
public void setStartLimit(int startLimit) {
this.startLimit = startLimit;
}
public boolean isAllowStartIfComplete() {
return this.allowStartIfComplete;
}
/**
* Public setter for the shouldAllowStartIfComplete.
*
* @param allowStartIfComplete the shouldAllowStartIfComplete to set
*/
public void setAllowStartIfComplete(boolean allowStartIfComplete) {
this.allowStartIfComplete = allowStartIfComplete;
}
/**
* Not supported but provided so that tests can easily create a step.
*
* @throws UnsupportedOperationException always
*
* @see org.springframework.batch.core.domain.Step#execute(org.springframework.batch.core.domain.StepExecution)
*/
public void execute(StepExecution stepExecution) throws JobInterruptedException, InfrastructureException {
throw new UnsupportedOperationException(
"Cannot process a StepExecution. Use a smarter subclass of StepSupport.");
}
}
/*
* 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.step;
import org.springframework.batch.core.domain.JobInterruptedException;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.io.exception.InfrastructureException;
import org.springframework.beans.factory.BeanNameAware;
/**
* Basic no-op support implementation for use as base class for {@link Step}. Implements {@link BeanNameAware} so that
* if no name is provided explicitly it will be inferred from the bean definition in Spring configuration.
*
* @author Dave Syer
*
*/
public class StepSupport implements Step, BeanNameAware {
private String name;
private int startLimit = Integer.MAX_VALUE;
private boolean allowStartIfComplete;
/**
* Default constructor for {@link StepSupport}.
*/
public StepSupport() {
super();
}
/**
* @param string
*/
public StepSupport(String string) {
super();
this.name = string;
}
public String getName() {
return this.name;
}
/**
* Set the name property if it is not already set. Because of the order of the callbacks in a Spring container the
* name property will be set first if it is present. Care is needed with bean definition inheritance - if a parent
* bean has a name, then its children need an explicit name as well, otherwise they will not be unique.
*
* @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)
*/
public void setBeanName(String name) {
if (this.name == null) {
this.name = name;
}
}
/**
* Set the name property. Always overrides the default value if this object is a Spring bean.
*
* @see #setBeanName(java.lang.String)
*/
public void setName(String name) {
this.name = name;
}
public int getStartLimit() {
return this.startLimit;
}
/**
* Public setter for the startLimit.
*
* @param startLimit the startLimit to set
*/
public void setStartLimit(int startLimit) {
this.startLimit = startLimit;
}
public boolean isAllowStartIfComplete() {
return this.allowStartIfComplete;
}
/**
* Public setter for the shouldAllowStartIfComplete.
*
* @param allowStartIfComplete the shouldAllowStartIfComplete to set
*/
public void setAllowStartIfComplete(boolean allowStartIfComplete) {
this.allowStartIfComplete = allowStartIfComplete;
}
/**
* Not supported but provided so that tests can easily create a step.
*
* @throws UnsupportedOperationException always
*
* @see org.springframework.batch.core.domain.Step#execute(org.springframework.batch.core.domain.StepExecution)
*/
public void execute(StepExecution stepExecution) throws JobInterruptedException, InfrastructureException {
throw new UnsupportedOperationException(
"Cannot process a StepExecution. Use a smarter subclass of StepSupport.");
}
}

View File

@@ -1,189 +1,189 @@
/*
* 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.step.support;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import junit.framework.TestCase;
import org.springframework.batch.core.domain.BatchListener;
import org.springframework.batch.core.domain.BatchStatus;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.listener.ItemListenerSupport;
import org.springframework.batch.execution.job.SimpleJob;
import org.springframework.batch.execution.repository.SimpleJobRepository;
import org.springframework.batch.execution.repository.dao.MapJobExecutionDao;
import org.springframework.batch.execution.repository.dao.MapJobInstanceDao;
import org.springframework.batch.execution.repository.dao.MapStepExecutionDao;
import org.springframework.batch.execution.step.AbstractStep;
import org.springframework.batch.execution.step.ItemOrientedStep;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.reader.ListItemReader;
import org.springframework.batch.item.writer.AbstractItemWriter;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.exception.handler.ExceptionHandler;
import org.springframework.batch.repeat.support.RepeatTemplate;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
public class DefaultStepFactoryBeanTests extends TestCase {
private List recovered = new ArrayList();
private SimpleJobRepository repository = new SimpleJobRepository(new MapJobInstanceDao(), new MapJobExecutionDao(),
new MapStepExecutionDao());
private List processed = new ArrayList();
private ItemWriter processor = new AbstractItemWriter() {
public void write(Object data) throws Exception {
processed.add((String) data);
}
};
private ItemReader provider;
private SimpleJob job = new SimpleJob();;
protected void setUp() throws Exception {
super.setUp();
job.setJobRepository(repository);
MapJobInstanceDao.clear();
MapJobExecutionDao.clear();
MapStepExecutionDao.clear();
}
private DefaultStepFactoryBean getStep(String arg) throws Exception {
return getStep(new String[] { arg });
}
private DefaultStepFactoryBean getStep(String arg0, String arg1) throws Exception {
return getStep(new String[] { arg0, arg1 });
}
private DefaultStepFactoryBean getStep(String[] args) throws Exception {
DefaultStepFactoryBean factory = new DefaultStepFactoryBean();
List items = TransactionAwareProxyFactory.createTransactionalList();
items.addAll(Arrays.asList(args));
provider = new ListItemReader(items);
factory.setItemReader(provider);
factory.setItemWriter(processor);
factory.setJobRepository(repository);
factory.setTransactionManager(new ResourcelessTransactionManager());
factory.setBeanName("stepName");
return factory;
}
public void testSimpleJob() throws Exception {
job.setSteps(new ArrayList());
AbstractStep step = (AbstractStep) getStep("foo", "bar").getObject();
step.setName("step1");
job.addStep(step);
step = (AbstractStep) getStep("spam").getObject();
step.setName("step2");
job.addStep(step);
JobExecution jobExecution = repository.createJobExecution(job, new JobParameters());
job.execute(jobExecution);
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
assertEquals(3, processed.size());
assertTrue(processed.contains("foo"));
}
public void testSimpleJobWithItemListeners() throws Exception {
final List throwables = new ArrayList();
RepeatTemplate chunkOperations = new RepeatTemplate();
// Always handle the exception a check it is the right one...
chunkOperations.setExceptionHandler(new ExceptionHandler() {
public void handleException(RepeatContext context, Throwable throwable) throws RuntimeException {
throwables.add(throwable);
assertEquals("Error!", throwable.getMessage());
}
});
/*
* Each message fails once and the chunk (size=1) "rolls back"; then it
* is recovered ("skipped") on the second attempt (see retry policy
* definition above)...
*/
DefaultStepFactoryBean factory = getStep(new String[] { "foo", "bar", "spam" });
factory.setItemWriter(new AbstractItemWriter() {
public void write(Object data) throws Exception {
throw new RuntimeException("Error!");
}
});
factory.setListeners(new BatchListener[] { new ItemListenerSupport() {
public void onReadError(Exception ex) {
recovered.add(ex);
}
public void onWriteError(Exception ex, Object item) {
recovered.add(ex);
}
} });
ItemOrientedStep step = (ItemOrientedStep) factory.getObject();
step.setChunkOperations(chunkOperations);
job.setSteps(Collections.singletonList(step));
JobExecution jobExecution = repository.createJobExecution(job, new JobParameters());
job.execute(jobExecution);
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
assertEquals(0, processed.size());
// provider should be exhausted
assertEquals(null, provider.read());
assertEquals(3, recovered.size());
}
public void testExceptionTerminates() throws Exception {
DefaultStepFactoryBean factory = getStep(new String[] { "foo", "bar", "spam" });
factory.setBeanName("exceptionStep");
factory.setItemWriter(new AbstractItemWriter() {
public void write(Object data) throws Exception {
throw new RuntimeException("Foo");
}
});
ItemOrientedStep step = (ItemOrientedStep) factory.getObject();
job.setSteps(Collections.singletonList(step));
JobExecution jobExecution = repository.createJobExecution(job, new JobParameters());
try {
job.execute(jobExecution);
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
assertEquals("Foo", e.getMessage());
// expected
}
assertEquals(BatchStatus.FAILED, jobExecution.getStatus());
}
}
/*
* 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.step.support;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import junit.framework.TestCase;
import org.springframework.batch.core.domain.BatchListener;
import org.springframework.batch.core.domain.BatchStatus;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.listener.ItemListenerSupport;
import org.springframework.batch.execution.job.SimpleJob;
import org.springframework.batch.execution.repository.SimpleJobRepository;
import org.springframework.batch.execution.repository.dao.MapJobExecutionDao;
import org.springframework.batch.execution.repository.dao.MapJobInstanceDao;
import org.springframework.batch.execution.repository.dao.MapStepExecutionDao;
import org.springframework.batch.execution.step.AbstractStep;
import org.springframework.batch.execution.step.ItemOrientedStep;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.reader.ListItemReader;
import org.springframework.batch.item.writer.AbstractItemWriter;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.exception.handler.ExceptionHandler;
import org.springframework.batch.repeat.support.RepeatTemplate;
import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
public class DefaultStepFactoryBeanTests extends TestCase {
private List recovered = new ArrayList();
private SimpleJobRepository repository = new SimpleJobRepository(new MapJobInstanceDao(), new MapJobExecutionDao(),
new MapStepExecutionDao());
private List processed = new ArrayList();
private ItemWriter processor = new AbstractItemWriter() {
public void write(Object data) throws Exception {
processed.add((String) data);
}
};
private ItemReader provider;
private SimpleJob job = new SimpleJob();;
protected void setUp() throws Exception {
super.setUp();
job.setJobRepository(repository);
MapJobInstanceDao.clear();
MapJobExecutionDao.clear();
MapStepExecutionDao.clear();
}
private DefaultStepFactoryBean getStep(String arg) throws Exception {
return getStep(new String[] { arg });
}
private DefaultStepFactoryBean getStep(String arg0, String arg1) throws Exception {
return getStep(new String[] { arg0, arg1 });
}
private DefaultStepFactoryBean getStep(String[] args) throws Exception {
DefaultStepFactoryBean factory = new DefaultStepFactoryBean();
List items = TransactionAwareProxyFactory.createTransactionalList();
items.addAll(Arrays.asList(args));
provider = new ListItemReader(items);
factory.setItemReader(provider);
factory.setItemWriter(processor);
factory.setJobRepository(repository);
factory.setTransactionManager(new ResourcelessTransactionManager());
factory.setBeanName("stepName");
return factory;
}
public void testSimpleJob() throws Exception {
job.setSteps(new ArrayList());
AbstractStep step = (AbstractStep) getStep("foo", "bar").getObject();
step.setName("step1");
job.addStep(step);
step = (AbstractStep) getStep("spam").getObject();
step.setName("step2");
job.addStep(step);
JobExecution jobExecution = repository.createJobExecution(job, new JobParameters());
job.execute(jobExecution);
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
assertEquals(3, processed.size());
assertTrue(processed.contains("foo"));
}
public void testSimpleJobWithItemListeners() throws Exception {
final List throwables = new ArrayList();
RepeatTemplate chunkOperations = new RepeatTemplate();
// Always handle the exception a check it is the right one...
chunkOperations.setExceptionHandler(new ExceptionHandler() {
public void handleException(RepeatContext context, Throwable throwable) throws RuntimeException {
throwables.add(throwable);
assertEquals("Error!", throwable.getMessage());
}
});
/*
* Each message fails once and the chunk (size=1) "rolls back"; then it
* is recovered ("skipped") on the second attempt (see retry policy
* definition above)...
*/
DefaultStepFactoryBean factory = getStep(new String[] { "foo", "bar", "spam" });
factory.setItemWriter(new AbstractItemWriter() {
public void write(Object data) throws Exception {
throw new RuntimeException("Error!");
}
});
factory.setListeners(new BatchListener[] { new ItemListenerSupport() {
public void onReadError(Exception ex) {
recovered.add(ex);
}
public void onWriteError(Exception ex, Object item) {
recovered.add(ex);
}
} });
ItemOrientedStep step = (ItemOrientedStep) factory.getObject();
step.setChunkOperations(chunkOperations);
job.setSteps(Collections.singletonList(step));
JobExecution jobExecution = repository.createJobExecution(job, new JobParameters());
job.execute(jobExecution);
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
assertEquals(0, processed.size());
// provider should be exhausted
assertEquals(null, provider.read());
assertEquals(3, recovered.size());
}
public void testExceptionTerminates() throws Exception {
DefaultStepFactoryBean factory = getStep(new String[] { "foo", "bar", "spam" });
factory.setBeanName("exceptionStep");
factory.setItemWriter(new AbstractItemWriter() {
public void write(Object data) throws Exception {
throw new RuntimeException("Foo");
}
});
ItemOrientedStep step = (ItemOrientedStep) factory.getObject();
job.setSteps(Collections.singletonList(step));
JobExecution jobExecution = repository.createJobExecution(job, new JobParameters());
try {
job.execute(jobExecution);
fail("Expected RuntimeException");
}
catch (RuntimeException e) {
assertEquals("Foo", e.getMessage());
// expected
}
assertEquals(BatchStatus.FAILED, jobExecution.getStatus());
}
}

View File

@@ -1,73 +1,73 @@
/*
* 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.step.support;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.core.repository.JobRepository;
/**
* @author Dave Syer
*
*/
public class JobRepositorySupport implements JobRepository {
/* (non-Javadoc)
* @see org.springframework.batch.container.common.repository.JobRepository#findOrCreateJob(org.springframework.batch.container.common.domain.JobConfiguration)
*/
public JobExecution createJobExecution(Job jobConfiguration, JobParameters jobParameters) {
return null;
}
/* (non-Javadoc)
* @see org.springframework.batch.container.common.repository.JobRepository#saveOrUpdate(org.springframework.batch.container.common.domain.JobExecution)
*/
public void saveOrUpdate(JobExecution jobExecution) {
}
/* (non-Javadoc)
* @see org.springframework.batch.container.common.repository.JobRepository#saveOrUpdate(org.springframework.batch.container.common.domain.StepExecution)
*/
public void saveOrUpdate(StepExecution stepExecution) {
}
/* (non-Javadoc)
* @see org.springframework.batch.core.repository.JobRepository#saveOrUpdateExecutionContext(org.springframework.batch.core.domain.StepExecution)
*/
public void saveOrUpdateExecutionContext(StepExecution stepExecution) {
}
/* (non-Javadoc)
* @see org.springframework.batch.container.common.repository.JobRepository#update(org.springframework.batch.container.common.domain.Job)
*/
public void update(JobInstance job) {
}
public StepExecution getLastStepExecution(JobInstance jobInstance, Step step) {
// TODO Auto-generated method stub
return null;
}
public int getStepExecutionCount(JobInstance jobInstance, Step step) {
// TODO Auto-generated method stub
return 0;
}
}
/*
* 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.step.support;
import org.springframework.batch.core.domain.Job;
import org.springframework.batch.core.domain.JobExecution;
import org.springframework.batch.core.domain.JobInstance;
import org.springframework.batch.core.domain.JobParameters;
import org.springframework.batch.core.domain.Step;
import org.springframework.batch.core.domain.StepExecution;
import org.springframework.batch.core.repository.JobRepository;
/**
* @author Dave Syer
*
*/
public class JobRepositorySupport implements JobRepository {
/* (non-Javadoc)
* @see org.springframework.batch.container.common.repository.JobRepository#findOrCreateJob(org.springframework.batch.container.common.domain.JobConfiguration)
*/
public JobExecution createJobExecution(Job jobConfiguration, JobParameters jobParameters) {
return null;
}
/* (non-Javadoc)
* @see org.springframework.batch.container.common.repository.JobRepository#saveOrUpdate(org.springframework.batch.container.common.domain.JobExecution)
*/
public void saveOrUpdate(JobExecution jobExecution) {
}
/* (non-Javadoc)
* @see org.springframework.batch.container.common.repository.JobRepository#saveOrUpdate(org.springframework.batch.container.common.domain.StepExecution)
*/
public void saveOrUpdate(StepExecution stepExecution) {
}
/* (non-Javadoc)
* @see org.springframework.batch.core.repository.JobRepository#saveOrUpdateExecutionContext(org.springframework.batch.core.domain.StepExecution)
*/
public void saveOrUpdateExecutionContext(StepExecution stepExecution) {
}
/* (non-Javadoc)
* @see org.springframework.batch.container.common.repository.JobRepository#update(org.springframework.batch.container.common.domain.Job)
*/
public void update(JobInstance job) {
}
public StepExecution getLastStepExecution(JobInstance jobInstance, Step step) {
// TODO Auto-generated method stub
return null;
}
public int getStepExecutionCount(JobInstance jobInstance, Step step) {
// TODO Auto-generated method stub
return 0;
}
}

View File

@@ -1,77 +1,77 @@
/*
* 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.step.support;
import org.springframework.batch.core.domain.JobInterruptedException;
import org.springframework.batch.core.runtime.ExitStatusExceptionClassifier;
import org.springframework.batch.execution.step.support.SimpleExitStatusExceptionClassifier;
import org.springframework.batch.repeat.ExitStatus;
import junit.framework.TestCase;
/**
* @author Lucas Ward
*
*/
public class SimpleExitStatusExceptionClassifierTests extends TestCase {
NullPointerException exception;
SimpleExitStatusExceptionClassifier classifier = new SimpleExitStatusExceptionClassifier();
protected void setUp() throws Exception {
super.setUp();
exception = new NullPointerException();
}
public void testClassifyForExitCode() {
ExitStatus exitStatus = classifier.classifyForExitCode(exception);
assertEquals(exitStatus.getExitCode(), "FATAL_EXCEPTION");
String description = exitStatus.getExitDescription();
assertTrue("Description does not contain NullPointerException: "+description, description.indexOf("java.lang.NullPointerException")>=0);
}
public void testClassify() {
ExitStatus exitStatus = (ExitStatus)classifier.classify(exception);
assertEquals(exitStatus.getExitCode(), "FATAL_EXCEPTION");
String description = exitStatus.getExitDescription();
assertTrue("Description does not contain NullPointerException: "+description, description.indexOf("java.lang.NullPointerException")>=0);
}
public void testGetDefault() {
ExitStatus exitStatus = (ExitStatus)classifier.getDefault();
assertEquals(exitStatus.getExitCode(), "FATAL_EXCEPTION");
assertEquals(exitStatus.getExitDescription(), "");
}
/*
* Attempting to classify a null throwable should lead to a blank description, not a
* null pointer exception.
*/
public void testClassifyNullThrowable(){
ExitStatus exitStatus = (ExitStatus)classifier.classify(null);
assertEquals(exitStatus.getExitCode(), "FATAL_EXCEPTION");
assertEquals(exitStatus.getExitDescription(), "");
}
public void testClassifyInterruptedException(){
ExitStatus exitStatus = (ExitStatus)classifier.classifyForExitCode(new JobInterruptedException(""));
assertEquals(exitStatus.getExitCode(), ExitStatusExceptionClassifier.JOB_INTERRUPTED);
assertEquals(exitStatus.getExitDescription(),
JobInterruptedException.class.getName());
}
}
/*
* 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.step.support;
import org.springframework.batch.core.domain.JobInterruptedException;
import org.springframework.batch.core.runtime.ExitStatusExceptionClassifier;
import org.springframework.batch.execution.step.support.SimpleExitStatusExceptionClassifier;
import org.springframework.batch.repeat.ExitStatus;
import junit.framework.TestCase;
/**
* @author Lucas Ward
*
*/
public class SimpleExitStatusExceptionClassifierTests extends TestCase {
NullPointerException exception;
SimpleExitStatusExceptionClassifier classifier = new SimpleExitStatusExceptionClassifier();
protected void setUp() throws Exception {
super.setUp();
exception = new NullPointerException();
}
public void testClassifyForExitCode() {
ExitStatus exitStatus = classifier.classifyForExitCode(exception);
assertEquals(exitStatus.getExitCode(), "FATAL_EXCEPTION");
String description = exitStatus.getExitDescription();
assertTrue("Description does not contain NullPointerException: "+description, description.indexOf("java.lang.NullPointerException")>=0);
}
public void testClassify() {
ExitStatus exitStatus = (ExitStatus)classifier.classify(exception);
assertEquals(exitStatus.getExitCode(), "FATAL_EXCEPTION");
String description = exitStatus.getExitDescription();
assertTrue("Description does not contain NullPointerException: "+description, description.indexOf("java.lang.NullPointerException")>=0);
}
public void testGetDefault() {
ExitStatus exitStatus = (ExitStatus)classifier.getDefault();
assertEquals(exitStatus.getExitCode(), "FATAL_EXCEPTION");
assertEquals(exitStatus.getExitDescription(), "");
}
/*
* Attempting to classify a null throwable should lead to a blank description, not a
* null pointer exception.
*/
public void testClassifyNullThrowable(){
ExitStatus exitStatus = (ExitStatus)classifier.classify(null);
assertEquals(exitStatus.getExitCode(), "FATAL_EXCEPTION");
assertEquals(exitStatus.getExitDescription(), "");
}
public void testClassifyInterruptedException(){
ExitStatus exitStatus = (ExitStatus)classifier.classifyForExitCode(new JobInterruptedException(""));
assertEquals(exitStatus.getExitCode(), ExitStatusExceptionClassifier.JOB_INTERRUPTED);
assertEquals(exitStatus.getExitDescription(),
JobInterruptedException.class.getName());
}
}

View File

@@ -1,58 +1,58 @@
/*
* 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.step.support;
import junit.framework.TestCase;
import org.springframework.batch.core.domain.JobInterruptedException;
import org.springframework.batch.execution.step.support.ThreadStepInterruptionPolicy;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.context.RepeatContextSupport;
/**
* @author Dave Syer
*
*/
public class ThreadStepInterruptionPolicyTests extends TestCase {
ThreadStepInterruptionPolicy policy = new ThreadStepInterruptionPolicy();
private RepeatContext context = new RepeatContextSupport(null);;
/**
* Test method for {@link org.springframework.batch.core.executor.interrupt.ThreadStepInterruptionPolicy#checkInterrupted(org.springframework.batch.repeat.RepeatContext)}.
* @throws Exception
*/
public void testCheckInterruptedNotComplete() throws Exception {
policy.checkInterrupted(context);
// no exception
}
/**
* Test method for {@link org.springframework.batch.core.executor.interrupt.ThreadStepInterruptionPolicy#checkInterrupted(org.springframework.batch.repeat.RepeatContext)}.
* @throws Exception
*/
public void testCheckInterruptedComplete() throws Exception {
context.setTerminateOnly();
try {
policy.checkInterrupted(context);
fail("Expected StepInterruptedException");
} catch (JobInterruptedException e) {
// expected
assertTrue(e.getMessage().indexOf("interrupt")>=0);
}
}
}
/*
* 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.step.support;
import junit.framework.TestCase;
import org.springframework.batch.core.domain.JobInterruptedException;
import org.springframework.batch.execution.step.support.ThreadStepInterruptionPolicy;
import org.springframework.batch.repeat.RepeatContext;
import org.springframework.batch.repeat.context.RepeatContextSupport;
/**
* @author Dave Syer
*
*/
public class ThreadStepInterruptionPolicyTests extends TestCase {
ThreadStepInterruptionPolicy policy = new ThreadStepInterruptionPolicy();
private RepeatContext context = new RepeatContextSupport(null);;
/**
* Test method for {@link org.springframework.batch.core.executor.interrupt.ThreadStepInterruptionPolicy#checkInterrupted(org.springframework.batch.repeat.RepeatContext)}.
* @throws Exception
*/
public void testCheckInterruptedNotComplete() throws Exception {
policy.checkInterrupted(context);
// no exception
}
/**
* Test method for {@link org.springframework.batch.core.executor.interrupt.ThreadStepInterruptionPolicy#checkInterrupted(org.springframework.batch.repeat.RepeatContext)}.
* @throws Exception
*/
public void testCheckInterruptedComplete() throws Exception {
context.setTerminateOnly();
try {
policy.checkInterrupted(context);
fail("Expected StepInterruptedException");
} catch (JobInterruptedException e) {
// expected
assertTrue(e.getMessage().indexOf("interrupt")>=0);
}
}
}