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

nameclass, %method, %block, %line, %
JdbcJobInstanceDao.java100% (5/5)100% (23/23)96%  (747/781)92%  (123.9/135)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JdbcJobInstanceDao100% (1/1)100% (14/14)94%  (563/597)89%  (88.9/100)
getJobInstance (String, JobParameters): JobInstance 100% (1/1)74%  (55/74)79%  (11.9/15)
createJobKey (JobParameters): String 100% (1/1)85%  (70/82)62%  (10/16)
getJobInstance (JobExecution): JobInstance 100% (1/1)86%  (19/22)60%  (3/5)
JdbcJobInstanceDao (): void 100% (1/1)100% (3/3)100% (1/1)
access$0 (JdbcJobInstanceDao, Long): JobParameters 100% (1/1)100% (4/4)100% (1/1)
afterPropertiesSet (): void 100% (1/1)100% (6/6)100% (3/3)
createJobInstance (String, JobParameters): JobInstance 100% (1/1)100% (85/85)100% (16/16)
getJobInstance (Long): JobInstance 100% (1/1)100% (21/21)100% (4/4)
getJobInstances (String, int, int): List 100% (1/1)100% (25/25)100% (5/5)
getJobNames (): List 100% (1/1)100% (13/13)100% (2/2)
getJobParameters (Long): JobParameters 100% (1/1)100% (29/29)100% (6/6)
insertJobParameters (Long, JobParameters): void 100% (1/1)100% (28/28)100% (6/6)
insertParameter (Long, JobParameter$ParameterType, String, Object): void 100% (1/1)100% (201/201)100% (18/18)
setJobIncrementer (DataFieldMaxValueIncrementer): void 100% (1/1)100% (4/4)100% (2/2)
     
class JdbcJobInstanceDao$1100% (1/1)100% (2/2)100% (70/70)100% (14/14)
JdbcJobInstanceDao$1 (JdbcJobInstanceDao, Map): void 100% (1/1)100% (9/9)100% (2/2)
processRow (ResultSet): void 100% (1/1)100% (61/61)100% (12/12)
     
class JdbcJobInstanceDao$2100% (1/1)100% (2/2)100% (10/10)100% (3/3)
JdbcJobInstanceDao$2 (JdbcJobInstanceDao): void 100% (1/1)100% (6/6)100% (2/2)
mapRow (ResultSet, int): String 100% (1/1)100% (4/4)100% (1/1)
     
class JdbcJobInstanceDao$3100% (1/1)100% (2/2)100% (58/58)100% (11/11)
JdbcJobInstanceDao$3 (JdbcJobInstanceDao, int, int): void 100% (1/1)100% (17/17)100% (3/3)
extractData (ResultSet): Object 100% (1/1)100% (41/41)100% (8/8)
     
class JdbcJobInstanceDao$JobInstanceRowMapper100% (1/1)100% (3/3)100% (46/46)100% (12/12)
JdbcJobInstanceDao$JobInstanceRowMapper (JdbcJobInstanceDao): void 100% (1/1)100% (6/6)100% (2/2)
JdbcJobInstanceDao$JobInstanceRowMapper (JdbcJobInstanceDao, JobParameters): ... 100% (1/1)100% (9/9)100% (3/3)
mapRow (ResultSet, int): JobInstance 100% (1/1)100% (31/31)100% (7/7)

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.io.UnsupportedEncodingException;
20import java.math.BigInteger;
21import java.security.MessageDigest;
22import java.security.NoSuchAlgorithmException;
23import java.sql.ResultSet;
24import java.sql.SQLException;
25import java.sql.Timestamp;
26import java.sql.Types;
27import java.util.ArrayList;
28import java.util.Collections;
29import java.util.HashMap;
30import java.util.List;
31import java.util.Map;
32import java.util.Map.Entry;
33 
34import org.springframework.batch.core.JobExecution;
35import org.springframework.batch.core.JobInstance;
36import org.springframework.batch.core.JobParameter;
37import org.springframework.batch.core.JobParameters;
38import org.springframework.batch.core.JobParameter.ParameterType;
39import org.springframework.beans.factory.InitializingBean;
40import org.springframework.dao.DataAccessException;
41import org.springframework.dao.EmptyResultDataAccessException;
42import org.springframework.jdbc.core.ResultSetExtractor;
43import org.springframework.jdbc.core.RowCallbackHandler;
44import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
45import org.springframework.jdbc.support.incrementer.DataFieldMaxValueIncrementer;
46import org.springframework.util.Assert;
47import org.springframework.util.StringUtils;
48 
49/**
50 * Jdbc implementation of {@link JobInstanceDao}. Uses sequences (via Spring's
51 * {@link DataFieldMaxValueIncrementer} abstraction) to create all primary keys
52 * before inserting a new row. Objects are checked to ensure all mandatory
53 * fields to be stored are not null. If any are found to be null, an
54 * IllegalArgumentException will be thrown. This could be left to JdbcTemplate,
55 * however, the exception will be fairly vague, and fails to highlight which
56 * field caused the exception.
57 * 
58 * @author Lucas Ward
59 * @author Dave Syer
60 * @author Robert Kasanicky
61 */
62public class JdbcJobInstanceDao extends AbstractJdbcBatchMetadataDao implements
63                JobInstanceDao, InitializingBean {
64 
65        private static final String CREATE_JOB_INSTANCE = "INSERT into %PREFIX%JOB_INSTANCE(JOB_INSTANCE_ID, JOB_NAME, JOB_KEY, VERSION)"
66                        + " values (?, ?, ?, ?)";
67 
68        private static final String CREATE_JOB_PARAMETERS = "INSERT into %PREFIX%JOB_PARAMS(JOB_INSTANCE_ID, KEY_NAME, TYPE_CD, "
69                        + "STRING_VAL, DATE_VAL, LONG_VAL, DOUBLE_VAL) values (?, ?, ?, ?, ?, ?, ?)";
70 
71        private static final String FIND_JOBS_WITH_NAME = "SELECT JOB_INSTANCE_ID, JOB_NAME from %PREFIX%JOB_INSTANCE where JOB_NAME = ?";
72 
73        private static final String FIND_JOBS_WITH_KEY = FIND_JOBS_WITH_NAME
74                        + " and JOB_KEY = ?";
75 
76        private static final String FIND_JOBS_WITH_EMPTY_KEY = "SELECT JOB_INSTANCE_ID, JOB_NAME from %PREFIX%JOB_INSTANCE where JOB_NAME = ? and (JOB_KEY = ? OR JOB_KEY is NULL)";
77 
78        private static final String GET_JOB_FROM_ID = "SELECT JOB_INSTANCE_ID, JOB_NAME, JOB_KEY, VERSION from %PREFIX%JOB_INSTANCE where JOB_INSTANCE_ID = ?";
79 
80        private static final String GET_JOB_FROM_EXECUTION_ID = "SELECT ji.JOB_INSTANCE_ID, JOB_NAME, JOB_KEY, ji.VERSION from %PREFIX%JOB_INSTANCE ji, "
81                        + "%PREFIX%JOB_EXECUTION je where JOB_EXECUTION_ID = ? and ji.JOB_INSTANCE_ID = je.JOB_INSTANCE_ID";
82 
83        private static final String FIND_PARAMS_FROM_ID = "SELECT JOB_INSTANCE_ID, KEY_NAME, TYPE_CD, "
84                        + "STRING_VAL, DATE_VAL, LONG_VAL, DOUBLE_VAL from %PREFIX%JOB_PARAMS where JOB_INSTANCE_ID = ?";
85 
86        private static final String FIND_JOB_NAMES = "SELECT distinct JOB_NAME from %PREFIX%JOB_INSTANCE order by JOB_NAME";
87 
88        private static final String FIND_LAST_JOBS_BY_NAME = "SELECT JOB_INSTANCE_ID, JOB_NAME from %PREFIX%JOB_INSTANCE where JOB_NAME = ? order by JOB_INSTANCE_ID desc";
89 
90        private DataFieldMaxValueIncrementer jobIncrementer;
91 
92        /**
93         * In this jdbc implementation a job id is obtained by asking the
94         * jobIncrementer (which is likely a sequence) for the next long value, and
95         * then passing the Id and parameter values into an INSERT statement.
96         * 
97         * @see JobInstanceDao#createJobInstance(String, JobParameters)
98         * @throws IllegalArgumentException
99         *             if any {@link JobParameters} fields are null.
100         */
101        public JobInstance createJobInstance(String jobName,
102                        JobParameters jobParameters) {
103 
104                Assert.notNull(jobName, "Job name must not be null.");
105                Assert.notNull(jobParameters, "JobParameters must not be null.");
106 
107                Assert.state(getJobInstance(jobName, jobParameters) == null,
108                                "JobInstance must not already exist");
109 
110                Long jobId = jobIncrementer.nextLongValue();
111 
112                JobInstance jobInstance = new JobInstance(jobId, jobParameters, jobName);
113                jobInstance.incrementVersion();
114 
115                Object[] parameters = new Object[] { jobId, jobName,
116                                createJobKey(jobParameters), jobInstance.getVersion() };
117                getJdbcTemplate().getJdbcOperations().update(
118                                getQuery(CREATE_JOB_INSTANCE),
119                                parameters,
120                                new int[] { Types.INTEGER, Types.VARCHAR, Types.VARCHAR,
121                                                Types.INTEGER });
122 
123                insertJobParameters(jobId, jobParameters);
124 
125                return jobInstance;
126        }
127 
128        protected String createJobKey(JobParameters jobParameters) {
129 
130                Map<String, JobParameter> props = jobParameters.getParameters();
131                StringBuffer stringBuffer = new StringBuffer();
132                List<String> keys = new ArrayList<String>(props.keySet());
133                Collections.sort(keys);
134                for (String key : keys) {
135                        stringBuffer.append(key + "=" + props.get(key).toString() + ";");
136                }
137 
138                MessageDigest digest;
139                try {
140                        digest = MessageDigest.getInstance("MD5");
141                } catch (NoSuchAlgorithmException e) {
142                        throw new IllegalStateException(
143                                        "MD5 algorithm not available.  Fatal (should be in the JDK).");
144                }
145 
146                try {
147                        byte[] bytes = digest.digest(stringBuffer.toString().getBytes(
148                                        "UTF-8"));
149                        return String.format("%032x", new BigInteger(1, bytes));
150                } catch (UnsupportedEncodingException e) {
151                        throw new IllegalStateException(
152                                        "UTF-8 encoding not available.  Fatal (should be in the JDK).");
153                }
154        }
155 
156        /**
157         * Convenience method that inserts all parameters from the provided
158         * JobParameters.
159         * 
160         */
161        private void insertJobParameters(Long jobId, JobParameters jobParameters) {
162 
163                for (Entry<String, JobParameter> entry : jobParameters.getParameters()
164                                .entrySet()) {
165                        JobParameter jobParameter = entry.getValue();
166                        insertParameter(jobId, jobParameter.getType(), entry.getKey(),
167                                        jobParameter.getValue());
168                }
169        }
170 
171        /**
172         * Convenience method that inserts an individual records into the
173         * JobParameters table.
174         */
175        private void insertParameter(Long jobId, ParameterType type, String key,
176                        Object value) {
177 
178                Object[] args = new Object[0];
179                int[] argTypes = new int[] { Types.BIGINT, Types.VARCHAR,
180                                Types.VARCHAR, Types.VARCHAR, Types.TIMESTAMP, Types.BIGINT,
181                                Types.DOUBLE };
182 
183                if (type == ParameterType.STRING) {
184                        args = new Object[] { jobId, key, type, value, new Timestamp(0L),
185                                        0L, 0D };
186                } else if (type == ParameterType.LONG) {
187                        args = new Object[] { jobId, key, type, "", new Timestamp(0L),
188                                        value, new Double(0) };
189                } else if (type == ParameterType.DOUBLE) {
190                        args = new Object[] { jobId, key, type, "", new Timestamp(0L), 0L,
191                                        value };
192                } else if (type == ParameterType.DATE) {
193                        args = new Object[] { jobId, key, type, "", value, 0L, 0D };
194                }
195 
196                getJdbcTemplate().getJdbcOperations().update(
197                                getQuery(CREATE_JOB_PARAMETERS), args, argTypes);
198        }
199 
200        /**
201         * The job table is queried for <strong>any</strong> jobs that match the
202         * given identifier, adding them to a list via the RowMapper callback.
203         * 
204         * @see JobInstanceDao#getJobInstance(String, JobParameters)
205         * @throws IllegalArgumentException
206         *             if any {@link JobParameters} fields are null.
207         */
208        public JobInstance getJobInstance(final String jobName,
209                        final JobParameters jobParameters) {
210 
211                Assert.notNull(jobName, "Job name must not be null.");
212                Assert.notNull(jobParameters, "JobParameters must not be null.");
213 
214                String jobKey = createJobKey(jobParameters);
215 
216                ParameterizedRowMapper<JobInstance> rowMapper = new JobInstanceRowMapper(
217                                jobParameters);
218 
219                List<JobInstance> instances;
220                if (StringUtils.hasLength(jobKey)) {
221                        instances = getJdbcTemplate().query(getQuery(FIND_JOBS_WITH_KEY),
222                                        rowMapper, jobName, jobKey);
223                } else {
224                        instances = getJdbcTemplate().query(
225                                        getQuery(FIND_JOBS_WITH_EMPTY_KEY), rowMapper, jobName,
226                                        jobKey);
227                }
228 
229                if (instances.isEmpty()) {
230                        return null;
231                } else {
232                        Assert.state(instances.size() == 1);
233                        return instances.get(0);
234                }
235        }
236 
237        /*
238         * (non-Javadoc)
239         * 
240         * @see
241         * org.springframework.batch.core.repository.dao.JobInstanceDao#getJobInstance
242         * (java.lang.Long)
243         */
244        public JobInstance getJobInstance(Long instanceId) {
245 
246                try {
247                        return getJdbcTemplate().queryForObject(getQuery(GET_JOB_FROM_ID),
248                                        new JobInstanceRowMapper(), instanceId);
249                } catch (EmptyResultDataAccessException e) {
250                        return null;
251                }
252 
253        }
254 
255        /**
256         * @param instanceId
257         * @return
258         */
259        private JobParameters getJobParameters(Long instanceId) {
260                final Map<String, JobParameter> map = new HashMap<String, JobParameter>();
261                RowCallbackHandler handler = new RowCallbackHandler() {
262                        public void processRow(ResultSet rs) throws SQLException {
263                                ParameterType type = ParameterType.valueOf(rs.getString(3));
264                                JobParameter value = null;
265                                if (type == ParameterType.STRING) {
266                                        value = new JobParameter(rs.getString(4));
267                                } else if (type == ParameterType.LONG) {
268                                        value = new JobParameter(rs.getLong(6));
269                                } else if (type == ParameterType.DOUBLE) {
270                                        value = new JobParameter(rs.getDouble(7));
271                                } else if (type == ParameterType.DATE) {
272                                        value = new JobParameter(rs.getTimestamp(5));
273                                }
274                                // No need to assert that value is not null because it's an enum
275                                map.put(rs.getString(2), value);
276                        }
277                };
278                getJdbcTemplate().getJdbcOperations().query(
279                                getQuery(FIND_PARAMS_FROM_ID), new Object[] { instanceId },
280                                handler);
281                return new JobParameters(map);
282        }
283 
284        /*
285         * (non-Javadoc)
286         * 
287         * @see
288         * org.springframework.batch.core.repository.dao.JobInstanceDao#getJobNames
289         * ()
290         */
291        public List<String> getJobNames() {
292                return getJdbcTemplate().query(getQuery(FIND_JOB_NAMES),
293                                new ParameterizedRowMapper<String>() {
294                                        public String mapRow(ResultSet rs, int rowNum)
295                                                        throws SQLException {
296                                                return rs.getString(1);
297                                        }
298                                });
299        }
300 
301        /*
302         * (non-Javadoc)
303         * 
304         * @seeorg.springframework.batch.core.repository.dao.JobInstanceDao#
305         * getLastJobInstances(java.lang.String, int)
306         */
307        public List<JobInstance> getJobInstances(String jobName, final int start,
308                        final int count) {
309 
310                ResultSetExtractor extractor = new ResultSetExtractor() {
311 
312                        private List<JobInstance> list = new ArrayList<JobInstance>();
313 
314                        public Object extractData(ResultSet rs) throws SQLException,
315                                        DataAccessException {
316                                int rowNum = 0;
317                                while (rowNum < start && rs.next()) {
318                                        rowNum++;
319                                }
320                                while (rowNum < start + count && rs.next()) {
321                                        ParameterizedRowMapper<JobInstance> rowMapper = new JobInstanceRowMapper();
322                                        list.add(rowMapper.mapRow(rs, rowNum));
323                                        rowNum++;
324                                }
325                                return list;
326                        }
327 
328                };
329 
330                @SuppressWarnings("unchecked")
331                List<JobInstance> result = (List<JobInstance>) getJdbcTemplate()
332                                .getJdbcOperations().query(getQuery(FIND_LAST_JOBS_BY_NAME),
333                                                new Object[] { jobName }, extractor);
334 
335                return result;
336        }
337 
338        /*
339         * (non-Javadoc)
340         * 
341         * @see
342         * org.springframework.batch.core.repository.dao.JobInstanceDao#getJobInstance
343         * (org.springframework.batch.core.JobExecution)
344         */
345        public JobInstance getJobInstance(JobExecution jobExecution) {
346 
347                try {
348                        return getJdbcTemplate().queryForObject(
349                                        getQuery(GET_JOB_FROM_EXECUTION_ID),
350                                        new JobInstanceRowMapper(), jobExecution.getId());
351                } catch (EmptyResultDataAccessException e) {
352                        return null;
353                }
354        }
355 
356        /**
357         * Setter for {@link DataFieldMaxValueIncrementer} to be used when
358         * generating primary keys for {@link JobInstance} instances.
359         * 
360         * @param jobIncrementer
361         *            the {@link DataFieldMaxValueIncrementer}
362         */
363        public void setJobIncrementer(DataFieldMaxValueIncrementer jobIncrementer) {
364                this.jobIncrementer = jobIncrementer;
365        }
366 
367        public void afterPropertiesSet() throws Exception {
368                super.afterPropertiesSet();
369                Assert.notNull(jobIncrementer);
370        }
371 
372        /**
373         * @author Dave Syer
374         * 
375         */
376        private final class JobInstanceRowMapper implements
377                        ParameterizedRowMapper<JobInstance> {
378 
379                private JobParameters jobParameters;
380 
381                public JobInstanceRowMapper() {
382                }
383 
384                public JobInstanceRowMapper(JobParameters jobParameters) {
385                        this.jobParameters = jobParameters;
386                }
387 
388                public JobInstance mapRow(ResultSet rs, int rowNum) throws SQLException {
389                        Long id = rs.getLong(1);
390                        if (jobParameters == null) {
391                                jobParameters = getJobParameters(id);
392                        }
393                        JobInstance jobInstance = new JobInstance(rs.getLong(1),
394                                        jobParameters, rs.getString(2));
395                        // should always be at version=0 because they never get updated
396                        jobInstance.incrementVersion();
397                        return jobInstance;
398                }
399        }
400}

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