From 9f9282a2a461b234532c5ac6feb77191871d868d Mon Sep 17 00:00:00 2001 From: robokaso Date: Tue, 27 Jan 2009 17:42:45 +0000 Subject: [PATCH] RESOLVED - BATCH-1032: Modify AbstractJobTests in test project to be able to launch FlowJob steps individually applied patch with some tweaks --- .../batch/core/job/flow/FlowJob.java | 321 +++++++++-------- .../core/job/flow/support/SimpleFlow.java | 8 + .../job/flow/support/state/StepState.java | 118 +++--- spring-batch-test/.springBeans | 5 +- .../batch/test/AbstractJobTests.java | 335 ++++++++++++------ .../batch/test/AbstractSimpleJobTests.java | 101 ------ ...Tests.java => AbstractSampleJobTests.java} | 130 +++---- .../batch/test/SampleFlowJobTests.java | 19 + .../batch/test/SampleSimpleJobTests.java | 19 + .../batch/test/SampleStepTests.java | 120 +++---- .../jobs/{sampleJob.xml => sample-steps.xml} | 57 ++- .../src/test/resources/jobs/sampleFlowJob.xml | 22 ++ .../test/resources/jobs/sampleSimpleJob.xml | 21 ++ 13 files changed, 693 insertions(+), 583 deletions(-) delete mode 100755 spring-batch-test/src/main/java/org/springframework/batch/test/AbstractSimpleJobTests.java rename spring-batch-test/src/test/java/org/springframework/batch/test/{SampleJobTests.java => AbstractSampleJobTests.java} (71%) mode change 100755 => 100644 create mode 100644 spring-batch-test/src/test/java/org/springframework/batch/test/SampleFlowJobTests.java create mode 100644 spring-batch-test/src/test/java/org/springframework/batch/test/SampleSimpleJobTests.java rename spring-batch-test/src/test/resources/jobs/{sampleJob.xml => sample-steps.xml} (72%) mode change 100755 => 100644 create mode 100644 spring-batch-test/src/test/resources/jobs/sampleFlowJob.xml create mode 100644 spring-batch-test/src/test/resources/jobs/sampleSimpleJob.xml diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/FlowJob.java b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/FlowJob.java index 6af1341cb..48f797b5c 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/FlowJob.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/FlowJob.java @@ -1,157 +1,164 @@ -/* - * 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.core.job.flow; - -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.JobExecutionException; -import org.springframework.batch.core.JobInterruptedException; -import org.springframework.batch.core.StartLimitExceededException; -import org.springframework.batch.core.Step; -import org.springframework.batch.core.StepExecution; -import org.springframework.batch.core.job.AbstractJob; -import org.springframework.batch.core.repository.JobRestartException; -import org.springframework.util.Assert; - - -/** - * @author Dave Syer - * - */ -public class FlowJob extends AbstractJob { - - private Flow flow; - - /** - * Create a {@link FlowJob} with null name and no flow (invalid state). - */ - public FlowJob() { - super(); - } - - /** - * Create a {@link FlowJob} with provided name and no flow (invalid state). - */ - public FlowJob(String name) { - super(name); - } - - /** - * Public setter for the flow. - * @param flow the flow to set - */ - public void setFlow(Flow flow) { - this.flow = flow; - } - - /** - * @see AbstractJob#doExecute(JobExecution) - */ - @Override - protected StepExecution doExecute(final JobExecution execution) throws JobExecutionException { - try { - FlowExecution result = flow.start(new JobFlowExecutor(execution)); - return getLastStepExecution(execution, result); - } - catch (FlowExecutionException e) { - if (e.getCause() instanceof JobExecutionException) { - throw (JobExecutionException) e.getCause(); - } - throw new JobExecutionException("Flow execution ended unexpectedly", e); - } - } - - /** - * @param execution the current {@link JobExecution} - * @param result the result of the flow execution - * @return a {@link StepExecution} with matching properties to the result - */ - private StepExecution getLastStepExecution(JobExecution execution, FlowExecution result) { - StepExecution value = null; - StepExecution backup = null; - for (StepExecution stepExecution : execution.getStepExecutions()) { - if (stepExecution.getStepName().equals(result.getName()) - && stepExecution.getExitStatus().getExitCode().equals(result.getStatus())) { - value = stepExecution; - } - if (isLater(backup,stepExecution)) { - backup = stepExecution; - } - } - if (value==null) { - value = backup; - } - Assert.state(value != null, String.format( - "Could not locate step execution matching expected properties: flowExecution=%s, stepExecutions=%s", - result, execution.getStepExecutions())); - return value; - } - - /** - * @param first - * @param second - * @return true if the first is deemed to be executed after the second - */ - private boolean isLater(StepExecution first, StepExecution second) { - if (first==null) { - return true; - } - if (first.getEndTime()==null) { - return first.getStartTime().after(second.getStartTime()); - } - if (second.getEndTime()==null) { - return false; - } - return first.getEndTime().after(second.getEndTime()); - } - - /** - * @author Dave Syer - * - */ - private class JobFlowExecutor implements FlowExecutor { - - private final ThreadLocal stepExecutionHolder = new ThreadLocal(); - private final JobExecution execution; - - /** - * @param execution - */ - private JobFlowExecutor(JobExecution execution) { - this.execution = execution; - stepExecutionHolder.set(null); - } - - public String executeStep(Step step) throws JobInterruptedException, JobRestartException, StartLimitExceededException { - StepExecution stepExecution = handleStep(step, execution); - stepExecutionHolder.set(stepExecution); - return stepExecution==null ? FlowExecution.COMPLETED : stepExecution.getExitStatus().getExitCode(); - } - - public JobExecution getJobExecution() { - return execution; - } - - public StepExecution getStepExecution() { - return stepExecutionHolder.get(); - } - - public void close(FlowExecution result) { - stepExecutionHolder.set(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.core.job.flow; + +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobExecutionException; +import org.springframework.batch.core.JobInterruptedException; +import org.springframework.batch.core.StartLimitExceededException; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.StepExecution; +import org.springframework.batch.core.job.AbstractJob; +import org.springframework.batch.core.repository.JobRestartException; +import org.springframework.util.Assert; + + +/** + * @author Dave Syer + * + */ +public class FlowJob extends AbstractJob { + + private Flow flow; + + /** + * Create a {@link FlowJob} with null name and no flow (invalid state). + */ + public FlowJob() { + super(); + } + + /** + * Create a {@link FlowJob} with provided name and no flow (invalid state). + */ + public FlowJob(String name) { + super(name); + } + + /** + * Public setter for the flow. + * @param flow the flow to set + */ + public void setFlow(Flow flow) { + this.flow = flow; + } + + /** + * @return the flow + */ + public Flow getFlow(){ + return this.flow; + } + + /** + * @see AbstractJob#doExecute(JobExecution) + */ + @Override + protected StepExecution doExecute(final JobExecution execution) throws JobExecutionException { + try { + FlowExecution result = flow.start(new JobFlowExecutor(execution)); + return getLastStepExecution(execution, result); + } + catch (FlowExecutionException e) { + if (e.getCause() instanceof JobExecutionException) { + throw (JobExecutionException) e.getCause(); + } + throw new JobExecutionException("Flow execution ended unexpectedly", e); + } + } + + /** + * @param execution the current {@link JobExecution} + * @param result the result of the flow execution + * @return a {@link StepExecution} with matching properties to the result + */ + private StepExecution getLastStepExecution(JobExecution execution, FlowExecution result) { + StepExecution value = null; + StepExecution backup = null; + for (StepExecution stepExecution : execution.getStepExecutions()) { + if (stepExecution.getStepName().equals(result.getName()) + && stepExecution.getExitStatus().getExitCode().equals(result.getStatus())) { + value = stepExecution; + } + if (isLater(backup,stepExecution)) { + backup = stepExecution; + } + } + if (value==null) { + value = backup; + } + Assert.state(value != null, String.format( + "Could not locate step execution matching expected properties: flowExecution=%s, stepExecutions=%s", + result, execution.getStepExecutions())); + return value; + } + + /** + * @param first + * @param second + * @return true if the first is deemed to be executed after the second + */ + private boolean isLater(StepExecution first, StepExecution second) { + if (first==null) { + return true; + } + if (first.getEndTime()==null) { + return first.getStartTime().after(second.getStartTime()); + } + if (second.getEndTime()==null) { + return false; + } + return first.getEndTime().after(second.getEndTime()); + } + + /** + * @author Dave Syer + * + */ + private class JobFlowExecutor implements FlowExecutor { + + private final ThreadLocal stepExecutionHolder = new ThreadLocal(); + private final JobExecution execution; + + /** + * @param execution + */ + private JobFlowExecutor(JobExecution execution) { + this.execution = execution; + stepExecutionHolder.set(null); + } + + public String executeStep(Step step) throws JobInterruptedException, JobRestartException, StartLimitExceededException { + StepExecution stepExecution = handleStep(step, execution); + stepExecutionHolder.set(stepExecution); + return stepExecution==null ? FlowExecution.COMPLETED : stepExecution.getExitStatus().getExitCode(); + } + + public JobExecution getJobExecution() { + return execution; + } + + public StepExecution getStepExecution() { + return stepExecutionHolder.get(); + } + + public void close(FlowExecution result) { + stepExecutionHolder.set(null); + } + + } + +} diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/SimpleFlow.java b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/SimpleFlow.java index 6f062db4f..dbcb2b580 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/SimpleFlow.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/SimpleFlow.java @@ -81,6 +81,14 @@ public class SimpleFlow implements Flow, InitializingBean { this.stateTransitions = stateTransitions; } + /** + * @param stateName + * @return state with given name, null if not found + */ + public State getState(String stateName) { + return stateMap.get(stateName); + } + /** * Locate start state and pre-populate data structures needed for execution. * diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/state/StepState.java b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/state/StepState.java index d453d397f..90c2ef781 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/state/StepState.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/job/flow/support/state/StepState.java @@ -1,56 +1,62 @@ -/* - * 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.core.job.flow.support.state; - -import org.springframework.batch.core.Step; -import org.springframework.batch.core.job.flow.FlowExecutor; -import org.springframework.batch.core.job.flow.support.State; - -/** - * {@link State} implementation that delegates to a {@link FlowExecutor} to - * execute the specified {@link Step}. - * - * @author Dave Syer - * - */ -public class StepState extends AbstractState { - - private final Step step; - - /** - * @param step the step that will be executed - */ - public StepState(Step step) { - super(step.getName()); - this.step = step; - } - - /** - * @param name for the step that will be executed - * @param step the step that will be executed - */ - public StepState(String name, Step step) { - super(name); - this.step = step; - } - - @Override - public String handle(FlowExecutor executor) throws Exception { - return executor.executeStep(step); - } - -} \ No newline at end of file +/* + * 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.core.job.flow.support.state; + +import org.springframework.batch.core.Step; +import org.springframework.batch.core.job.flow.FlowExecutor; +import org.springframework.batch.core.job.flow.support.State; + +/** + * {@link State} implementation that delegates to a {@link FlowExecutor} to + * execute the specified {@link Step}. + * + * @author Dave Syer + * + */ +public class StepState extends AbstractState { + + private final Step step; + + /** + * @param step the step that will be executed + */ + public StepState(Step step) { + super(step.getName()); + this.step = step; + } + + /** + * @param name for the step that will be executed + * @param step the step that will be executed + */ + public StepState(String name, Step step) { + super(name); + this.step = step; + } + + @Override + public String handle(FlowExecutor executor) throws Exception { + return executor.executeStep(step); + } + + /** + * @return the step + */ + public Step getStep() { + return step; + } +} diff --git a/spring-batch-test/.springBeans b/spring-batch-test/.springBeans index 43f05bb98..767d5a4d1 100644 --- a/spring-batch-test/.springBeans +++ b/spring-batch-test/.springBeans @@ -9,8 +9,10 @@ src/test/resources/data-source-context.xml src/test/resources/simple-job-launcher-context.xml - src/test/resources/jobs/sampleJob.xml src/test/resources/org/springframework/batch/sample/config/common-context.xml + src/test/resources/jobs/sampleFlowJob.xml + src/test/resources/jobs/sampleSimpleJob.xml + src/test/resources/jobs/sample-steps.xml @@ -19,7 +21,6 @@ false src/test/resources/data-source-context.xml - src/test/resources/jobs/sampleJob.xml src/test/resources/org/springframework/batch/sample/config/common-context.xml src/test/resources/simple-job-launcher-context.xml diff --git a/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractJobTests.java b/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractJobTests.java index cc3e654cf..5817205c4 100644 --- a/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractJobTests.java +++ b/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractJobTests.java @@ -1,111 +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.test; - -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.batch.core.Job; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.JobParameter; -import org.springframework.batch.core.JobParameters; -import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.batch.core.repository.JobRepository; -import org.springframework.beans.factory.annotation.Autowired; - -/** - * Base class for testing batch jobs using the SimpleJob implementation. It - * provides methods for launching a Job, or individual Steps within a Job on - * their own, allowing for end to end testing of individual steps, without - * having to run every step in the job. Any test classes inheriting from this - * class should make sure they are part of an ApplicationContext, which is - * generally expected to be done as part of the Spring test framework. - * Furthermore, the ApplicationContext in which it is a part of is expected to - * have one {@link JobLauncher}, {@link JobRepository}, and a single Job - * implementation. It should be noted that using any of the methods that don't - * conain {@link JobParameters} in their signature, will result in one being - * created with the current system time as a parameter. - * - * @author Lucas Ward - * @author Dan Garrette - * @since 2.0 - */ -public abstract class AbstractJobTests { - - /** Logger */ - protected final Log logger = LogFactory.getLog(getClass()); - - @Autowired - private JobLauncher launcher; - - @Autowired - private Job job; - - @Autowired - private JobRepository jobRepository; - - public JobRepository getJobRepository() { - return jobRepository; - } - - public Job getJob() { - return job; - } - - /** - * Public getter for the launcher. - * - * @return the launcher - */ - protected JobLauncher getJobLauncher() { - return launcher; - } - - /** - * Launch the entire job, including all steps, in order. - * - * @return JobExecution, so that the test may validate the exit status - * @throws Exception - */ - public JobExecution launchJob() throws Exception { - return this.launchJob(this.makeUniqueJobParameters()); - } - - /** - * Launch the entire job, including all steps, in order. - * - * @param jobParameters - * @return JobExecution, so that the test may validate the exit status - * @throws Exception - */ - public JobExecution launchJob(JobParameters jobParameters) throws Exception { - return getJobLauncher().run(this.job, jobParameters); - } - - /** - * @return a new JobParameters object containing only a parameter for the - * current timestamp, to ensure that the job instance will be unique - */ - private JobParameters makeUniqueJobParameters() { - Map parameters = new HashMap(); - parameters.put("timestamp", new JobParameter(new Date().getTime())); - return new JobParameters(parameters); - } -} +/* + * 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.test; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.batch.core.Job; +import org.springframework.batch.core.JobExecution; +import org.springframework.batch.core.JobParameter; +import org.springframework.batch.core.JobParameters; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.job.SimpleJob; +import org.springframework.batch.core.job.flow.FlowJob; +import org.springframework.batch.core.job.flow.support.SimpleFlow; +import org.springframework.batch.core.job.flow.support.State; +import org.springframework.batch.core.job.flow.support.state.StepState; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.util.Assert; + +/** + *

+ * Base class for testing batch jobs. It provides methods for launching an + * entire {@link Job}, allowing for end to end testing of individual steps, + * without having to run every step in the job. Any test classes inheriting from + * this class should make sure they are part of an {@link ApplicationContext}, + * which is generally expected to be done as part of the Spring test framework. + * Furthermore, the {@link ApplicationContext} in which it is a part of is + * expected to have one {@link JobLauncher}, {@link JobRepository}, and a + * single {@link Job} implementation. + * + *

+ * This class also provides the ability to run {@link Step}s from a + * {@link FlowJob} or {@link SimpleJob} individually. By launching {@link Step}s + * within a {@link Job} on their own, end to end testing of individual steps can + * be performed without having to run every step in the job. + * + *

+ * It should be noted that using any of the methods that don't contain + * {@link JobParameters} in their signature, will result in one being created + * with the current system time as a parameter. This will ensure restartability + * when no parameters are provided. + * + * @author Lucas Ward + * @author Dan Garrette + * @since 2.0 + */ +public abstract class AbstractJobTests { + + /** Logger */ + protected final Log logger = LogFactory.getLog(getClass()); + + @Autowired + private JobLauncher launcher; + + @Autowired + private Job job; + + @Autowired + private JobRepository jobRepository; + + private StepRunner stepRunner; + private Map stepMap; + + /** + * @return the job repository + */ + public JobRepository getJobRepository() { + return jobRepository; + } + + /** + * @return the job + */ + public Job getJob() { + return job; + } + + /** + * @return the launcher + */ + protected JobLauncher getJobLauncher() { + return launcher; + } + + /** + * Launch the entire job, including all steps. + * + * @return JobExecution, so that the test may validate the exit status + * @throws Exception + */ + public JobExecution launchJob() throws Exception { + return this.launchJob(this.makeUniqueJobParameters()); + } + + /** + * Launch the entire job, including all steps + * + * @param jobParameters + * @return JobExecution, so that the test may validate the exit status + * @throws Exception + */ + public JobExecution launchJob(JobParameters jobParameters) throws Exception { + return getJobLauncher().run(this.job, jobParameters); + } + + /** + * @return a new JobParameters object containing only a parameter for the + * current timestamp, to ensure that the job instance will be unique + */ + private JobParameters makeUniqueJobParameters() { + Map parameters = new HashMap(); + parameters.put("timestamp", new JobParameter(new Date().getTime())); + return new JobParameters(parameters); + } + + protected StepRunner getStepRunner() { + if (this.stepRunner == null) { + this.stepRunner = new StepRunner(getJobLauncher(), getJobRepository()); + } + return this.stepRunner; + } + + /** + * Launch just the specified step in the job. + * + * @param stepName + */ + public JobExecution launchStep(String stepName) { + return getStepRunner().launchStep(getStep(stepName)); + } + + /** + * @param stepName + * @return + */ + public Step getStep(String stepName) { + Job job = getJob(); + if (job instanceof FlowJob) { + return getFlowJobStep(stepName); + } + else if (job instanceof SimpleJob) { + return getSimpleJobStep(stepName); + } + else { + throw new IllegalStateException("Job is neither a FlowJob or a SimpleJob"); + } + } + + /** + * Extract the step from a FlowJob. Throw an exception of the step does not + * exist. + * + * @param stepName + * @return the step + */ + private Step getFlowJobStep(String stepName) { + try{ + State state = ((SimpleFlow) ((FlowJob) getJob()).getFlow()).getState(stepName); + Assert.notNull(state, "no matching state found in flow for " + stepName); + Assert.isInstanceOf(StepState.class, state); + return ((StepState) state).getStep(); + } + catch(IllegalArgumentException e) + { + throw new IllegalStateException("No Step found with name: [" + stepName + "]"); + } + } + + /** + * Extract the step from a SimpleJob. Throw an exception of the step does + * not exist. + * + * @param stepName + * @return the step + */ + private Step getSimpleJobStep(String stepName) { + if (this.stepMap == null) { + // + // Populate the step map + // + SimpleJob simpleJob = (SimpleJob) getJob(); + this.stepMap = new HashMap(); + for (Step step : simpleJob.getSteps()) { + this.stepMap.put(step.getName(), step); + } + } + + if (!this.stepMap.containsKey(stepName)) { + throw new IllegalStateException("No Step found with name: [" + stepName + "]"); + } + return this.stepMap.get(stepName); + } + + /** + * Launch just the specified step in the job. + * + * @param stepName + * @param jobParameters + */ + public JobExecution launchStep(String stepName, JobParameters jobParameters) { + return getStepRunner().launchStep(getStep(stepName), jobParameters); + } +} diff --git a/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractSimpleJobTests.java b/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractSimpleJobTests.java deleted file mode 100755 index c49b4d022..000000000 --- a/spring-batch-test/src/main/java/org/springframework/batch/test/AbstractSimpleJobTests.java +++ /dev/null @@ -1,101 +0,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.test; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Before; -import org.springframework.batch.core.JobExecution; -import org.springframework.batch.core.JobParameters; -import org.springframework.batch.core.Step; -import org.springframework.batch.core.job.SimpleJob; -import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.batch.core.repository.JobRepository; - -/** - * Base class for testing batch jobs using the SimpleJob implementation. - * It provides methods for launching a Job, or individual Steps within a Job on their own, - * allowing for end to end testing of individual steps, without having to run every step - * in the job. Any test classes inheriting from this class should make sure they are part - * of an ApplicationContext, which is generally expected to be done as part of the Spring - * test framework. Furthermore, the ApplicationContext in which it is a part of is expected - * to have one {@link JobLauncher}, {@link JobRepository}, and a single Job implementation. - * It should be noted that using any of the methods that don't conain {@link JobParameters} - * in their signature, will result in one being created with the current system time as a - * parameter. - * - * @author Lucas Ward - * @author Dan Garrette - * @since 2.0 - */ -public abstract class AbstractSimpleJobTests extends AbstractJobTests { - - private StepRunner stepRunner; - - private Map stepMap = new HashMap(); - private List stepList = new ArrayList(); - - @Before - public void setUpSteps() { - for (Step step : (getSimpleJob()).getSteps()) { - stepMap.put(step.getName(), step); - stepList.add(step); - } - } - - protected StepRunner getStepRunner() { - if(stepRunner == null){ - stepRunner = new StepRunner(getJobLauncher(), getJobRepository()); - } - return stepRunner; - } - - public SimpleJob getSimpleJob() { - return (SimpleJob)getJob(); - } - - public Step getStep(String stepName){ - - if(!stepMap.containsKey(stepName)){ - throw new IllegalStateException("No Step found with name: [" + stepName + "]"); - } - return stepMap.get(stepName); - } - - /** - * Launch just the specified step in the job. - * - * @param stepName - */ - public JobExecution launchStep(String stepName) { - return getStepRunner().launchStep(getStep(stepName)); - } - - /** - * Launch just the specified step in the job. - * - * @param stepName - * @param jobParameters - */ - public JobExecution launchStep(String stepName, JobParameters jobParameters) { - return getStepRunner().launchStep(getStep(stepName), jobParameters); - } - -} diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/SampleJobTests.java b/spring-batch-test/src/test/java/org/springframework/batch/test/AbstractSampleJobTests.java old mode 100755 new mode 100644 similarity index 71% rename from spring-batch-test/src/test/java/org/springframework/batch/test/SampleJobTests.java rename to spring-batch-test/src/test/java/org/springframework/batch/test/AbstractSampleJobTests.java index 5f8daf1fe..b7f97e8ee --- a/spring-batch-test/src/test/java/org/springframework/batch/test/SampleJobTests.java +++ b/spring-batch-test/src/test/java/org/springframework/batch/test/AbstractSampleJobTests.java @@ -1,64 +1,66 @@ -package org.springframework.batch.test; - -import static org.junit.Assert.assertEquals; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.batch.core.BatchStatus; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/jobs/sampleJob.xml" }) -public class SampleJobTests extends AbstractSimpleJobTests { - - private SimpleJdbcTemplate jdbcTemplate; - - @Autowired - public void setJdbcTemplate(SimpleJdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - @Before - public void setUp() { - this.jdbcTemplate.update("create table TESTS (ID integer, NAME varchar(40))"); - } - - @After - public void tearDown() { - this.jdbcTemplate.update("drop table TESTS"); - } - - @Test - public void testJob() throws Exception { - assertEquals(BatchStatus.COMPLETED,this.launchJob().getStatus()); - this.verifyTasklet(1); - this.verifyTasklet(2); - } - - @Test(expected=IllegalStateException.class) - public void voidTestNonExistentStep(){ - launchStep("nonExistent"); - } - - @Test - public void testStep1Execution() { - assertEquals(BatchStatus.COMPLETED, this.launchStep("step1").getStatus()); - this.verifyTasklet(1); - } - - @Test - public void testStep2Execution() { - assertEquals(BatchStatus.COMPLETED, this.launchStep("step2").getStatus()); - this.verifyTasklet(2); - } - - private void verifyTasklet(int id) { - assertEquals(id, jdbcTemplate.queryForInt("SELECT ID from TESTS where NAME = 'SampleTasklet" + id + "'")); - } - -} +package org.springframework.batch.test; + +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.batch.core.BatchStatus; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; + +/** + * This is an abstract test class to be used by test classes to test the + * {@link AbstractJobTests} class. + * + * @author Dan Garrette + * @since 2.0 + */ +public abstract class AbstractSampleJobTests extends AbstractJobTests { + + private SimpleJdbcTemplate jdbcTemplate; + + @Autowired + public void setJdbcTemplate(SimpleJdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Before + public void setUp() { + this.jdbcTemplate.update("create table TESTS (ID integer, NAME varchar(40))"); + } + + @After + public void tearDown() { + this.jdbcTemplate.update("drop table TESTS"); + } + + @Test + public void testJob() throws Exception { + assertEquals(BatchStatus.COMPLETED, this.launchJob().getStatus()); + this.verifyTasklet(1); + this.verifyTasklet(2); + } + + @Test(expected = IllegalStateException.class) + public void testNonExistentStep() { + launchStep("nonExistent"); + } + + @Test + public void testStep1Execution() { + assertEquals(BatchStatus.COMPLETED, this.launchStep("step1").getStatus()); + this.verifyTasklet(1); + } + + @Test + public void testStep2Execution() { + assertEquals(BatchStatus.COMPLETED, this.launchStep("step2").getStatus()); + this.verifyTasklet(2); + } + + private void verifyTasklet(int id) { + assertEquals(id, jdbcTemplate.queryForInt("SELECT ID from TESTS where NAME = 'SampleTasklet" + id + "'")); + } + +} diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/SampleFlowJobTests.java b/spring-batch-test/src/test/java/org/springframework/batch/test/SampleFlowJobTests.java new file mode 100644 index 000000000..4ccaaa5cf --- /dev/null +++ b/spring-batch-test/src/test/java/org/springframework/batch/test/SampleFlowJobTests.java @@ -0,0 +1,19 @@ +package org.springframework.batch.test; + +import org.junit.runner.RunWith; +import org.springframework.batch.core.job.flow.FlowJob; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * This class will specifically test the capabilities of + * {@link AbstractSampleJobTests} to test {@link FlowJob}s. + * + * @author Dan Garrette + * @since 2.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/jobs/sampleFlowJob.xml" }) +public class SampleFlowJobTests extends AbstractSampleJobTests { + +} diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/SampleSimpleJobTests.java b/spring-batch-test/src/test/java/org/springframework/batch/test/SampleSimpleJobTests.java new file mode 100644 index 000000000..24eedd98a --- /dev/null +++ b/spring-batch-test/src/test/java/org/springframework/batch/test/SampleSimpleJobTests.java @@ -0,0 +1,19 @@ +package org.springframework.batch.test; + +import org.junit.runner.RunWith; +import org.springframework.batch.core.job.SimpleJob; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * This class will specifically test the capabilities of + * {@link AbstractSampleJobTests} to test {@link SimpleJob}s. + * + * @author Dan Garrette + * @since 2.0 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/jobs/sampleSimpleJob.xml" }) +public class SampleSimpleJobTests extends AbstractSampleJobTests { + +} diff --git a/spring-batch-test/src/test/java/org/springframework/batch/test/SampleStepTests.java b/spring-batch-test/src/test/java/org/springframework/batch/test/SampleStepTests.java index 3af16a91c..bc4f37414 100755 --- a/spring-batch-test/src/test/java/org/springframework/batch/test/SampleStepTests.java +++ b/spring-batch-test/src/test/java/org/springframework/batch/test/SampleStepTests.java @@ -1,60 +1,60 @@ -package org.springframework.batch.test; - -import static org.junit.Assert.*; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.batch.core.BatchStatus; -import org.springframework.batch.core.Step; -import org.springframework.batch.core.launch.JobLauncher; -import org.springframework.batch.core.repository.JobRepository; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/jobs/sampleJob.xml" }) -public class SampleStepTests implements ApplicationContextAware{ - - @Autowired - private SimpleJdbcTemplate jdbcTemplate; - - private StepRunner stepRunner; - private ApplicationContext context; - - @Autowired - private JobLauncher jobLauncher; - - @Autowired - private JobRepository jobRepository; - - @Before - public void setUp() { - jdbcTemplate.update("create table TESTS (ID integer, NAME varchar(40))"); - stepRunner = new StepRunner(jobLauncher, jobRepository); - } - - @After - public void tearDown() { - this.jdbcTemplate.update("drop table TESTS"); - } - - @Test - public void testTasklet() { - Step step = (Step)context.getBean("step2"); - assertEquals(BatchStatus.COMPLETED, stepRunner.launchStep(step).getStatus()); - assertEquals(2, jdbcTemplate.queryForInt("SELECT ID from TESTS where NAME = 'SampleTasklet2'")); - } - - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { - this.context = applicationContext; - } - -} +package org.springframework.batch.test; + +import static org.junit.Assert.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.batch.core.BatchStatus; +import org.springframework.batch.core.Step; +import org.springframework.batch.core.launch.JobLauncher; +import org.springframework.batch.core.repository.JobRepository; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { "/simple-job-launcher-context.xml", "/jobs/sample-steps.xml" }) +public class SampleStepTests implements ApplicationContextAware{ + + @Autowired + private SimpleJdbcTemplate jdbcTemplate; + + private StepRunner stepRunner; + private ApplicationContext context; + + @Autowired + private JobLauncher jobLauncher; + + @Autowired + private JobRepository jobRepository; + + @Before + public void setUp() { + jdbcTemplate.update("create table TESTS (ID integer, NAME varchar(40))"); + stepRunner = new StepRunner(jobLauncher, jobRepository); + } + + @After + public void tearDown() { + this.jdbcTemplate.update("drop table TESTS"); + } + + @Test + public void testTasklet() { + Step step = (Step)context.getBean("step2"); + assertEquals(BatchStatus.COMPLETED, stepRunner.launchStep(step).getStatus()); + assertEquals(2, jdbcTemplate.queryForInt("SELECT ID from TESTS where NAME = 'SampleTasklet2'")); + } + + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.context = applicationContext; + } + +} diff --git a/spring-batch-test/src/test/resources/jobs/sampleJob.xml b/spring-batch-test/src/test/resources/jobs/sample-steps.xml old mode 100755 new mode 100644 similarity index 72% rename from spring-batch-test/src/test/resources/jobs/sampleJob.xml rename to spring-batch-test/src/test/resources/jobs/sample-steps.xml index e569d7db3..67460d000 --- a/spring-batch-test/src/test/resources/jobs/sampleJob.xml +++ b/spring-batch-test/src/test/resources/jobs/sample-steps.xml @@ -1,33 +1,26 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/spring-batch-test/src/test/resources/jobs/sampleFlowJob.xml b/spring-batch-test/src/test/resources/jobs/sampleFlowJob.xml new file mode 100644 index 000000000..255920c88 --- /dev/null +++ b/spring-batch-test/src/test/resources/jobs/sampleFlowJob.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/spring-batch-test/src/test/resources/jobs/sampleSimpleJob.xml b/spring-batch-test/src/test/resources/jobs/sampleSimpleJob.xml new file mode 100644 index 000000000..613251c65 --- /dev/null +++ b/spring-batch-test/src/test/resources/jobs/sampleSimpleJob.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file