Added the concept of a StepRegistry that tracks the Step instance attached to a Job.
- Added a simple MapStepRegistry that keeps track of the step in a simple map per job - MapJobRegistry implements StepRegistry as well using the delegate pattern - DefaultJobLoader makes sure to initialize both registries now. Auto-detects that the JobRegistry implements StepRegistry - JobSupport now implements StepLocator so that such job can be loaded by the DefaultJobLoader - Also registering any "detached" step available for a particular job to that job if that wasn't done yet. Since a partition handler may link to any step by name, the step registry must be filled with any step that are available in the context of a job. If each job is located in its own application context, this is just fine. If more than one job is defined in the same context, the step registry may be able to return a step that is not related to that job but it shouldn't hurt as this happens only if a component is explicitly asking to execute a step by name for a particular job.
This commit is contained in:
committed by
Michael Minella
parent
b345e1c072
commit
00ee45854e
@@ -0,0 +1,47 @@
|
||||
package org.springframework.batch.core.configuration;
|
||||
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.launch.NoSuchJobException;
|
||||
import org.springframework.batch.core.step.NoSuchStepException;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Registry keeping track of all the {@link Step} defined in a
|
||||
* {@link org.springframework.batch.core.Job}.
|
||||
*
|
||||
* @author Sebastien Gerard
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public interface StepRegistry {
|
||||
|
||||
/**
|
||||
* Registers all the step of the given job. If the job is already registered,
|
||||
* the method {@link #unregisterStepsFromJob(String)} is called before registering
|
||||
* the given steps.
|
||||
*
|
||||
* @param jobName the give job name
|
||||
* @param steps the job steps
|
||||
*/
|
||||
void register(String jobName, Collection<Step> steps);
|
||||
|
||||
/**
|
||||
* Unregisters all the steps of the given job. If the job is not registered,
|
||||
* nothing happens.
|
||||
*
|
||||
* @param jobName the given job name
|
||||
*/
|
||||
void unregisterStepsFromJob(String jobName);
|
||||
|
||||
/**
|
||||
* Returns the {@link Step} of the specified job based on its name.
|
||||
*
|
||||
* @param jobName the name of the job
|
||||
* @param stepName the name of the step to retrieve
|
||||
* @return the step with the given name belonging to the mentioned job
|
||||
* @throws NoSuchJobException no such job with that name exists
|
||||
* @throws NoSuchStepException no such step with that name for that job exists
|
||||
*/
|
||||
Step getStep(String jobName, String stepName) throws NoSuchJobException, NoSuchStepException;
|
||||
|
||||
}
|
||||
@@ -24,24 +24,31 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.configuration.DuplicateJobException;
|
||||
import org.springframework.batch.core.configuration.JobFactory;
|
||||
import org.springframework.batch.core.configuration.JobRegistry;
|
||||
import org.springframework.batch.core.configuration.StepRegistry;
|
||||
import org.springframework.batch.core.launch.NoSuchJobException;
|
||||
import org.springframework.batch.core.step.StepLocator;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link JobLoader}. Uses a {@link JobRegistry} to
|
||||
* manage a population of loaded jobs and clears them up when asked.
|
||||
*
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class DefaultJobLoader implements JobLoader {
|
||||
public class DefaultJobLoader implements JobLoader, InitializingBean {
|
||||
|
||||
private static Log logger = LogFactory.getLog(DefaultJobLoader.class);
|
||||
|
||||
private JobRegistry jobRegistry;
|
||||
private StepRegistry stepRegistry;
|
||||
|
||||
private Map<ApplicationContextFactory, ConfigurableApplicationContext> contexts = new ConcurrentHashMap<ApplicationContextFactory, ConfigurableApplicationContext>();
|
||||
|
||||
@@ -51,30 +58,57 @@ public class DefaultJobLoader implements JobLoader {
|
||||
* Default constructor useful for declarative configuration.
|
||||
*/
|
||||
public DefaultJobLoader() {
|
||||
this(null);
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a job loader with the job registry provided.
|
||||
* <p/>
|
||||
* If the specified {@link JobRegistry} is also a {@link StepRegistry} it
|
||||
* is registered as the step registry to use for this instance.
|
||||
*
|
||||
* @param jobRegistry a {@link JobRegistry}
|
||||
*/
|
||||
public DefaultJobLoader(JobRegistry jobRegistry) {
|
||||
this(jobRegistry, jobRegistry instanceof StepRegistry ? (StepRegistry) jobRegistry : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a job loader with the job registry provided.
|
||||
* Creates a job loader with the job and step registries provided.
|
||||
*
|
||||
* @param jobRegistry a {@link JobRegistry}
|
||||
* @param stepRegistry a {@link StepRegistry}
|
||||
*/
|
||||
public DefaultJobLoader(JobRegistry jobRegistry) {
|
||||
public DefaultJobLoader(JobRegistry jobRegistry, StepRegistry stepRegistry) {
|
||||
this.jobRegistry = jobRegistry;
|
||||
this.stepRegistry = stepRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link JobRegistry} to use for jobs created.
|
||||
*
|
||||
* @param jobRegistry
|
||||
*
|
||||
* @param jobRegistry the job registry
|
||||
*/
|
||||
public void setJobRegistry(JobRegistry jobRegistry) {
|
||||
this.jobRegistry = jobRegistry;
|
||||
if (stepRegistry == null && jobRegistry instanceof StepRegistry) {
|
||||
setStepRegistry((StepRegistry) jobRegistry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link StepRegistry} to use for the steps of created jobs.
|
||||
*
|
||||
* @param stepRegistry the step registry
|
||||
*/
|
||||
public void setStepRegistry(StepRegistry stepRegistry) {
|
||||
this.stepRegistry = stepRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all the jobs and close all the contexts created by this
|
||||
* loader.
|
||||
*
|
||||
*
|
||||
* @see JobLoader#clear()
|
||||
*/
|
||||
public void clear() {
|
||||
@@ -85,6 +119,7 @@ public class DefaultJobLoader implements JobLoader {
|
||||
}
|
||||
for (String jobName : jobRegistry.getJobNames()) {
|
||||
jobRegistry.unregister(jobName);
|
||||
stepRegistry.unregisterStepsFromJob(jobName);
|
||||
}
|
||||
contexts.clear();
|
||||
}
|
||||
@@ -97,6 +132,7 @@ public class DefaultJobLoader implements JobLoader {
|
||||
for (String name : contextToJobNames.get(context)) {
|
||||
logger.debug("Unregistering job: " + name + " from context: " + context.getDisplayName());
|
||||
jobRegistry.unregister(name);
|
||||
stepRegistry.unregisterStepsFromJob(name);
|
||||
}
|
||||
context.close();
|
||||
}
|
||||
@@ -105,7 +141,7 @@ public class DefaultJobLoader implements JobLoader {
|
||||
return doLoad(factory, true);
|
||||
}
|
||||
catch (DuplicateJobException e) {
|
||||
throw new IllegalStateException("Found duplicte job in reload (it should have been unregistered "
|
||||
throw new IllegalStateException("Found duplicate job in reload (it should have been unregistered "
|
||||
+ "if it was previously registered in this loader)", e);
|
||||
}
|
||||
}
|
||||
@@ -151,6 +187,7 @@ public class DefaultJobLoader implements JobLoader {
|
||||
JobFactory jobFactory = new ReferenceJobFactory(job);
|
||||
jobRegistry.register(jobFactory);
|
||||
jobsRegistered.add(jobName);
|
||||
stepRegistry.register(job.getName(), getSteps(job, context));
|
||||
|
||||
}
|
||||
|
||||
@@ -174,4 +211,43 @@ public class DefaultJobLoader implements JobLoader {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the {@link Step} instances defined by the specified {@link Job}.
|
||||
*
|
||||
* @param job the given job
|
||||
* @param jobApplicationContext the application context of the job
|
||||
* @return all the {@link Step} defined in the given job
|
||||
* @see StepLocator
|
||||
*/
|
||||
private Collection<Step> getSteps(final Job job, final ApplicationContext jobApplicationContext) {
|
||||
// TODO: that sounds like we need a stronger contract here
|
||||
if (!(job instanceof StepLocator)) {
|
||||
throw new UnsupportedOperationException("Cannot locate step from a Job that is not a StepLocator: job="
|
||||
+ job.getName() + " does not implement StepLocator");
|
||||
}
|
||||
final StepLocator stepLocator = (StepLocator) job;
|
||||
final Collection<String> stepNames = stepLocator.getStepNames();
|
||||
final Collection<Step> result = new ArrayList<Step>();
|
||||
for (String stepName : stepNames) {
|
||||
result.add(stepLocator.getStep(stepName));
|
||||
}
|
||||
|
||||
// Because some steps are referenced by name, we need to look in the context to see if there
|
||||
// are more Step instances defined. Right now they are registered as being available in the
|
||||
// context of the job but we have no idea if they are linked to that Job or not.
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<String, Step> allSteps = jobApplicationContext.getBeansOfType(Step.class);
|
||||
for (Map.Entry<String, Step> entry : allSteps.entrySet()) {
|
||||
if (!stepNames.contains(entry.getKey())) {
|
||||
result.add(entry.getValue());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(jobRegistry, "Job registry could not be null.");
|
||||
Assert.notNull(stepRegistry, "Step registry could not be null. Should be set if the Job registry " +
|
||||
"implementation does not implement StepRegistry.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,64 +15,78 @@
|
||||
*/
|
||||
package org.springframework.batch.core.configuration.support;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.configuration.DuplicateJobException;
|
||||
import org.springframework.batch.core.configuration.JobFactory;
|
||||
import org.springframework.batch.core.configuration.JobRegistry;
|
||||
import org.springframework.batch.core.configuration.StepRegistry;
|
||||
import org.springframework.batch.core.launch.NoSuchJobException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* Simple, thread-safe, map-based implementation of {@link JobRegistry}.
|
||||
*
|
||||
* Simple, thread-safe, map-based implementation of {@link JobRegistry}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Robert Fischer
|
||||
*
|
||||
*/
|
||||
public class MapJobRegistry implements JobRegistry {
|
||||
public class MapJobRegistry implements JobRegistry, StepRegistry {
|
||||
|
||||
/**
|
||||
* The map holding the registered job factories.
|
||||
*/
|
||||
// The "final" ensures that it is visible and initialized when the constructor resolves.
|
||||
private final ConcurrentMap<String, JobFactory> map = new ConcurrentHashMap<String, JobFactory>();
|
||||
/**
|
||||
* The map holding the registered job factories.
|
||||
*/
|
||||
// The "final" ensures that it is visible and initialized when the constructor resolves.
|
||||
private final ConcurrentMap<String, JobFactory> map = new ConcurrentHashMap<String, JobFactory>();
|
||||
private final MapStepRegistry stepRegistry = new MapStepRegistry();
|
||||
|
||||
public void register(JobFactory jobFactory) throws DuplicateJobException {
|
||||
Assert.notNull(jobFactory);
|
||||
String name = jobFactory.getJobName();
|
||||
Assert.notNull(name, "Job configuration must have a name.");
|
||||
JobFactory previousValue = map.putIfAbsent(name, jobFactory);
|
||||
if(previousValue != null) {
|
||||
throw new DuplicateJobException("A job configuration with this name [" + name
|
||||
+ "] was already registered");
|
||||
}
|
||||
}
|
||||
public void register(JobFactory jobFactory) throws DuplicateJobException {
|
||||
Assert.notNull(jobFactory);
|
||||
String name = jobFactory.getJobName();
|
||||
Assert.notNull(name, "Job configuration must have a name.");
|
||||
JobFactory previousValue = map.putIfAbsent(name, jobFactory);
|
||||
if (previousValue != null) {
|
||||
throw new DuplicateJobException("A job configuration with this name [" + name
|
||||
+ "] was already registered");
|
||||
}
|
||||
}
|
||||
|
||||
public void unregister(String name) {
|
||||
Assert.notNull(name, "Job configuration must have a name.");
|
||||
map.remove(name);
|
||||
}
|
||||
public void unregister(String name) {
|
||||
Assert.notNull(name, "Job configuration must have a name.");
|
||||
map.remove(name);
|
||||
}
|
||||
|
||||
public Job getJob(String name) throws NoSuchJobException {
|
||||
JobFactory factory = map.get(name);
|
||||
if(factory == null) {
|
||||
throw new NoSuchJobException("No job configuration with the name [" + name + "] was registered");
|
||||
} else {
|
||||
return factory.createJob();
|
||||
}
|
||||
}
|
||||
public Job getJob(String name) throws NoSuchJobException {
|
||||
JobFactory factory = map.get(name);
|
||||
if (factory == null) {
|
||||
throw new NoSuchJobException("No job configuration with the name [" + name + "] was registered");
|
||||
} else {
|
||||
return factory.createJob();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an unmodifiable view of the job names.
|
||||
*/
|
||||
public Set<String> getJobNames() {
|
||||
return Collections.unmodifiableSet(map.keySet());
|
||||
}
|
||||
/**
|
||||
* Provides an unmodifiable view of the job names.
|
||||
*/
|
||||
public Set<String> getJobNames() {
|
||||
return Collections.unmodifiableSet(map.keySet());
|
||||
}
|
||||
|
||||
public void register(String jobName, Collection<Step> steps) {
|
||||
stepRegistry.register(jobName, steps);
|
||||
}
|
||||
|
||||
public void unregisterStepsFromJob(String jobName) {
|
||||
stepRegistry.unregisterStepsFromJob(jobName);
|
||||
}
|
||||
|
||||
public Step getStep(String jobName, String stepName) throws NoSuchJobException {
|
||||
return stepRegistry.getStep(jobName, stepName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package org.springframework.batch.core.configuration.support;
|
||||
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.configuration.StepRegistry;
|
||||
import org.springframework.batch.core.launch.NoSuchJobException;
|
||||
import org.springframework.batch.core.step.NoSuchStepException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Simple map-based implementation of {@link StepRegistry}. Access to the map is
|
||||
* synchronized, guarded by an internal lock.
|
||||
*
|
||||
* @author Sebastien Gerard
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class MapStepRegistry implements StepRegistry {
|
||||
|
||||
private final Map<String, Map<String, Step>> map = new HashMap<String, Map<String, Step>>();
|
||||
|
||||
public void register(String jobName, Collection<Step> steps) {
|
||||
Assert.notNull(jobName, "The job name cannot be null.");
|
||||
Assert.notNull(steps, "The job steps cannot be null.");
|
||||
|
||||
unregisterStepsFromJob(jobName);
|
||||
|
||||
synchronized (this.map) {
|
||||
final Map<String, Step> jobSteps = new HashMap<String, Step>();
|
||||
for (Step step : steps) {
|
||||
jobSteps.put(step.getName(), step);
|
||||
}
|
||||
|
||||
this.map.put(jobName, jobSteps);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterStepsFromJob(String jobName) {
|
||||
Assert.notNull(jobName, "Job configuration must have a name.");
|
||||
synchronized (map) {
|
||||
map.remove(jobName);
|
||||
}
|
||||
}
|
||||
|
||||
public Step getStep(String jobName, String stepName) throws NoSuchJobException {
|
||||
Assert.notNull(jobName, "The job name cannot be null.");
|
||||
Assert.notNull(stepName, "The step name cannot be null.");
|
||||
|
||||
synchronized (map) {
|
||||
if (!map.containsKey(jobName)) {
|
||||
throw new NoSuchJobException("No job configuration with the name [" + jobName + "] was registered");
|
||||
} else {
|
||||
final Map<String, Step> jobSteps = map.get(jobName);
|
||||
if (jobSteps.containsKey(stepName)) {
|
||||
return jobSteps.get(stepName);
|
||||
} else {
|
||||
throw new NoSuchStepException("The step called [" + stepName + "] does not exist in the job [" +
|
||||
jobName + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,82 +16,234 @@
|
||||
package org.springframework.batch.core.configuration.support;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobParametersIncrementer;
|
||||
import org.springframework.batch.core.JobParametersValidator;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.configuration.DuplicateJobException;
|
||||
import org.springframework.batch.core.configuration.JobFactory;
|
||||
import org.springframework.batch.core.configuration.JobRegistry;
|
||||
import org.springframework.batch.core.configuration.StepRegistry;
|
||||
import org.springframework.batch.core.launch.NoSuchJobException;
|
||||
import org.springframework.batch.core.step.NoSuchStepException;
|
||||
import org.springframework.batch.core.step.StepLocator;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class DefaultJobLoaderTests {
|
||||
|
||||
private JobRegistry registry = new MapJobRegistry();
|
||||
/**
|
||||
* The name of the job as defined in the test context used in this test.
|
||||
*/
|
||||
private static final String TEST_JOB_NAME = "test-job";
|
||||
|
||||
private DefaultJobLoader jobLoader = new DefaultJobLoader(registry);
|
||||
/**
|
||||
* The name of the step as defined in the test context used in this test.
|
||||
*/
|
||||
private static final String TEST_STEP_NAME = "test-step";
|
||||
|
||||
private JobRegistry jobRegistry = new MapJobRegistry();
|
||||
private StepRegistry stepRegistry = new MapStepRegistry();
|
||||
|
||||
private DefaultJobLoader jobLoader = new DefaultJobLoader(jobRegistry, stepRegistry);
|
||||
|
||||
@Test
|
||||
public void testLoadWithExplicitName() throws Exception {
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(new ByteArrayResource(
|
||||
JOB_XML.getBytes()));
|
||||
jobLoader.load(factory);
|
||||
assertEquals(1, registry.getJobNames().size());
|
||||
assertEquals(1, jobRegistry.getJobNames().size());
|
||||
jobLoader.reload(factory);
|
||||
assertEquals(1, registry.getJobNames().size());
|
||||
assertEquals(1, jobRegistry.getJobNames().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReload() throws Exception {
|
||||
@Test
|
||||
public void createWithBothRegistries() {
|
||||
final DefaultJobLoader loader = new DefaultJobLoader();
|
||||
loader.setJobRegistry(jobRegistry);
|
||||
loader.setStepRegistry(stepRegistry);
|
||||
|
||||
loader.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWithJobRegistryWhichIsAStepRegistry() {
|
||||
final DefaultJobLoader loader = new DefaultJobLoader();
|
||||
loader.setJobRegistry(jobRegistry);
|
||||
|
||||
loader.afterPropertiesSet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWithSimpleJobRegistry() {
|
||||
final DefaultJobLoader loader = new DefaultJobLoader();
|
||||
loader.setJobRegistry(new JobRegistryMock());
|
||||
|
||||
try {
|
||||
loader.afterPropertiesSet();
|
||||
fail("Should have failed to create job loader without a step registry (" +
|
||||
"and the job registry could not fulfill that role)");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// OK
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegistryUpdated() throws DuplicateJobException {
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
|
||||
new ClassPathResource("trivial-context.xml", getClass()));
|
||||
jobLoader.load(factory);
|
||||
assertEquals(1, jobRegistry.getJobNames().size());
|
||||
assertStepExist(TEST_JOB_NAME, TEST_STEP_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleJobsInTheSameContext() throws DuplicateJobException {
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
|
||||
new ClassPathResource("job-context-with-steps.xml", getClass()));
|
||||
jobLoader.load(factory);
|
||||
assertEquals(2, jobRegistry.getJobNames().size());
|
||||
assertStepExist("job1", "step11", "step12");
|
||||
assertStepDoNotExist("job1", "step21", "step22");
|
||||
assertStepExist("job2", "step21", "step22");
|
||||
assertStepDoNotExist("job2", "step11", "step12");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleJobsInTheSameContextWithSeparateSteps() throws DuplicateJobException {
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(
|
||||
new ClassPathResource("job-context-with-separate-steps.xml", getClass()));
|
||||
jobLoader.load(factory);
|
||||
assertEquals(2, jobRegistry.getJobNames().size());
|
||||
assertStepExist("job1", "step11", "step12", "genericStep1", "genericStep2");
|
||||
assertStepDoNotExist("job1", "step21", "step22");
|
||||
assertStepExist("job2", "step21", "step22", "genericStep1", "genericStep2");
|
||||
assertStepDoNotExist("job2", "step11", "step12");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReload() throws Exception {
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(new ClassPathResource(
|
||||
"trivial-context.xml", getClass()));
|
||||
jobLoader.load(factory);
|
||||
assertEquals(1, registry.getJobNames().size());
|
||||
jobLoader.reload(factory);
|
||||
assertEquals(1, registry.getJobNames().size());
|
||||
}
|
||||
jobLoader.load(factory);
|
||||
assertEquals(1, jobRegistry.getJobNames().size());
|
||||
assertStepExist(TEST_JOB_NAME, TEST_STEP_NAME);
|
||||
jobLoader.reload(factory);
|
||||
assertEquals(1, jobRegistry.getJobNames().size());
|
||||
assertStepExist(TEST_JOB_NAME, TEST_STEP_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReloadWithAutoRegister() throws Exception {
|
||||
@Test
|
||||
public void testReloadWithAutoRegister() throws Exception {
|
||||
GenericApplicationContextFactory factory = new GenericApplicationContextFactory(new ClassPathResource(
|
||||
"trivial-context-autoregister.xml", getClass()));
|
||||
jobLoader.load(factory);
|
||||
assertEquals(1, registry.getJobNames().size());
|
||||
jobLoader.reload(factory);
|
||||
assertEquals(1, registry.getJobNames().size());
|
||||
}
|
||||
jobLoader.load(factory);
|
||||
assertEquals(1, jobRegistry.getJobNames().size());
|
||||
assertStepExist(TEST_JOB_NAME, TEST_STEP_NAME);
|
||||
jobLoader.reload(factory);
|
||||
assertEquals(1, jobRegistry.getJobNames().size());
|
||||
assertStepExist(TEST_JOB_NAME, TEST_STEP_NAME);
|
||||
}
|
||||
|
||||
private static final String JOB_XML = String
|
||||
.format("<beans xmlns='http://www.springframework.org/schema/beans' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "
|
||||
+ "xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd'><bean class='%s$StubJob'/></beans>",
|
||||
DefaultJobLoaderTests.class.getName());
|
||||
protected void assertStepExist(String jobName, String... stepNames) {
|
||||
for (String stepName : stepNames) {
|
||||
try {
|
||||
stepRegistry.getStep(jobName, stepName);
|
||||
} catch (NoSuchJobException e) {
|
||||
fail("Job with name [" + jobName + "] should have been found.");
|
||||
} catch (NoSuchStepException e) {
|
||||
fail("Step with name [" + stepName + "] for job [" + jobName + "] should have been found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class StubJob implements Job {
|
||||
protected void assertStepDoNotExist(String jobName, String... stepNames) {
|
||||
for (String stepName : stepNames) {
|
||||
try {
|
||||
final Step step = stepRegistry.getStep(jobName, stepName);
|
||||
fail("Step with name [" + stepName + "] for job [" + jobName + "] should " +
|
||||
"not have been found but got [" + step + "]");
|
||||
} catch (NoSuchJobException e) {
|
||||
fail("Job with name [" + jobName + "] should have been found.");
|
||||
} catch (NoSuchStepException e) {
|
||||
// OK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final String JOB_XML = String
|
||||
.format(
|
||||
"<beans xmlns='http://www.springframework.org/schema/beans' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "
|
||||
+ "xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd'><bean class='%s$StubJob'/></beans>",
|
||||
DefaultJobLoaderTests.class.getName());
|
||||
|
||||
public static class StubJob implements Job, StepLocator {
|
||||
|
||||
@Override
|
||||
public void execute(JobExecution execution) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JobParametersIncrementer getJobParametersIncrementer() {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "job";
|
||||
}
|
||||
return "job";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRestartable() {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JobParametersValidator getJobParametersValidator() {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public Collection<String> getStepNames() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Step getStep(String stepName) throws NoSuchStepException {
|
||||
throw new NoSuchStepException("Step [" + stepName + "] does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
private static class JobRegistryMock implements JobRegistry {
|
||||
@Override
|
||||
public void register(JobFactory jobFactory) throws DuplicateJobException {
|
||||
// dummy
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(String jobName) {
|
||||
// dummy
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getJobNames() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Job getJob(String name) throws NoSuchJobException {
|
||||
throw new NoSuchJobException("Mock implementation does not hold any job.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
package org.springframework.batch.core.configuration.support;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.configuration.StepRegistry;
|
||||
import org.springframework.batch.core.launch.NoSuchJobException;
|
||||
import org.springframework.batch.core.step.NoSuchStepException;
|
||||
import org.springframework.batch.core.step.tasklet.TaskletStep;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* @author Sebastien Gerard
|
||||
*/
|
||||
public class MapStepRegistryTests {
|
||||
|
||||
private static final String EXCEPTION_NOT_THROWN_MSG = "An exception should have been thrown";
|
||||
|
||||
@Test
|
||||
public void registerStepEmptyCollection() {
|
||||
final StepRegistry stepRegistry = createRegistry();
|
||||
|
||||
launchRegisterGetRegistered(stepRegistry, "myJob", getStepCollection());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerStepNullJobName() {
|
||||
final StepRegistry stepRegistry = createRegistry();
|
||||
|
||||
try {
|
||||
stepRegistry.register(null, new HashSet<Step>());
|
||||
Assert.fail(EXCEPTION_NOT_THROWN_MSG);
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerStepNullSteps() {
|
||||
final StepRegistry stepRegistry = createRegistry();
|
||||
|
||||
try {
|
||||
stepRegistry.register("fdsfsd", null);
|
||||
Assert.fail(EXCEPTION_NOT_THROWN_MSG);
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerStepGetStep() {
|
||||
final StepRegistry stepRegistry = createRegistry();
|
||||
|
||||
launchRegisterGetRegistered(stepRegistry, "myJob",
|
||||
getStepCollection(
|
||||
createStep("myStep"),
|
||||
createStep("myOtherStep"),
|
||||
createStep("myThirdStep")
|
||||
));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getJobNotRegistered() {
|
||||
final StepRegistry stepRegistry = createRegistry();
|
||||
|
||||
final String aStepName = "myStep";
|
||||
launchRegisterGetRegistered(stepRegistry, "myJob",
|
||||
getStepCollection(
|
||||
createStep(aStepName),
|
||||
createStep("myOtherStep"),
|
||||
createStep("myThirdStep")
|
||||
));
|
||||
|
||||
assertJobNotRegistered(stepRegistry, "a ghost");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getJobNotRegisteredNoRegistration() {
|
||||
final StepRegistry stepRegistry = createRegistry();
|
||||
|
||||
assertJobNotRegistered(stepRegistry, "a ghost");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStepNotRegistered() {
|
||||
final StepRegistry stepRegistry = createRegistry();
|
||||
|
||||
final String jobName = "myJob";
|
||||
launchRegisterGetRegistered(stepRegistry, jobName,
|
||||
getStepCollection(
|
||||
createStep("myStep"),
|
||||
createStep("myOtherStep"),
|
||||
createStep("myThirdStep")
|
||||
));
|
||||
|
||||
assertStepNameNotRegistered(stepRegistry, jobName, "fsdfsdfsdfsd");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerRegisterAgainAndGet() {
|
||||
final StepRegistry stepRegistry = createRegistry();
|
||||
|
||||
final String jobName = "myJob";
|
||||
final Collection<Step> stepsFirstRegistration = getStepCollection(
|
||||
createStep("myStep"),
|
||||
createStep("myOtherStep"),
|
||||
createStep("myThirdStep")
|
||||
);
|
||||
|
||||
// first registration
|
||||
launchRegisterGetRegistered(stepRegistry, jobName, stepsFirstRegistration);
|
||||
|
||||
// register again the job
|
||||
launchRegisterGetRegistered(stepRegistry, jobName,
|
||||
getStepCollection(
|
||||
createStep("myFourthStep"),
|
||||
createStep("lastOne")
|
||||
));
|
||||
|
||||
assertStepsNotRegistered(stepRegistry, jobName, stepsFirstRegistration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStepNullJobName() throws NoSuchJobException {
|
||||
final StepRegistry stepRegistry = createRegistry();
|
||||
|
||||
try {
|
||||
stepRegistry.getStep(null, "a step");
|
||||
Assert.fail(EXCEPTION_NOT_THROWN_MSG);
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStepNullStepName() throws NoSuchJobException {
|
||||
final StepRegistry stepRegistry = createRegistry();
|
||||
|
||||
final String stepName = "myStep";
|
||||
launchRegisterGetRegistered(stepRegistry, "myJob", getStepCollection(createStep(stepName)));
|
||||
|
||||
try {
|
||||
stepRegistry.getStep(null, stepName);
|
||||
Assert.fail(EXCEPTION_NOT_THROWN_MSG);
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerStepUnregisterJob() {
|
||||
final StepRegistry stepRegistry = createRegistry();
|
||||
|
||||
final Collection<Step> steps = getStepCollection(
|
||||
createStep("myStep"),
|
||||
createStep("myOtherStep"),
|
||||
createStep("myThirdStep")
|
||||
);
|
||||
|
||||
final String jobName = "myJob";
|
||||
launchRegisterGetRegistered(stepRegistry, jobName, steps);
|
||||
|
||||
stepRegistry.unregisterStepsFromJob(jobName);
|
||||
assertJobNotRegistered(stepRegistry, jobName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregisterJobNameNull() {
|
||||
final StepRegistry stepRegistry = createRegistry();
|
||||
|
||||
try {
|
||||
stepRegistry.unregisterStepsFromJob(null);
|
||||
Assert.fail(EXCEPTION_NOT_THROWN_MSG);
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregisterNoRegistration() {
|
||||
final StepRegistry stepRegistry = createRegistry();
|
||||
|
||||
assertJobNotRegistered(stepRegistry, "a job");
|
||||
}
|
||||
|
||||
protected StepRegistry createRegistry() {
|
||||
return new MapStepRegistry();
|
||||
}
|
||||
|
||||
protected Step createStep(String stepName) {
|
||||
return new TaskletStep(stepName);
|
||||
}
|
||||
|
||||
protected Collection<Step> getStepCollection(Step... steps) {
|
||||
return Arrays.asList(steps);
|
||||
}
|
||||
|
||||
protected void launchRegisterGetRegistered(StepRegistry stepRegistry, String jobName, Collection<Step> steps) {
|
||||
stepRegistry.register(jobName, steps);
|
||||
assertStepsRegistered(stepRegistry, jobName, steps);
|
||||
}
|
||||
|
||||
protected void assertJobNotRegistered(StepRegistry stepRegistry, String jobName) {
|
||||
try {
|
||||
stepRegistry.getStep(jobName, "a step");
|
||||
Assert.fail(EXCEPTION_NOT_THROWN_MSG);
|
||||
} catch (NoSuchJobException e) {
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertStepsRegistered(StepRegistry stepRegistry, String jobName, Collection<Step> steps) {
|
||||
for (Step step : steps) {
|
||||
try {
|
||||
stepRegistry.getStep(jobName, step.getName());
|
||||
} catch (NoSuchJobException e) {
|
||||
Assert.fail("Unexpected exception " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertStepsNotRegistered(StepRegistry stepRegistry, String jobName, Collection<Step> steps) {
|
||||
for (Step step : steps) {
|
||||
assertStepNameNotRegistered(stepRegistry, jobName, step.getName());
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertStepNameNotRegistered(StepRegistry stepRegistry, String jobName, String stepName) {
|
||||
try {
|
||||
stepRegistry.getStep(jobName, stepName);
|
||||
Assert.fail(EXCEPTION_NOT_THROWN_MSG);
|
||||
} catch (NoSuchJobException e) {
|
||||
Assert.fail("Unexpected exception");
|
||||
} catch (NoSuchStepException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package org.springframework.batch.core.job;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
@@ -25,6 +27,8 @@ import org.springframework.batch.core.JobParametersIncrementer;
|
||||
import org.springframework.batch.core.JobParametersValidator;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.UnexpectedJobExecutionException;
|
||||
import org.springframework.batch.core.step.NoSuchStepException;
|
||||
import org.springframework.batch.core.step.StepLocator;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -37,9 +41,9 @@ import org.springframework.util.ClassUtils;
|
||||
* @author Lucas Ward
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class JobSupport implements BeanNameAware, Job {
|
||||
public class JobSupport implements BeanNameAware, Job, StepLocator {
|
||||
|
||||
private List<Step> steps = new ArrayList<Step>();
|
||||
private Map<String, Step> steps = new HashMap<String, Step>();
|
||||
|
||||
private String name;
|
||||
|
||||
@@ -110,11 +114,13 @@ public class JobSupport implements BeanNameAware, Job {
|
||||
|
||||
public void setSteps(List<Step> steps) {
|
||||
this.steps.clear();
|
||||
this.steps.addAll(steps);
|
||||
for (Step step : steps) {
|
||||
this.steps.put(step.getName(), step);
|
||||
}
|
||||
}
|
||||
|
||||
public void addStep(Step step) {
|
||||
this.steps.add(step);
|
||||
this.steps.put(step.getName(), step);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -172,4 +178,15 @@ public class JobSupport implements BeanNameAware, Job {
|
||||
return jobParametersValidator;
|
||||
}
|
||||
|
||||
public Collection<String> getStepNames() {
|
||||
return steps.keySet();
|
||||
}
|
||||
|
||||
public Step getStep(String stepName) throws NoSuchStepException {
|
||||
final Step step = steps.get(stepName);
|
||||
if (step == null) {
|
||||
throw new NoSuchStepException("Step ["+stepName+"] does not exist for job with name ["+getName()+"]");
|
||||
}
|
||||
return step;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
|
||||
|
||||
<description>
|
||||
Declares two jobs with a set of steps. Also declares two steps that are not attached to any job
|
||||
but could be linked to them through a partition handler configuration for instance.
|
||||
</description>
|
||||
|
||||
<import resource="job-context-with-steps.xml"/>
|
||||
|
||||
<bean id="genericStep1" class="org.springframework.batch.core.step.StepSupport"/>
|
||||
<bean id="genericStep2" class="org.springframework.batch.core.step.StepSupport"/>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
|
||||
|
||||
<bean id="job1"
|
||||
class="org.springframework.batch.core.job.JobSupport">
|
||||
<property name="steps">
|
||||
<list>
|
||||
<bean id="step11" class="org.springframework.batch.core.step.StepSupport"/>
|
||||
<bean id="step12" class="org.springframework.batch.core.step.StepSupport"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="job2"
|
||||
class="org.springframework.batch.core.job.JobSupport">
|
||||
<property name="steps">
|
||||
<list>
|
||||
<bean id="step21" class="org.springframework.batch.core.step.StepSupport"/>
|
||||
<bean id="step22" class="org.springframework.batch.core.step.StepSupport"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -1,12 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
|
||||
|
||||
<bean id="test-job" class="org.springframework.batch.core.job.JobSupport" />
|
||||
<bean id="test-job" class="org.springframework.batch.core.job.JobSupport">
|
||||
<property name="steps">
|
||||
<bean id="test-step"
|
||||
class="org.springframework.batch.core.step.StepSupport"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
|
||||
<property name="jobRegistry" ref="jobRegistry" />
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:tx="http://www.springframework.org/schema/tx"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
|
||||
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
|
||||
|
||||
<bean id="test-job"
|
||||
class="org.springframework.batch.core.job.JobSupport">
|
||||
<property name="steps">
|
||||
<bean id="test-step"
|
||||
class="org.springframework.batch.core.step.StepSupport"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="test-job"
|
||||
class="org.springframework.batch.core.job.JobSupport"/>
|
||||
|
||||
</beans>
|
||||
|
||||
Reference in New Issue
Block a user