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 [JdbcJobExecutionDao.java]

nameclass, %method, %block, %line, %
JdbcJobExecutionDao.java100% (4/4)100% (23/23)100% (1047/1049)100% (120.9/121)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JdbcJobExecutionDao100% (1/1)100% (16/16)100% (839/841)100% (86.9/87)
getLastJobExecution (JobInstance): JobExecution 100% (1/1)98%  (43/44)100% (6/6)
insertParameter (Long, JobParameter$ParameterType, String, Object, boolean): ... 100% (1/1)100% (225/226)100% (13/13)
<static initializer> 100% (1/1)100% (4/4)100% (1/1)
JdbcJobExecutionDao (): void 100% (1/1)100% (6/6)100% (3/3)
afterPropertiesSet (): void 100% (1/1)100% (7/7)100% (3/3)
findJobExecutions (JobInstance): List 100% (1/1)100% (26/26)100% (3/3)
findRunningJobExecutions (String): Set 100% (1/1)100% (25/25)100% (4/4)
getJobExecution (Long): JobExecution 100% (1/1)100% (23/23)100% (4/4)
getJobParameters (Long): JobParameters 100% (1/1)100% (28/28)100% (4/4)
insertJobParameters (Long, JobParameters): void 100% (1/1)100% (30/30)100% (5/5)
saveJobExecution (JobExecution): void 100% (1/1)100% (124/124)100% (7/7)
setExitMessageLength (int): void 100% (1/1)100% (4/4)100% (2/2)
setJobExecutionIncrementer (DataFieldMaxValueIncrementer): void 100% (1/1)100% (4/4)100% (2/2)
synchronizeStatus (JobExecution): void 100% (1/1)100% (44/44)100% (6/6)
updateJobExecution (JobExecution): void 100% (1/1)100% (231/231)100% (19/19)
validateJobExecution (JobExecution): void 100% (1/1)100% (15/15)100% (5/5)
     
class JdbcJobExecutionDao$1100% (1/1)100% (2/2)100% (24/24)100% (4/4)
JdbcJobExecutionDao$1 (JdbcJobExecutionDao, Set): void 100% (1/1)100% (9/9)100% (1/1)
processRow (ResultSet): void 100% (1/1)100% (15/15)100% (3/3)
     
class JdbcJobExecutionDao$2100% (1/1)100% (2/2)100% (90/90)100% (13/13)
JdbcJobExecutionDao$2 (JdbcJobExecutionDao, Map): void 100% (1/1)100% (9/9)100% (1/1)
processRow (ResultSet): void 100% (1/1)100% (81/81)100% (12/12)
     
class JdbcJobExecutionDao$JobExecutionRowMapper100% (1/1)100% (3/3)100% (94/94)100% (19/19)
JdbcJobExecutionDao$JobExecutionRowMapper (JdbcJobExecutionDao): void 100% (1/1)100% (6/6)100% (2/2)
JdbcJobExecutionDao$JobExecutionRowMapper (JdbcJobExecutionDao, JobInstance):... 100% (1/1)100% (9/9)100% (3/3)
mapRow (ResultSet, int): JobExecution 100% (1/1)100% (79/79)100% (14/14)

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.ResultSet;
20import java.sql.SQLException;
21import java.sql.Timestamp;
22import java.sql.Types;
23import java.util.HashMap;
24import java.util.HashSet;
25import java.util.List;
26import java.util.Map;
27import java.util.Map.Entry;
28import java.util.Set;
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.JobInstance;
36import org.springframework.batch.core.JobParameter;
37import org.springframework.batch.core.JobParameter.ParameterType;
38import org.springframework.batch.core.JobParameters;
39import org.springframework.beans.factory.InitializingBean;
40import org.springframework.dao.EmptyResultDataAccessException;
41import org.springframework.dao.OptimisticLockingFailureException;
42import org.springframework.jdbc.core.RowCallbackHandler;
43import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
44import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
45import org.springframework.util.Assert;
46 
47/**
48 * JDBC implementation of {@link JobExecutionDao}. Uses sequences (via Spring's
49 * {@link DataFieldMaxValueIncrementer} abstraction) to create all primary keys
50 * before inserting a new row. Objects are checked to ensure all mandatory
51 * fields to be stored are not null. If any are found to be null, an
52 * IllegalArgumentException will be thrown. This could be left to JdbcTemplate,
53 * however, the exception will be fairly vague, and fails to highlight which
54 * field caused the exception.
55 *
56 * @author Lucas Ward
57 * @author Dave Syer
58 * @author Robert Kasanicky
59 * @author Michael Minella
60 */
61public class JdbcJobExecutionDao extends AbstractJdbcBatchMetadataDao implements JobExecutionDao, InitializingBean {
62 
63        private static final Log logger = LogFactory.getLog(JdbcJobExecutionDao.class);
64 
65        private static final String SAVE_JOB_EXECUTION = "INSERT into %PREFIX%JOB_EXECUTION(JOB_EXECUTION_ID, JOB_INSTANCE_ID, START_TIME, "
66                        + "END_TIME, STATUS, EXIT_CODE, EXIT_MESSAGE, VERSION, CREATE_TIME, LAST_UPDATED) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
67 
68        private static final String CHECK_JOB_EXECUTION_EXISTS = "SELECT COUNT(*) FROM %PREFIX%JOB_EXECUTION WHERE JOB_EXECUTION_ID = ?";
69 
70        private static final String GET_STATUS = "SELECT STATUS from %PREFIX%JOB_EXECUTION where JOB_EXECUTION_ID = ?";
71 
72        private static final String UPDATE_JOB_EXECUTION = "UPDATE %PREFIX%JOB_EXECUTION set START_TIME = ?, END_TIME = ?, "
73                        + " STATUS = ?, EXIT_CODE = ?, EXIT_MESSAGE = ?, VERSION = ?, CREATE_TIME = ?, LAST_UPDATED = ? where JOB_EXECUTION_ID = ? and VERSION = ?";
74 
75        private static final String FIND_JOB_EXECUTIONS = "SELECT JOB_EXECUTION_ID, START_TIME, END_TIME, STATUS, EXIT_CODE, EXIT_MESSAGE, CREATE_TIME, LAST_UPDATED, VERSION"
76                        + " from %PREFIX%JOB_EXECUTION where JOB_INSTANCE_ID = ? order by JOB_EXECUTION_ID desc";
77 
78        private static final String GET_LAST_EXECUTION = "SELECT JOB_EXECUTION_ID, START_TIME, END_TIME, STATUS, EXIT_CODE, EXIT_MESSAGE, CREATE_TIME, LAST_UPDATED, VERSION "
79                        + "from %PREFIX%JOB_EXECUTION E where JOB_INSTANCE_ID = ? and JOB_EXECUTION_ID in (SELECT max(JOB_EXECUTION_ID) from %PREFIX%JOB_EXECUTION E2 where E2.JOB_INSTANCE_ID = ?)";
80 
81        private static final String GET_EXECUTION_BY_ID = "SELECT JOB_EXECUTION_ID, START_TIME, END_TIME, STATUS, EXIT_CODE, EXIT_MESSAGE, CREATE_TIME, LAST_UPDATED, VERSION"
82                        + " from %PREFIX%JOB_EXECUTION where JOB_EXECUTION_ID = ?";
83 
84        private static final String GET_RUNNING_EXECUTIONS = "SELECT E.JOB_EXECUTION_ID, E.START_TIME, E.END_TIME, E.STATUS, E.EXIT_CODE, E.EXIT_MESSAGE, E.CREATE_TIME, E.LAST_UPDATED, E.VERSION, "
85                        + "E.JOB_INSTANCE_ID from %PREFIX%JOB_EXECUTION E, %PREFIX%JOB_INSTANCE I where E.JOB_INSTANCE_ID=I.JOB_INSTANCE_ID and I.JOB_NAME=? and E.END_TIME is NULL order by E.JOB_EXECUTION_ID desc";
86 
87        private static final String CURRENT_VERSION_JOB_EXECUTION = "SELECT VERSION FROM %PREFIX%JOB_EXECUTION WHERE JOB_EXECUTION_ID=?";
88 
89        private static final String FIND_PARAMS_FROM_ID = "SELECT JOB_EXECUTION_ID, KEY_NAME, TYPE_CD, "
90                        + "STRING_VAL, DATE_VAL, LONG_VAL, DOUBLE_VAL, IDENTIFYING from %PREFIX%JOB_EXECUTION_PARAMS where JOB_EXECUTION_ID = ?";
91 
92        private static final String CREATE_JOB_PARAMETERS = "INSERT into %PREFIX%JOB_EXECUTION_PARAMS(JOB_EXECUTION_ID, KEY_NAME, TYPE_CD, "
93                        + "STRING_VAL, DATE_VAL, LONG_VAL, DOUBLE_VAL, IDENTIFYING) values (?, ?, ?, ?, ?, ?, ?, ?)";
94 
95        private int exitMessageLength = DEFAULT_EXIT_MESSAGE_LENGTH;
96 
97        private DataFieldMaxValueIncrementer jobExecutionIncrementer;
98 
99        /**
100         * Public setter for the exit message length in database. Do not set this if
101         * you haven't modified the schema.
102         * @param exitMessageLength the exitMessageLength to set
103         */
104        public void setExitMessageLength(int exitMessageLength) {
105                this.exitMessageLength = exitMessageLength;
106        }
107 
108        /**
109         * Setter for {@link DataFieldMaxValueIncrementer} to be used when
110         * generating primary keys for {@link JobExecution} instances.
111         *
112         * @param jobExecutionIncrementer the {@link DataFieldMaxValueIncrementer}
113         */
114        public void setJobExecutionIncrementer(DataFieldMaxValueIncrementer jobExecutionIncrementer) {
115                this.jobExecutionIncrementer = jobExecutionIncrementer;
116        }
117 
118        @Override
119        public void afterPropertiesSet() throws Exception {
120                super.afterPropertiesSet();
121                Assert.notNull(jobExecutionIncrementer, "The jobExecutionIncrementer must not be null.");
122        }
123 
124        @Override
125        public List<JobExecution> findJobExecutions(final JobInstance job) {
126 
127                Assert.notNull(job, "Job cannot be null.");
128                Assert.notNull(job.getId(), "Job Id cannot be null.");
129 
130                return getJdbcTemplate().query(getQuery(FIND_JOB_EXECUTIONS), new JobExecutionRowMapper(job), job.getId());
131        }
132 
133        /**
134         *
135         * SQL implementation using Sequences via the Spring incrementer
136         * abstraction. Once a new id has been obtained, the JobExecution is saved
137         * via a SQL INSERT statement.
138         *
139         * @see JobExecutionDao#saveJobExecution(JobExecution)
140         * @throws IllegalArgumentException if jobExecution is null, as well as any
141         * of it's fields to be persisted.
142         */
143        @Override
144        public void saveJobExecution(JobExecution jobExecution) {
145 
146                validateJobExecution(jobExecution);
147 
148                jobExecution.incrementVersion();
149 
150                jobExecution.setId(jobExecutionIncrementer.nextLongValue());
151                Object[] parameters = new Object[] { jobExecution.getId(), jobExecution.getJobId(),
152                                jobExecution.getStartTime(), jobExecution.getEndTime(), jobExecution.getStatus().toString(),
153                                jobExecution.getExitStatus().getExitCode(), jobExecution.getExitStatus().getExitDescription(),
154                                jobExecution.getVersion(), jobExecution.getCreateTime(), jobExecution.getLastUpdated() };
155                getJdbcTemplate().update(
156                                getQuery(SAVE_JOB_EXECUTION),
157                                parameters,
158                                new int[] { Types.BIGINT, Types.BIGINT, Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR,
159                                        Types.VARCHAR, Types.VARCHAR, Types.INTEGER, Types.TIMESTAMP, Types.TIMESTAMP });
160 
161                insertJobParameters(jobExecution.getId(), jobExecution.getJobParameters());
162        }
163 
164        /**
165         * Validate JobExecution. At a minimum, JobId, StartTime, EndTime, and
166         * Status cannot be null.
167         *
168         * @param jobExecution
169         * @throws IllegalArgumentException
170         */
171        private void validateJobExecution(JobExecution jobExecution) {
172 
173                Assert.notNull(jobExecution);
174                Assert.notNull(jobExecution.getJobId(), "JobExecution Job-Id cannot be null.");
175                Assert.notNull(jobExecution.getStatus(), "JobExecution status cannot be null.");
176                Assert.notNull(jobExecution.getCreateTime(), "JobExecution create time cannot be null");
177        }
178 
179        /**
180         * Update given JobExecution using a SQL UPDATE statement. The JobExecution
181         * is first checked to ensure all fields are not null, and that it has an
182         * ID. The database is then queried to ensure that the ID exists, which
183         * ensures that it is valid.
184         *
185         * @see JobExecutionDao#updateJobExecution(JobExecution)
186         */
187        @Override
188        public void updateJobExecution(JobExecution jobExecution) {
189 
190                validateJobExecution(jobExecution);
191 
192                Assert.notNull(jobExecution.getId(),
193                                "JobExecution ID cannot be null. JobExecution must be saved before it can be updated");
194 
195                Assert.notNull(jobExecution.getVersion(),
196                                "JobExecution version cannot be null. JobExecution must be saved before it can be updated");
197 
198                synchronized (jobExecution) {
199                        Integer version = jobExecution.getVersion() + 1;
200 
201                        String exitDescription = jobExecution.getExitStatus().getExitDescription();
202                        if (exitDescription != null && exitDescription.length() > exitMessageLength) {
203                                exitDescription = exitDescription.substring(0, exitMessageLength);
204                                logger.debug("Truncating long message before update of JobExecution: " + jobExecution);
205                        }
206                        Object[] parameters = new Object[] { jobExecution.getStartTime(), jobExecution.getEndTime(),
207                                        jobExecution.getStatus().toString(), jobExecution.getExitStatus().getExitCode(), exitDescription,
208                                        version, jobExecution.getCreateTime(), jobExecution.getLastUpdated(), jobExecution.getId(),
209                                        jobExecution.getVersion() };
210 
211                        // Check if given JobExecution's Id already exists, if none is found
212                        // it
213                        // is invalid and
214                        // an exception should be thrown.
215                        if (getJdbcTemplate().queryForInt(getQuery(CHECK_JOB_EXECUTION_EXISTS),
216                                        new Object[] { jobExecution.getId() }) != 1) {
217                                throw new NoSuchObjectException("Invalid JobExecution, ID " + jobExecution.getId() + " not found.");
218                        }
219 
220                        int count = getJdbcTemplate().update(
221                                        getQuery(UPDATE_JOB_EXECUTION),
222                                        parameters,
223                                        new int[] { Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
224                                                Types.INTEGER, Types.TIMESTAMP, Types.TIMESTAMP, Types.BIGINT, Types.INTEGER });
225 
226                        // Avoid concurrent modifications...
227                        if (count == 0) {
228                                int curentVersion = getJdbcTemplate().queryForInt(getQuery(CURRENT_VERSION_JOB_EXECUTION),
229                                                new Object[] { jobExecution.getId() });
230                                throw new OptimisticLockingFailureException("Attempt to update job execution id="
231                                                + jobExecution.getId() + " with wrong version (" + jobExecution.getVersion()
232                                                + "), where current version is " + curentVersion);
233                        }
234 
235                        jobExecution.incrementVersion();
236                }
237        }
238 
239        @Override
240        public JobExecution getLastJobExecution(JobInstance jobInstance) {
241 
242                Long id = jobInstance.getId();
243 
244                List<JobExecution> executions = getJdbcTemplate().query(getQuery(GET_LAST_EXECUTION),
245                                new JobExecutionRowMapper(jobInstance), id, id);
246 
247                Assert.state(executions.size() <= 1, "There must be at most one latest job execution");
248 
249                if (executions.isEmpty()) {
250                        return null;
251                }
252                else {
253                        return executions.get(0);
254                }
255        }
256 
257        /*
258         * (non-Javadoc)
259         *
260         * @seeorg.springframework.batch.core.repository.dao.JobExecutionDao#
261         * getLastJobExecution(java.lang.String)
262         */
263        @Override
264        public JobExecution getJobExecution(Long executionId) {
265                try {
266                        JobExecution jobExecution = getJdbcTemplate().queryForObject(getQuery(GET_EXECUTION_BY_ID),
267                                        new JobExecutionRowMapper(), executionId);
268                        return jobExecution;
269                }
270                catch (EmptyResultDataAccessException e) {
271                        return null;
272                }
273        }
274 
275        /*
276         * (non-Javadoc)
277         *
278         * @seeorg.springframework.batch.core.repository.dao.JobExecutionDao#
279         * findRunningJobExecutions(java.lang.String)
280         */
281        @Override
282        public Set<JobExecution> findRunningJobExecutions(String jobName) {
283 
284                final Set<JobExecution> result = new HashSet<JobExecution>();
285                RowCallbackHandler handler = new RowCallbackHandler() {
286                        @Override
287                        public void processRow(ResultSet rs) throws SQLException {
288                                JobExecutionRowMapper mapper = new JobExecutionRowMapper();
289                                result.add(mapper.mapRow(rs, 0));
290                        }
291                };
292                getJdbcTemplate().query(getQuery(GET_RUNNING_EXECUTIONS), new Object[] { jobName }, handler);
293 
294                return result;
295        }
296 
297        @Override
298        public void synchronizeStatus(JobExecution jobExecution) {
299                int currentVersion = getJdbcTemplate().queryForInt(getQuery(CURRENT_VERSION_JOB_EXECUTION),
300                                jobExecution.getId());
301 
302                if (currentVersion != jobExecution.getVersion().intValue()) {
303                        String status = getJdbcTemplate().queryForObject(getQuery(GET_STATUS), String.class, jobExecution.getId());
304                        jobExecution.upgradeStatus(BatchStatus.valueOf(status));
305                        jobExecution.setVersion(currentVersion);
306                }
307        }
308 
309        /**
310         * Convenience method that inserts all parameters from the provided
311         * JobParameters.
312         *
313         */
314        private void insertJobParameters(Long executionId, JobParameters jobParameters) {
315 
316                for (Entry<String, JobParameter> entry : jobParameters.getParameters()
317                                .entrySet()) {
318                        JobParameter jobParameter = entry.getValue();
319                        insertParameter(executionId, jobParameter.getType(), entry.getKey(),
320                                        jobParameter.getValue(), jobParameter.isIdentifying());
321                }
322        }
323 
324        /**
325         * Convenience method that inserts an individual records into the
326         * JobParameters table.
327         */
328        private void insertParameter(Long executionId, ParameterType type, String key,
329                        Object value, boolean identifying) {
330 
331                Object[] args = new Object[0];
332                int[] argTypes = new int[] { Types.BIGINT, Types.VARCHAR,
333                                Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP, Types.BIGINT,
334                                Types.DOUBLE, Types.CHAR };
335 
336                String identifyingFlag = identifying? "Y":"N";
337 
338                if (type == ParameterType.STRING) {
339                        args = new Object[] { executionId, key, type, value, new Timestamp(0L),
340                                        0L, 0D, identifyingFlag};
341                } else if (type == ParameterType.LONG) {
342                        args = new Object[] { executionId, key, type, "", new Timestamp(0L),
343                                        value, new Double(0), identifyingFlag};
344                } else if (type == ParameterType.DOUBLE) {
345                        args = new Object[] { executionId, key, type, "", new Timestamp(0L), 0L,
346                                        value, identifyingFlag};
347                } else if (type == ParameterType.DATE) {
348                        args = new Object[] { executionId, key, type, "", value, 0L, 0D, identifyingFlag};
349                }
350 
351                getJdbcTemplate().update(getQuery(CREATE_JOB_PARAMETERS), args, argTypes);
352        }
353 
354        /**
355         * @param executionId
356         * @return
357         */
358        protected JobParameters getJobParameters(Long executionId) {
359                final Map<String, JobParameter> map = new HashMap<String, JobParameter>();
360                RowCallbackHandler handler = new RowCallbackHandler() {
361                        @Override
362                        public void processRow(ResultSet rs) throws SQLException {
363                                ParameterType type = ParameterType.valueOf(rs.getString(3));
364                                JobParameter value = null;
365 
366                                if (type == ParameterType.STRING) {
367                                        value = new JobParameter(rs.getString(4), rs.getString(8).equalsIgnoreCase("Y"));
368                                } else if (type == ParameterType.LONG) {
369                                        value = new JobParameter(rs.getLong(6), rs.getString(8).equalsIgnoreCase("Y"));
370                                } else if (type == ParameterType.DOUBLE) {
371                                        value = new JobParameter(rs.getDouble(7), rs.getString(8).equalsIgnoreCase("Y"));
372                                } else if (type == ParameterType.DATE) {
373                                        value = new JobParameter(rs.getTimestamp(5), rs.getString(8).equalsIgnoreCase("Y"));
374                                }
375 
376                                // No need to assert that value is not null because it's an enum
377                                map.put(rs.getString(2), value);
378                        }
379                };
380 
381                getJdbcTemplate().query(getQuery(FIND_PARAMS_FROM_ID), new Object[] { executionId }, handler);
382 
383                return new JobParameters(map);
384        }
385 
386        /**
387         * Re-usable mapper for {@link JobExecution} instances.
388         *
389         * @author Dave Syer
390         *
391         */
392        private final class JobExecutionRowMapper implements ParameterizedRowMapper<JobExecution> {
393 
394                private JobInstance jobInstance;
395 
396                private JobParameters jobParameters;
397 
398                public JobExecutionRowMapper() {
399                }
400 
401                public JobExecutionRowMapper(JobInstance jobInstance) {
402                        this.jobInstance = jobInstance;
403                }
404 
405                @Override
406                public JobExecution mapRow(ResultSet rs, int rowNum) throws SQLException {
407                        Long id = rs.getLong(1);
408                        JobExecution jobExecution;
409                        if (jobParameters == null) {
410                                jobParameters = getJobParameters(id);
411                        }
412 
413                        if (jobInstance == null) {
414                                jobExecution = new JobExecution(id, jobParameters);
415                        }
416                        else {
417                                jobExecution = new JobExecution(jobInstance, id, jobParameters);
418                        }
419 
420                        jobExecution.setStartTime(rs.getTimestamp(2));
421                        jobExecution.setEndTime(rs.getTimestamp(3));
422                        jobExecution.setStatus(BatchStatus.valueOf(rs.getString(4)));
423                        jobExecution.setExitStatus(new ExitStatus(rs.getString(5), rs.getString(6)));
424                        jobExecution.setCreateTime(rs.getTimestamp(7));
425                        jobExecution.setLastUpdated(rs.getTimestamp(8));
426                        jobExecution.setVersion(rs.getInt(9));
427                        return jobExecution;
428                }
429 
430        }
431}

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