View Javadoc

1   /*
2    * Copyright 2006-2007 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.batch.test;
18  
19  import java.util.Date;
20  import java.util.HashMap;
21  import java.util.Map;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.springframework.batch.core.Job;
26  import org.springframework.batch.core.JobExecution;
27  import org.springframework.batch.core.JobParameter;
28  import org.springframework.batch.core.JobParameters;
29  import org.springframework.batch.core.Step;
30  import org.springframework.batch.core.job.AbstractJob;
31  import org.springframework.batch.core.job.SimpleJob;
32  import org.springframework.batch.core.job.flow.FlowJob;
33  import org.springframework.batch.core.launch.JobLauncher;
34  import org.springframework.batch.core.repository.JobRepository;
35  import org.springframework.batch.item.ExecutionContext;
36  import org.springframework.beans.BeansException;
37  import org.springframework.beans.factory.annotation.Autowired;
38  import org.springframework.context.ApplicationContext;
39  import org.springframework.context.ApplicationContextAware;
40  
41  /**
42   * <p>
43   * Base class for testing batch jobs. It provides methods for launching an
44   * entire {@link AbstractJob}, allowing for end to end testing of individual
45   * steps, without having to run every step in the job. Any test classes
46   * inheriting from this class should make sure they are part of an
47   * {@link ApplicationContext}, which is generally expected to be done as part of
48   * the Spring test framework. Furthermore, the {@link ApplicationContext} in
49   * which it is a part of is expected to have one {@link JobLauncher},
50   * {@link JobRepository}, and a single {@link AbstractJob} implementation.
51   * 
52   * <p>
53   * This class also provides the ability to run {@link Step}s from a
54   * {@link FlowJob} or {@link SimpleJob} individually. By launching {@link Step}s
55   * within a {@link Job} on their own, end to end testing of individual steps can
56   * be performed without having to run every step in the job.
57   * 
58   * <p>
59   * It should be noted that using any of the methods that don't contain
60   * {@link JobParameters} in their signature, will result in one being created
61   * with the current system time as a parameter. This will ensure restartability
62   * when no parameters are provided.
63   * 
64   * @author Lucas Ward
65   * @author Dan Garrette
66   * @since 2.0
67   * 
68   * @deprecated (from 2.1) use {@link JobLauncherTestUtils} instead
69   */
70  public abstract class AbstractJobTests implements ApplicationContextAware {
71  
72  	/** Logger */
73  	protected final Log logger = LogFactory.getLog(getClass());
74  
75  	@Autowired
76  	private JobLauncher launcher;
77  
78  	@Autowired
79  	private AbstractJob job;
80  
81  	@Autowired
82  	private JobRepository jobRepository;
83  
84  	private StepRunner stepRunner;
85  
86  	private ApplicationContext applicationContext;
87  
88  	/**
89  	 * {@inheritDoc}
90  	 */
91      @Override
92  	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
93  		this.applicationContext = applicationContext;
94  	}
95  
96  	/**
97  	 * @return the applicationContext
98  	 */
99  	protected ApplicationContext getApplicationContext() {
100 		return applicationContext;
101 	}
102 
103 	/**
104 	 * @return the job repository which is autowired by type
105 	 */
106 	public JobRepository getJobRepository() {
107 		return jobRepository;
108 	}
109 
110 	/**
111 	 * @return the job which is autowired by type
112 	 */
113 	public AbstractJob getJob() {
114 		return job;
115 	}
116 
117 	/**
118 	 * @return the launcher
119 	 */
120 	protected JobLauncher getJobLauncher() {
121 		return launcher;
122 	}
123 
124 	/**
125 	 * Launch the entire job, including all steps.
126 	 * 
127 	 * @return JobExecution, so that the test can validate the exit status
128 	 * @throws Exception
129 	 */
130 	protected JobExecution launchJob() throws Exception {
131 		return this.launchJob(this.getUniqueJobParameters());
132 	}
133 
134 	/**
135 	 * Launch the entire job, including all steps
136 	 * 
137 	 * @param jobParameters
138 	 * @return JobExecution, so that the test can validate the exit status
139 	 * @throws Exception
140 	 */
141 	protected JobExecution launchJob(JobParameters jobParameters) throws Exception {
142 		return getJobLauncher().run(this.job, jobParameters);
143 	}
144 
145 	/**
146 	 * @return a new JobParameters object containing only a parameter for the
147 	 * current timestamp, to ensure that the job instance will be unique.
148 	 */
149 	protected JobParameters getUniqueJobParameters() {
150 		Map<String, JobParameter> parameters = new HashMap<String, JobParameter>();
151 		parameters.put("timestamp", new JobParameter(new Date().getTime()));
152 		return new JobParameters(parameters);
153 	}
154 
155 	/**
156 	 * Convenient method for subclasses to grab a {@link StepRunner} for running
157 	 * steps by name.
158 	 * 
159 	 * @return a {@link StepRunner}
160 	 */
161 	protected StepRunner getStepRunner() {
162 		if (this.stepRunner == null) {
163 			this.stepRunner = new StepRunner(getJobLauncher(), getJobRepository());
164 		}
165 		return this.stepRunner;
166 	}
167 
168 	/**
169 	 * Launch just the specified step in the job. A unique set of JobParameters
170 	 * will automatically be generated. An IllegalStateException is thrown if
171 	 * there is no Step with the given name.
172 	 * 
173 	 * @param stepName The name of the step to launch
174 	 * @return JobExecution
175 	 */
176 	public JobExecution launchStep(String stepName) {
177 		return this.launchStep(stepName, this.getUniqueJobParameters(), null);
178 	}
179 
180 	/**
181 	 * Launch just the specified step in the job. A unique set of JobParameters
182 	 * will automatically be generated. An IllegalStateException is thrown if
183 	 * there is no Step with the given name.
184 	 * 
185 	 * @param stepName The name of the step to launch
186 	 * @param jobExecutionContext An ExecutionContext whose values will be
187 	 * loaded into the Job ExecutionContext prior to launching the step.
188 	 * @return JobExecution
189 	 */
190 	public JobExecution launchStep(String stepName, ExecutionContext jobExecutionContext) {
191 		return this.launchStep(stepName, this.getUniqueJobParameters(), jobExecutionContext);
192 	}
193 
194 	/**
195 	 * Launch just the specified step in the job. An IllegalStateException is
196 	 * thrown if there is no Step with the given name.
197 	 * 
198 	 * @param stepName The name of the step to launch
199 	 * @param jobParameters The JobParameters to use during the launch
200 	 * @return JobExecution
201 	 */
202 	public JobExecution launchStep(String stepName, JobParameters jobParameters) {
203 		return this.launchStep(stepName, jobParameters, null);
204 	}
205 
206 	/**
207 	 * Launch just the specified step in the job. An IllegalStateException is
208 	 * thrown if there is no Step with the given name.
209 	 * 
210 	 * @param stepName The name of the step to launch
211 	 * @param jobParameters The JobParameters to use during the launch
212 	 * @param jobExecutionContext An ExecutionContext whose values will be
213 	 * loaded into the Job ExecutionContext prior to launching the step.
214 	 * @return JobExecution
215 	 */
216 	public JobExecution launchStep(String stepName, JobParameters jobParameters, ExecutionContext jobExecutionContext) {
217 		Step step = this.job.getStep(stepName);
218 		if (step == null) {
219 			step = this.job.getStep(this.job.getName() + "." + stepName);
220 		}
221 		if (step == null) {
222 			throw new IllegalStateException("No Step found with name: [" + stepName + "]");
223 		}
224 		return getStepRunner().launchStep(step, jobParameters, jobExecutionContext);
225 	}
226 }