View Javadoc
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  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   * @author Dave Syer
48   * @author Michael Minella
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  	 * @param dataSource the dataSource to set
70  	 */
71  	public void setDataSource(DataSource dataSource) {
72  		this.dataSource = dataSource;
73  	}
74  
75  	/**
76  	 * @see JdbcJobExecutionDao#afterPropertiesSet()
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 	 * @return a {@link PagingQueryProvider} with a where clause to narrow the
170 	 * query
171 	 * @throws Exception
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 }