/* * Copyright 2006-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.xd.dirt.job.dao; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.sql.DataSource; import org.springframework.batch.admin.service.JdbcSearchableJobExecutionDao; import org.springframework.batch.admin.service.SearchableJobExecutionDao; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.repository.dao.JdbcJobExecutionDao; import org.springframework.batch.item.database.Order; import org.springframework.batch.item.database.PagingQueryProvider; import org.springframework.batch.item.database.support.SqlPagingQueryProviderFactoryBean; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.incrementer.AbstractDataFieldMaxValueIncrementer; import org.springframework.util.Assert; import org.springframework.xd.dirt.batch.tasklet.JobLaunchingTasklet; /** * @author Dave Syer * @author Michael Minella * @author Gunnar Hillert * */ public class XdJdbcSearchableJobExecutionDao extends JdbcSearchableJobExecutionDao { private static final String FIELDS = "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, I.JOB_INSTANCE_ID, I.JOB_NAME"; private PagingQueryProvider allExecutionsPagingQueryProvider; private PagingQueryProvider childJobExecutionsPagingQueryProvider; private DataSource dataSource; /** * @param dataSource the dataSource to set */ @Override public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; super.setDataSource(dataSource); } /** * @see JdbcJobExecutionDao#afterPropertiesSet() */ @Override public void afterPropertiesSet() throws Exception { Assert.state(dataSource != null, "DataSource must be provided"); if (getJdbcTemplate() == null) { setJdbcTemplate(new JdbcTemplate(dataSource)); } setJobExecutionIncrementer(new AbstractDataFieldMaxValueIncrementer() { @Override protected long getNextKey() { return 0; } }); final String subQuery = getQuery( "not exists (select * from %PREFIX%JOB_EXECUTION_PARAMS a where key_name = '" + JobLaunchingTasklet.XD_PARENT_JOB_EXECUTION_ID + "' and E.JOB_EXECUTION_ID=a.JOB_EXECUTION_ID)"); allExecutionsPagingQueryProvider = getPagingQueryProvider(subQuery); final String childJobExecutionsSubQuery = getQuery( "exists (select * from %PREFIX%JOB_EXECUTION_PARAMS a where key_name = '" + JobLaunchingTasklet.XD_PARENT_JOB_EXECUTION_ID + "' and E.JOB_EXECUTION_ID=a.JOB_EXECUTION_ID and string_val=?)"); childJobExecutionsPagingQueryProvider = getPagingQueryProvider(childJobExecutionsSubQuery); super.afterPropertiesSet(); } /** * @return a {@link PagingQueryProvider} with a where clause to narrow the * query * @throws Exception */ private PagingQueryProvider getPagingQueryProvider(String whereClause) throws Exception { SqlPagingQueryProviderFactoryBean factory = new SqlPagingQueryProviderFactoryBean(); factory.setDataSource(dataSource); String fromClause = "%PREFIX%JOB_EXECUTION E, %PREFIX%JOB_INSTANCE I"; factory.setFromClause(getQuery(fromClause)); factory.setSelectClause(FIELDS); Map<String, Order> sortKeys = new HashMap<String, Order>(); sortKeys.put("JOB_EXECUTION_ID", Order.DESCENDING); factory.setSortKeys(sortKeys); whereClause = "E.JOB_INSTANCE_ID=I.JOB_INSTANCE_ID" + (whereClause == null ? "" : " and " + whereClause); factory.setWhereClause(whereClause); return factory.getObject(); } /** * @see SearchableJobExecutionDao#getJobExecutions(int, int) */ public List<JobExecution> getTopLevelJobExecutions(int start, int count) { if (start <= 0) { return getJdbcTemplate().query(allExecutionsPagingQueryProvider.generateFirstPageQuery(count), new JobExecutionRowMapper()); } try { Long startAfterValue = getJdbcTemplate().queryForObject( allExecutionsPagingQueryProvider.generateJumpToItemQuery(start, count), Long.class); return getJdbcTemplate().query(allExecutionsPagingQueryProvider.generateRemainingPagesQuery(count), new JobExecutionRowMapper(), startAfterValue); } catch (IncorrectResultSizeDataAccessException e) { return Collections.emptyList(); } } public int countTopLevelJobExecutions() { return getJdbcTemplate().queryForObject(getQuery("SELECT COUNT(1) from %PREFIX%JOB_EXECUTION bje where " + "not exists (select 1 from %PREFIX%JOB_EXECUTION_PARAMS bje_params where key_name = '" + JobLaunchingTasklet.XD_PARENT_JOB_EXECUTION_ID + "' and bje.JOB_EXECUTION_ID=bje_params.JOB_EXECUTION_ID)"), Integer.class); } /** * * @param jobExecutionId the execution id. * @return the list of {@link JobExecution}s. */ public List<JobExecution> getChildJobExecutions(long jobExecutionId) { try { return getJdbcTemplate().query(childJobExecutionsPagingQueryProvider.generateFirstPageQuery(100000), new JobExecutionRowMapper(), String.valueOf(jobExecutionId)); } catch (Exception e) { throw new IllegalStateException(e); } } /** * * @param jobExecutionId the execution id. * @return the list of {@link JobExecution}s. */ public boolean isComposedJobExecution(long jobExecutionId) { String query = getQuery("select count(*) from %PREFIX%JOB_EXECUTION_PARAMS a where key_name = '" + JobLaunchingTasklet.XD_PARENT_JOB_EXECUTION_ID + "' and string_val = ?"); int count = getJdbcTemplate().queryForObject(query, Integer.class, String.valueOf(jobExecutionId)); return count > 0 ? true : false; } }