1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.springframework.batch.admin.service;
17
18 import java.sql.ResultSet;
19 import java.sql.SQLException;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.LinkedHashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28
29 import javax.sql.DataSource;
30
31 import org.springframework.batch.core.BatchStatus;
32 import org.springframework.batch.core.ExitStatus;
33 import org.springframework.batch.core.StepExecution;
34 import org.springframework.batch.core.repository.dao.JdbcJobExecutionDao;
35 import org.springframework.batch.core.repository.dao.JdbcStepExecutionDao;
36 import org.springframework.batch.item.database.Order;
37 import org.springframework.batch.item.database.PagingQueryProvider;
38 import org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean;
39 import org.springframework.batch.support.PatternMatcher;
40 import org.springframework.dao.IncorrectResultSizeDataAccessException;
41 import org.springframework.jdbc.core.JdbcTemplate;
42 import org.springframework.jdbc.core.RowMapper;
43 import org.springframework.jdbc.support.incrementer.AbstractDataFieldMaxValueIncrementer;
44 import org.springframework.util.Assert;
45
46
47
48
49
50
51 public class JdbcSearchableStepExecutionDao extends JdbcStepExecutionDao implements SearchableStepExecutionDao {
52
53 private static final String STEP_EXECUTIONS_FOR_JOB = "SELECT distinct STEP_NAME from %PREFIX%STEP_EXECUTION S, %PREFIX%JOB_EXECUTION E, %PREFIX%JOB_INSTANCE I "
54 + "where S.JOB_EXECUTION_ID = E.JOB_EXECUTION_ID AND E.JOB_INSTANCE_ID = I.JOB_INSTANCE_ID AND I.JOB_NAME = ?";
55
56 private static final String COUNT_STEP_EXECUTIONS_FOR_STEP = "SELECT COUNT(STEP_EXECUTION_ID) from %PREFIX%STEP_EXECUTION S, %PREFIX%JOB_EXECUTION E, %PREFIX%JOB_INSTANCE I "
57 + "where S.JOB_EXECUTION_ID = E.JOB_EXECUTION_ID AND E.JOB_INSTANCE_ID = I.JOB_INSTANCE_ID AND I.JOB_NAME = ? AND S.STEP_NAME = ?";
58
59 private static final String COUNT_STEP_EXECUTIONS_FOR_STEP_PATTERN = "SELECT COUNT(STEP_EXECUTION_ID) from %PREFIX%STEP_EXECUTION S, %PREFIX%JOB_EXECUTION E, %PREFIX%JOB_INSTANCE I"
60 + " where S.JOB_EXECUTION_ID = E.JOB_EXECUTION_ID AND E.JOB_INSTANCE_ID = I.JOB_INSTANCE_ID AND I.JOB_NAME = ? AND S.STEP_NAME like ?";
61
62 private static final String FIELDS = "S.STEP_EXECUTION_ID, S.STEP_NAME, S.START_TIME, S.END_TIME, S.STATUS, S.COMMIT_COUNT,"
63 + " S.READ_COUNT, S.FILTER_COUNT, S.WRITE_COUNT, S.EXIT_CODE, S.EXIT_MESSAGE, S.READ_SKIP_COUNT, S.WRITE_SKIP_COUNT,"
64 + " S.PROCESS_SKIP_COUNT, S.ROLLBACK_COUNT, S.LAST_UPDATED, S.VERSION";
65
66 private DataSource dataSource;
67
68
69
70
71 public void setDataSource(DataSource dataSource) {
72 this.dataSource = dataSource;
73 }
74
75
76
77
78 @Override
79 public void afterPropertiesSet() throws Exception {
80
81 Assert.state(dataSource != null, "DataSource must be provided");
82
83 if (getJdbcTemplate() == null) {
84 setJdbcTemplate(new JdbcTemplate(dataSource));
85 }
86 setStepExecutionIncrementer(new AbstractDataFieldMaxValueIncrementer() {
87 @Override
88 protected long getNextKey() {
89 return 0;
90 }
91 });
92
93 super.afterPropertiesSet();
94
95 }
96
97 public Collection<String> findStepNamesForJobExecution(String jobName, String excludesPattern) {
98
99 List<String> list = getJdbcTemplate().query(getQuery(STEP_EXECUTIONS_FOR_JOB), new RowMapper<String>() {
100 public String mapRow(java.sql.ResultSet rs, int rowNum) throws java.sql.SQLException {
101 return rs.getString(1);
102 }
103 }, jobName);
104
105 Set<String> stepNames = new LinkedHashSet<String>(list);
106 for (Iterator<String> iterator = stepNames.iterator(); iterator.hasNext();) {
107 String name = iterator.next();
108 if (PatternMatcher.match(excludesPattern, name)) {
109 iterator.remove();
110 }
111 }
112
113 return stepNames;
114
115 }
116
117 public Collection<StepExecution> findStepExecutions(String jobName, String stepName, int start, int count) {
118
119 String whereClause;
120
121 if (jobName.contains("*")) {
122 whereClause = "JOB_NAME like ?";
123 jobName = jobName.replace("*", "%");
124 }
125 else {
126 whereClause = "JOB_NAME = ?";
127 }
128
129 if (stepName.contains("*")) {
130 whereClause = whereClause + " AND STEP_NAME like ?";
131 stepName = stepName.replace("*", "%");
132 }
133 else {
134 whereClause = whereClause + " AND STEP_NAME = ?";
135 }
136
137 PagingQueryProvider queryProvider = getPagingQueryProvider(whereClause);
138
139 List<StepExecution> stepExecutions;
140 if (start <= 0) {
141 stepExecutions = getJdbcTemplate().query(queryProvider.generateFirstPageQuery(count),
142 new StepExecutionRowMapper(), jobName, stepName);
143 }
144 else {
145 try {
146 Long startAfterValue = getJdbcTemplate().queryForObject(
147 queryProvider.generateJumpToItemQuery(start, count), Long.class, jobName, stepName);
148 stepExecutions = getJdbcTemplate().query(queryProvider.generateRemainingPagesQuery(count),
149 new StepExecutionRowMapper(), jobName, stepName, startAfterValue);
150 }
151 catch (IncorrectResultSizeDataAccessException e) {
152 return Collections.emptyList();
153 }
154 }
155
156 return stepExecutions;
157
158 }
159
160 public int countStepExecutions(String jobName, String stepName) {
161 if (stepName.contains("*")) {
162 return getJdbcTemplate().queryForObject(getQuery(COUNT_STEP_EXECUTIONS_FOR_STEP_PATTERN), Integer.class, jobName,
163 stepName.replace("*", "%"));
164 }
165 return getJdbcTemplate().queryForObject(getQuery(COUNT_STEP_EXECUTIONS_FOR_STEP), Integer.class, jobName, stepName);
166 }
167
168
169
170
171
172
173 private PagingQueryProvider getPagingQueryProvider(String whereClause) {
174 SqlPagingQueryProviderFactoryBean factory = new SqlPagingQueryProviderFactoryBean();
175 factory.setDataSource(dataSource);
176 factory.setFromClause(getQuery("%PREFIX%STEP_EXECUTION S, %PREFIX%JOB_EXECUTION J, %PREFIX%JOB_INSTANCE I"));
177 factory.setSelectClause(FIELDS);
178 Map<String, Order> sortKeys = new HashMap<String, Order>();
179 sortKeys.put("STEP_EXECUTION_ID", Order.DESCENDING);
180 factory.setSortKeys(sortKeys);
181 if (whereClause != null) {
182 factory.setWhereClause(whereClause
183 + " AND S.JOB_EXECUTION_ID = J.JOB_EXECUTION_ID AND J.JOB_INSTANCE_ID = I.JOB_INSTANCE_ID");
184 }
185 try {
186 return (PagingQueryProvider) factory.getObject();
187 }
188 catch (Exception e) {
189 throw new IllegalStateException("Unexpected exception creating paging query provide", e);
190 }
191 }
192
193 private static class StepExecutionRowMapper implements RowMapper<StepExecution> {
194
195 public StepExecution mapRow(ResultSet rs, int rowNum) throws SQLException {
196 StepExecution stepExecution = new StepExecution(rs.getString(2), null);
197 stepExecution.setId(rs.getLong(1));
198 stepExecution.setStartTime(rs.getTimestamp(3));
199 stepExecution.setEndTime(rs.getTimestamp(4));
200 stepExecution.setStatus(BatchStatus.valueOf(rs.getString(5)));
201 stepExecution.setCommitCount(rs.getInt(6));
202 stepExecution.setReadCount(rs.getInt(7));
203 stepExecution.setFilterCount(rs.getInt(8));
204 stepExecution.setWriteCount(rs.getInt(9));
205 stepExecution.setExitStatus(new ExitStatus(rs.getString(10), rs.getString(11)));
206 stepExecution.setReadSkipCount(rs.getInt(12));
207 stepExecution.setWriteSkipCount(rs.getInt(13));
208 stepExecution.setProcessSkipCount(rs.getInt(14));
209 stepExecution.setRollbackCount(rs.getInt(15));
210 stepExecution.setLastUpdated(rs.getTimestamp(16));
211 stepExecution.setVersion(rs.getInt(17));
212 return stepExecution;
213 }
214
215 }
216
217 }