EMMA Coverage Report (generated Fri Aug 21 15:59:46 BST 2009)
[all classes][org.springframework.batch.core.partition.support]

COVERAGE SUMMARY FOR SOURCE FILE [SimpleStepExecutionSplitter.java]

nameclass, %method, %block, %line, %
SimpleStepExecutionSplitter.java100% (1/1)100% (7/7)97%  (194/199)98%  (44/45)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class SimpleStepExecutionSplitter100% (1/1)100% (7/7)97%  (194/199)98%  (44/45)
shouldStart (Step, StepExecution): boolean 100% (1/1)80%  (20/25)88%  (7/8)
SimpleStepExecutionSplitter (JobRepository, Step): void 100% (1/1)100% (8/8)100% (2/2)
SimpleStepExecutionSplitter (JobRepository, Step, Partitioner): void 100% (1/1)100% (16/16)100% (6/6)
getSplitSize (StepExecution, int): int 100% (1/1)100% (34/34)100% (7/7)
getStartable (StepExecution, ExecutionContext): boolean 100% (1/1)100% (45/45)100% (9/9)
getStepName (): String 100% (1/1)100% (3/3)100% (1/1)
split (StepExecution, int): Set 100% (1/1)100% (68/68)100% (12/12)

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 
17package org.springframework.batch.core.partition.support;
18 
19import java.util.HashSet;
20import java.util.Map;
21import java.util.Set;
22import java.util.Map.Entry;
23 
24import org.springframework.batch.core.BatchStatus;
25import org.springframework.batch.core.JobExecution;
26import org.springframework.batch.core.JobExecutionException;
27import org.springframework.batch.core.JobInstance;
28import org.springframework.batch.core.Step;
29import org.springframework.batch.core.StepExecution;
30import org.springframework.batch.core.partition.StepExecutionSplitter;
31import org.springframework.batch.core.repository.JobRepository;
32import org.springframework.batch.item.ExecutionContext;
33 
34/**
35 * Generic implementation of {@link StepExecutionSplitter} that delegates to a
36 * {@link Partitioner} to generate {@link ExecutionContext} instances. Takes
37 * care of restartability and identifying the step executions from previous runs
38 * of the same job. The generated {@link StepExecution} instances have names
39 * that identify them uniquely in the partition. The name is constructed from a
40 * base (name of the target step) plus a suffix taken from the
41 * {@link Partitioner} identifiers, separated by a colon, e.g.
42 * <code>{step1:partition0, step1:partition1, ...}</code>.
43 * 
44 * @author Dave Syer
45 * @since 2.0
46 */
47public class SimpleStepExecutionSplitter implements StepExecutionSplitter {
48 
49        private static final String STEP_NAME_SEPARATOR = ":";
50 
51        private final String stepName;
52 
53        private final Partitioner partitioner;
54 
55        private final Step step;
56 
57        private final JobRepository jobRepository;
58 
59        public SimpleStepExecutionSplitter(JobRepository jobRepository, Step step) {
60                this(jobRepository, step, new SimplePartitioner());
61        }
62 
63        /**
64         * Construct a {@link SimpleStepExecutionSplitter} from its mandatory
65         * properties.
66         * 
67         * @param jobRepository the {@link JobRepository}
68         * @param step the target step (a local version of it)
69         * @param partitioner a {@link Partitioner} to use for generating input
70         * parameters
71         */
72        public SimpleStepExecutionSplitter(JobRepository jobRepository, Step step, Partitioner partitioner) {
73                this.jobRepository = jobRepository;
74                this.step = step;
75                this.partitioner = partitioner;
76                this.stepName = step.getName();
77        }
78 
79        /**
80         * @see StepExecutionSplitter#getStepName()
81         */
82        public String getStepName() {
83                return this.stepName;
84        }
85 
86        /**
87         * @see StepExecutionSplitter#split(StepExecution, int)
88         */
89        public Set<StepExecution> split(StepExecution stepExecution, int gridSize) throws JobExecutionException {
90 
91                JobExecution jobExecution = stepExecution.getJobExecution();
92 
93                // If this is a restart we must retain the same grid size, ignoring the
94                // one passed in...
95                int splitSize = getSplitSize(stepExecution, gridSize);
96 
97                Map<String, ExecutionContext> contexts = partitioner.partition(splitSize);
98                Set<StepExecution> set = new HashSet<StepExecution>(contexts.size());
99 
100                for (Entry<String, ExecutionContext> context : contexts.entrySet()) {
101 
102                        // Make the step execution name unique and repeatable
103                        String stepName = this.stepName + STEP_NAME_SEPARATOR + context.getKey();
104 
105                        StepExecution currentStepExecution = jobExecution.createStepExecution(stepName);
106 
107                        boolean startable = getStartable(currentStepExecution, context.getValue());
108 
109                        if (startable) {
110                                jobRepository.add(currentStepExecution);
111                                set.add(currentStepExecution);
112                        }
113 
114                }
115 
116                return set;
117 
118        }
119 
120        private int getSplitSize(StepExecution stepExecution, int gridSize) {
121                ExecutionContext context = stepExecution.getExecutionContext();
122                String key = SimpleStepExecutionSplitter.class.getSimpleName() + ".GRID_SIZE";
123                int result = (int) context.getLong(key, gridSize);
124                context.putLong(key, result);
125                if (context.isDirty()) {
126                        jobRepository.updateExecutionContext(stepExecution);
127                }
128                return result;
129        }
130 
131        private boolean getStartable(StepExecution stepExecution, ExecutionContext context) throws JobExecutionException {
132 
133                JobInstance jobInstance = stepExecution.getJobExecution().getJobInstance();
134                String stepName = stepExecution.getStepName();
135                StepExecution lastStepExecution = jobRepository.getLastStepExecution(jobInstance, stepName);
136 
137                boolean isRestart = (lastStepExecution != null && lastStepExecution.getStatus() != BatchStatus.COMPLETED) ? true
138                                : false;
139 
140                if (isRestart) {
141                        stepExecution.setExecutionContext(lastStepExecution.getExecutionContext());
142                }
143                else {
144                        stepExecution.setExecutionContext(context);
145                }
146 
147                return shouldStart(step, lastStepExecution) || isRestart;
148 
149        }
150 
151        private boolean shouldStart(Step step, StepExecution lastStepExecution) throws JobExecutionException {
152 
153                if (lastStepExecution == null) {
154                        return true;
155                }
156 
157                BatchStatus stepStatus = lastStepExecution.getStatus();
158 
159                if (stepStatus == BatchStatus.UNKNOWN) {
160                        throw new JobExecutionException("Cannot restart step from UNKNOWN status.  "
161                                        + "The last execution ended with a failure that could not be rolled back, "
162                                        + "so it may be dangerous to proceed.  " + "Manual intervention is probably necessary.");
163                }
164 
165                if (stepStatus == BatchStatus.COMPLETED && step.isAllowStartIfComplete() == false) {
166                        // step is complete, false should be returned, indicating that the
167                        // step should not be started
168                        return false;
169                }
170 
171                return true;
172 
173        }
174 
175}

[all classes][org.springframework.batch.core.partition.support]
EMMA 2.0.5312 (C) Vladimir Roubtsov