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

COVERAGE SUMMARY FOR SOURCE FILE [JdbcStepExecutionDao.java]

nameclass, %method, %block, %line, %
JdbcStepExecutionDao.java100% (2/2)100% (13/13)100% (678/679)100% (99/99)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JdbcStepExecutionDao100% (1/1)100% (11/11)100% (579/580)100% (80/80)
getStepExecution (JobExecution, Long): StepExecution 100% (1/1)98%  (40/41)100% (7/7)
<static initializer> 100% (1/1)100% (4/4)100% (2/2)
JdbcStepExecutionDao (): void 100% (1/1)100% (6/6)100% (2/2)
addStepExecutions (JobExecution): void 100% (1/1)100% (19/19)100% (3/3)
afterPropertiesSet (): void 100% (1/1)100% (7/7)100% (3/3)
saveStepExecution (StepExecution): void 100% (1/1)100% (211/211)100% (23/23)
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% (24/24)100% (4/4)
updateStepExecution (StepExecution): void 100% (1/1)100% (245/245)100% (28/28)
validateStepExecution (StepExecution): void 100% (1/1)100% (15/15)100% (5/5)
     
class JdbcStepExecutionDao$StepExecutionRowMapper100% (1/1)100% (2/2)100% (99/99)100% (19/19)
JdbcStepExecutionDao$StepExecutionRowMapper (JobExecution): void 100% (1/1)100% (6/6)100% (3/3)
mapRow (ResultSet, int): StepExecution 100% (1/1)100% (93/93)100% (16/16)

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

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