EMMA Coverage Report (generated Thu May 22 12:08:10 CDT 2014)
[all classes][org.springframework.batch.core.repository.dao]

COVERAGE SUMMARY FOR SOURCE FILE [JdbcStepExecutionDao.java]

nameclass, %method, %block, %line, %
JdbcStepExecutionDao.java100% (3/3)100% (19/19)99%  (892/905)99%  (101/102)

COVERAGE BREAKDOWN BY CLASS AND METHOD

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

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