EMMA Coverage Report (generated Fri Jan 30 13:20:29 EST 2009)
[all classes][org.springframework.batch.core.repository.dao]

COVERAGE SUMMARY FOR SOURCE FILE [JdbcStepExecutionDao.java]

nameclass, %method, %block, %line, %
JdbcStepExecutionDao.java100% (2/2)93%  (14/15)99%  (645/651)97%  (71.9/74)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JdbcStepExecutionDao100% (1/1)92%  (12/13)99%  (553/559)96%  (55.9/58)
setLobHandler (LobHandler): void 0%   (0/1)0%   (0/4)0%   (0/2)
<static initializer> 100% (1/1)91%  (10/11)90%  (0.9/1)
getStepExecution (JobExecution, Step): StepExecution 100% (1/1)98%  (43/44)99%  (5/5)
JdbcStepExecutionDao (): void 100% (1/1)100% (16/16)100% (5/5)
afterPropertiesSet (): void 100% (1/1)100% (25/25)100% (7/7)
findExecutionContext (StepExecution): ExecutionContext 100% (1/1)100% (5/5)100% (1/1)
saveOrUpdateExecutionContext (StepExecution): void 100% (1/1)100% (5/5)100% (2/2)
saveStepExecution (StepExecution): void 100% (1/1)100% (182/182)100% (9/9)
setExitMessageLength (int): void 100% (1/1)100% (4/4)100% (2/2)
setStepExecutionIncrementer (DataFieldMaxValueIncrementer): void 100% (1/1)100% (4/4)100% (2/2)
truncateExitDescription (String): String 100% (1/1)100% (25/25)100% (4/4)
updateStepExecution (StepExecution): void 100% (1/1)100% (219/219)100% (13/13)
validateStepExecution (StepExecution): void 100% (1/1)100% (15/15)100% (5/5)
     
class JdbcStepExecutionDao$StepExecutionRowMapper100% (1/1)100% (2/2)100% (92/92)100% (16/16)
JdbcStepExecutionDao$StepExecutionRowMapper (JdbcStepExecutionDao, JobExecuti... 100% (1/1)100% (12/12)100% (4/4)
mapRow (ResultSet, int): Object 100% (1/1)100% (80/80)100% (12/12)

1package org.springframework.batch.core.repository.dao;
2 
3import java.sql.ResultSet;
4import java.sql.SQLException;
5import java.sql.Types;
6import java.util.List;
7 
8import org.apache.commons.logging.Log;
9import org.apache.commons.logging.LogFactory;
10import org.springframework.batch.core.BatchStatus;
11import org.springframework.batch.core.JobExecution;
12import org.springframework.batch.core.Step;
13import org.springframework.batch.core.StepExecution;
14import org.springframework.batch.item.ExecutionContext;
15import org.springframework.batch.repeat.ExitStatus;
16import org.springframework.beans.factory.InitializingBean;
17import org.springframework.dao.OptimisticLockingFailureException;
18import org.springframework.jdbc.core.RowMapper;
19import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
20import org.springframework.jdbc.support.lob.DefaultLobHandler;
21import org.springframework.jdbc.support.lob.LobCreator;
22import org.springframework.jdbc.support.lob.LobHandler;
23import org.springframework.util.Assert;
24 
25/**
26 * Jdbc implementation of {@link StepExecutionDao}.<br/>
27 * 
28 * Allows customization of the tables names used by Spring Batch for step meta
29 * data via a prefix property.<br/>
30 * 
31 * Uses sequences or tables (via Spring's {@link DataFieldMaxValueIncrementer}
32 * abstraction) to create all primary keys before inserting a new row. All
33 * objects are checked to ensure all fields to be stored are not null. If any
34 * are found to be null, an IllegalArgumentException will be thrown. This could
35 * be left to JdbcTemplate, however, the exception will be fairly vague, and
36 * fails to highlight which field caused the exception.<br/>
37 * 
38 * @author Lucas Ward
39 * @author Dave Syer
40 * @author Robert Kasanicky
41 * 
42 * @see StepExecutionDao
43 */
44public class JdbcStepExecutionDao extends AbstractJdbcBatchMetadataDao implements StepExecutionDao, InitializingBean {
45 
46        private static final Log logger = LogFactory.getLog(JdbcStepExecutionDao.class);
47 
48        private static final String SAVE_STEP_EXECUTION = "INSERT into %PREFIX%STEP_EXECUTION(STEP_EXECUTION_ID, VERSION, STEP_NAME, JOB_EXECUTION_ID, START_TIME, "
49                        + "END_TIME, STATUS, COMMIT_COUNT, ITEM_COUNT, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE, READ_SKIP_COUNT, WRITE_SKIP_COUNT, ROLLBACK_COUNT) "
50                        + "values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
51 
52        private static final String UPDATE_STEP_EXECUTION = "UPDATE %PREFIX%STEP_EXECUTION set START_TIME = ?, END_TIME = ?, "
53                        + "STATUS = ?, COMMIT_COUNT = ?, ITEM_COUNT = ?, CONTINUABLE = ? , EXIT_CODE = ?, "
54                        + "EXIT_MESSAGE = ?, VERSION = ?, READ_SKIP_COUNT = ?, WRITE_SKIP_COUNT = ?, ROLLBACK_COUNT = ? where STEP_EXECUTION_ID = ? and VERSION = ?";
55 
56        private static final String GET_STEP_EXECUTION = "SELECT STEP_EXECUTION_ID, STEP_NAME, START_TIME, END_TIME, STATUS, COMMIT_COUNT,"
57                        + " ITEM_COUNT, CONTINUABLE, EXIT_CODE, EXIT_MESSAGE, READ_SKIP_COUNT, WRITE_SKIP_COUNT, ROLLBACK_COUNT from %PREFIX%STEP_EXECUTION where STEP_NAME = ? and JOB_EXECUTION_ID = ?";
58 
59        private static final String CURRENT_VERSION_STEP_EXECUTION = "SELECT VERSION FROM %PREFIX%STEP_EXECUTION WHERE STEP_EXECUTION_ID=?";
60 
61        private int exitMessageLength = DEFAULT_EXIT_MESSAGE_LENGTH;
62 
63        private LobHandler lobHandler = new DefaultLobHandler();
64 
65        private DataFieldMaxValueIncrementer stepExecutionIncrementer;
66 
67        private JdbcExecutionContextDao ecDao = new JdbcExecutionContextDao();
68 
69        /**
70         * Public setter for the exit message length in database. Do not set this if
71         * you haven't modified the schema.
72         * @param exitMessageLength the exitMessageLength to set
73         */
74        public void setExitMessageLength(int exitMessageLength) {
75                this.exitMessageLength = exitMessageLength;
76        }
77 
78        public ExecutionContext findExecutionContext(final StepExecution stepExecution) {
79 
80                return ecDao.getExecutionContext(stepExecution);
81        }
82 
83        /**
84         * Save a StepExecution. A unique id will be generated by the
85         * stepExecutionIncrementor, and then set in the StepExecution. All values
86         * will then be stored via an INSERT statement.
87         * 
88         * @see StepExecutionDao#saveStepExecution(StepExecution)
89         */
90        public void saveStepExecution(StepExecution stepExecution) {
91 
92                Assert.isNull(stepExecution.getId(),
93                                "to-be-saved (not updated) StepExecution can't already have an id assigned");
94                Assert.isNull(stepExecution.getVersion(),
95                                "to-be-saved (not updated) StepExecution can't already have a version assigned");
96 
97                validateStepExecution(stepExecution);
98 
99                String exitDescription = truncateExitDescription(stepExecution.getExitStatus().getExitDescription());
100 
101                stepExecution.setId(new Long(stepExecutionIncrementer.nextLongValue()));
102                stepExecution.incrementVersion(); // should be 0 now
103                Object[] parameters = new Object[] { stepExecution.getId(), stepExecution.getVersion(),
104                                stepExecution.getStepName(), stepExecution.getJobExecutionId(), stepExecution.getStartTime(),
105                                stepExecution.getEndTime(), stepExecution.getStatus().toString(), stepExecution.getCommitCount(),
106                                stepExecution.getItemCount(), stepExecution.getExitStatus().isContinuable() ? "Y" : "N",
107                                stepExecution.getExitStatus().getExitCode(), exitDescription, stepExecution.getReadSkipCount(),
108                                stepExecution.getWriteSkipCount(), stepExecution.getRollbackCount() };
109                getJdbcTemplate().update(
110                                getQuery(SAVE_STEP_EXECUTION),
111                                parameters,
112                                new int[] { Types.INTEGER, Types.INTEGER, Types.VARCHAR, Types.INTEGER, Types.TIMESTAMP,
113                                                Types.TIMESTAMP, Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.CHAR, Types.VARCHAR,
114                                                Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.INTEGER });
115        }
116 
117        /**
118         * Validate StepExecution. At a minimum, JobId, StartTime, and Status cannot
119         * be null. EndTime can be null for an unfinished job.
120         * 
121         * @param jobExecution
122         * @throws IllegalArgumentException
123         */
124        private void validateStepExecution(StepExecution stepExecution) {
125                Assert.notNull(stepExecution);
126                Assert.notNull(stepExecution.getStepName(), "StepExecution step name cannot be null.");
127                Assert.notNull(stepExecution.getStartTime(), "StepExecution start time cannot be null.");
128                Assert.notNull(stepExecution.getStatus(), "StepExecution status cannot be null.");
129        }
130 
131        /**
132         * Save or update execution attributes. A lob creator must be used, since
133         * any attributes that don't match a provided type must be serialized into a
134         * blob.
135         * 
136         * @see LobCreator
137         */
138        public void saveOrUpdateExecutionContext(final StepExecution stepExecution) {
139 
140                ecDao.saveOrUpdateExecutionContext(stepExecution);
141        }
142 
143        /*
144         * (non-Javadoc)
145         * @see org.springframework.batch.execution.repository.dao.StepExecutionDao#updateStepExecution(org.springframework.batch.core.domain.StepExecution)
146         */
147        public void updateStepExecution(StepExecution stepExecution) {
148 
149                validateStepExecution(stepExecution);
150                Assert.notNull(stepExecution.getId(), "StepExecution Id cannot be null. StepExecution must saved"
151                                + " before it can be updated.");
152 
153                // Do not check for existence of step execution considering
154                // it is saved at every commit point.
155 
156                String exitDescription = truncateExitDescription(stepExecution.getExitStatus().getExitDescription());
157 
158                // Attempt to prevent concurrent modification errors by blocking here if
159                // someone is already trying to do it.
160                synchronized (stepExecution) {
161 
162                        Integer version = new Integer(stepExecution.getVersion().intValue() + 1);
163                        Object[] parameters = new Object[] { stepExecution.getStartTime(), stepExecution.getEndTime(),
164                                        stepExecution.getStatus().toString(), stepExecution.getCommitCount(), stepExecution.getItemCount(),
165                                        stepExecution.getExitStatus().isContinuable() ? "Y" : "N",
166                                        stepExecution.getExitStatus().getExitCode(), exitDescription, version,
167                                        stepExecution.getReadSkipCount(), stepExecution.getWriteSkipCount(),
168                                        stepExecution.getRollbackCount(), stepExecution.getId(), stepExecution.getVersion() };
169                        int count = getJdbcTemplate().update(
170                                        getQuery(UPDATE_STEP_EXECUTION),
171                                        parameters,
172                                        new int[] { Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.INTEGER, Types.INTEGER,
173                                                        Types.CHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.INTEGER, Types.INTEGER,
174                                                        Types.INTEGER, Types.INTEGER, Types.INTEGER });
175 
176                        // Avoid concurrent modifications...
177                        if (count == 0) {
178                                int curentVersion = getJdbcTemplate().queryForInt(getQuery(CURRENT_VERSION_STEP_EXECUTION),
179                                                new Object[] { stepExecution.getId() });
180                                throw new OptimisticLockingFailureException("Attempt to update step execution id="
181                                                + stepExecution.getId() + " with wrong version (" + stepExecution.getVersion()
182                                                + "), where current version is " + curentVersion);
183                        }
184 
185                        stepExecution.incrementVersion();
186 
187                }
188        }
189 
190        /**
191         * Truncate the exit description if the length exceeds
192         * {@link #EXIT_MESSAGE_LENGTH}.
193         * @param description the string to truncate
194         * @return truncated description
195         */
196        private String truncateExitDescription(String description) {
197                if (description != null && description.length() > exitMessageLength) {
198                        logger.debug("Truncating long message before update of StepExecution, original message is: " + description);
199                        return description.substring(0, exitMessageLength);
200                }
201                else {
202                        return description;
203                }
204        }
205 
206        private class StepExecutionRowMapper implements RowMapper {
207 
208                private final JobExecution jobExecution;
209 
210                private final Step step;
211 
212                public StepExecutionRowMapper(JobExecution jobExecution, Step step) {
213                        this.jobExecution = jobExecution;
214                        this.step = step;
215                }
216 
217                public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
218 
219                        StepExecution stepExecution = new StepExecution(step.getName(), jobExecution, new Long(rs.getLong(1)));
220                        stepExecution.setStartTime(rs.getTimestamp(3));
221                        stepExecution.setEndTime(rs.getTimestamp(4));
222                        stepExecution.setStatus(BatchStatus.getStatus(rs.getString(5)));
223                        stepExecution.setCommitCount(rs.getInt(6));
224                        stepExecution.setItemCount(rs.getInt(7));
225                        stepExecution.setExitStatus(new ExitStatus("Y".equals(rs.getString(8)), rs.getString(9), rs.getString(10)));
226                        stepExecution.setExecutionContext(findExecutionContext(stepExecution));
227                        stepExecution.setReadSkipCount(rs.getInt(11));
228                        stepExecution.setWriteSkipCount(rs.getInt(12));
229                        stepExecution.setRollbackCount(rs.getInt(13));
230                        return stepExecution;
231                }
232 
233        }
234 
235        public void setLobHandler(LobHandler lobHandler) {
236                this.lobHandler = lobHandler;
237        }
238 
239        public void setStepExecutionIncrementer(DataFieldMaxValueIncrementer stepExecutionIncrementer) {
240                this.stepExecutionIncrementer = stepExecutionIncrementer;
241        }
242 
243        public void afterPropertiesSet() throws Exception {
244                super.afterPropertiesSet();
245                Assert.notNull(stepExecutionIncrementer, "StepExecutionIncrementer cannot be null.");
246                ecDao.setJdbcTemplate(getJdbcTemplate());
247                ecDao.setLobHandler(lobHandler);
248                ecDao.setTablePrefix(getTablePrefix());
249                ecDao.afterPropertiesSet();
250        }
251 
252        public StepExecution getStepExecution(JobExecution jobExecution, Step step) {
253                List executions = getJdbcTemplate().query(getQuery(GET_STEP_EXECUTION),
254                                new Object[] { step.getName(), jobExecution.getId() }, new StepExecutionRowMapper(jobExecution, step));
255 
256                Assert.state(executions.size() <= 1,
257                                "There can be at most one step execution with given name for single job execution");
258                if (executions.isEmpty()) {
259                        return null;
260                }
261                else {
262                        return (StepExecution) executions.get(0);
263                }
264        }
265 
266}

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