SCT-7 Add TaskExplorer implementation

* Supports SimpleTaskExplorer that accepts a TaskExecutionDao.
* Updated Dao's to support the explorer query requirements
* Added tests.

This resolves spring-cloud/spring-cloud-task#7
This commit is contained in:
Glenn Renfro
2015-12-09 18:13:03 -05:00
committed by Michael Minella
parent 5c3795e7c7
commit b62cb184c0
14 changed files with 762 additions and 13 deletions

View File

@@ -24,7 +24,9 @@ import org.springframework.cloud.task.repository.TaskExplorer;
import org.springframework.cloud.task.repository.TaskRepository;
import org.springframework.cloud.task.repository.dao.JdbcTaskExecutionDao;
import org.springframework.cloud.task.repository.dao.MapTaskExecutionDao;
import org.springframework.cloud.task.repository.support.JdbcTaskExplorerFactoryBean;
import org.springframework.cloud.task.repository.support.JdbcTaskRepositoryFactoryBean;
import org.springframework.cloud.task.repository.support.MapTaskExplorerFactoryBean;
import org.springframework.cloud.task.repository.support.MapTaskRepositoryFactoryBean;
import org.springframework.cloud.task.repository.support.SimpleTaskRepository;
@@ -48,6 +50,8 @@ public class DefaultTaskConfigurer implements TaskConfigurer {
private TaskRepository taskRepository;
private TaskExplorer taskExplorer;
public DefaultTaskConfigurer(){
initialize();
}
@@ -62,8 +66,7 @@ public class DefaultTaskConfigurer implements TaskConfigurer {
}
public TaskExplorer getTaskExplorer() {
throw new UnsupportedOperationException("method not implemented");
//TODO if datasource != null use TaskRepositoryFactoryBean from above like initialize method in DefaultBatchConfigurer
return taskExplorer;
}
private void initialize(){
@@ -72,11 +75,18 @@ public class DefaultTaskConfigurer implements TaskConfigurer {
MapTaskRepositoryFactoryBean mapTaskRepositoryFactoryBean =
new MapTaskRepositoryFactoryBean();
taskRepository = mapTaskRepositoryFactoryBean.getObject();
MapTaskExplorerFactoryBean mapTaskExplorerFactoryBean =
new MapTaskExplorerFactoryBean();
taskExplorer = mapTaskExplorerFactoryBean.getObject();
}
else {
JdbcTaskRepositoryFactoryBean jdbcTaskRepositoryFactoryBean =
new JdbcTaskRepositoryFactoryBean(dataSource);
taskRepository = jdbcTaskRepositoryFactoryBean.getObject();
JdbcTaskExplorerFactoryBean jdbcTaskExplorerFactoryBean =
new JdbcTaskExplorerFactoryBean(dataSource);
taskExplorer = jdbcTaskExplorerFactoryBean.getObject();
}
}

View File

@@ -32,7 +32,7 @@ public interface TaskExplorer {
* @param executionId the task execution id
* @return the {@link TaskExecution} with this id, or null if not found
*/
public TaskExecution getTaskExecution(Long executionId);
public TaskExecution getTaskExecution(String executionId);
/**

View File

@@ -16,15 +16,26 @@
package org.springframework.cloud.task.repository.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.sql.DataSource;
import org.springframework.cloud.task.repository.TaskExecution;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -51,6 +62,30 @@ public class JdbcTaskExecutionDao implements TaskExecutionDao {
+ "EXIT_MESSAGE = ?, LAST_UPDATED = ?, STATUS_CODE = ? "
+ "where TASK_EXECUTION_ID = ?";
private static final String GET_EXECUTION_BY_ID = "SELECT TASK_EXECUTION_ID, " +
"START_TIME, END_TIME, TASK_NAME, EXIT_CODE, "
+ "EXIT_MESSAGE, LAST_UPDATED, STATUS_CODE "
+ "from %PREFIX%EXECUTION where TASK_EXECUTION_ID = ?";
private static final String FIND_PARAMS_FROM_ID = "SELECT TASK_EXECUTION_ID, "
+ "TASK_PARAM from %PREFIX%EXECUTION_PARAMS where TASK_EXECUTION_ID = ?";
private static final String TASK_EXECUTION_COUNT = "SELECT COUNT(*) FROM " +
"%PREFIX%EXECUTION where TASK_NAME = ?";
private static final String FIND_RUNNING_TASK_EXECUTIONS = "SELECT TASK_EXECUTION_ID, "
+ "START_TIME, END_TIME, TASK_NAME, EXIT_CODE, "
+ "EXIT_MESSAGE, LAST_UPDATED, STATUS_CODE "
+ "from %PREFIX%EXECUTION where TASK_NAME = ? AND END_TIME IS NULL "
+ "order by TASK_EXECUTION_ID";
private static final String FIND_TASK_EXECUTIONS = "SELECT TASK_EXECUTION_ID, "
+ "START_TIME, END_TIME, TASK_NAME, EXIT_CODE, "
+ "EXIT_MESSAGE, LAST_UPDATED, STATUS_CODE "
+ "from %PREFIX%EXECUTION where TASK_NAME = ? "
+ "order by TASK_EXECUTION_ID";
final String FIND_TASK_NAMES = "SELECT distinct TASK_NAME from %PREFIX%EXECUTION order by TASK_NAME";
private static final String DEFAULT_TABLE_PREFIX = "TASK_";
@@ -75,7 +110,7 @@ public class JdbcTaskExecutionDao implements TaskExecutionDao {
parameters,
new int[]{ Types.VARCHAR, Types.TIMESTAMP, Types.TIMESTAMP, Types.VARCHAR,
Types.INTEGER, Types.VARCHAR, Types.TIMESTAMP, Types.VARCHAR });
insertJobParameters(taskExecution.getExecutionId(), taskExecution.getParameters());
insertTaskParameters(taskExecution.getExecutionId(), taskExecution.getParameters());
}
@Override
@@ -109,6 +144,92 @@ public class JdbcTaskExecutionDao implements TaskExecutionDao {
this.tablePrefix = tablePrefix;
}
@Override
public TaskExecution getTaskExecution(String executionId) {
try {
TaskExecution taskExecution = jdbcTemplate.queryForObject(getQuery(GET_EXECUTION_BY_ID),
new TaskExecutionRowMapper(), executionId);
taskExecution.setParameters(getTaskParameters(executionId));
return taskExecution;
}
catch (EmptyResultDataAccessException e) {
return null;
}
}
@Override
public long getTaskExecutionCount(String taskName) {
try {
return jdbcTemplate.queryForObject(
getQuery(TASK_EXECUTION_COUNT), new Object[] { taskName }, Long.class);
}
catch (EmptyResultDataAccessException e) {
return 0;
}
}
@Override
public Set<TaskExecution> findRunningTaskExecutions(String taskName) {
final Set<TaskExecution> result = new HashSet<TaskExecution>();
RowCallbackHandler handler = new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
TaskExecutionRowMapper mapper = new TaskExecutionRowMapper();
result.add(mapper.mapRow(rs, 0));
}
};
jdbcTemplate.query(getQuery(FIND_RUNNING_TASK_EXECUTIONS),
new Object[] { taskName }, handler);
return result;
}
@Override
public List<TaskExecution> getTaskExecutionsByName(String taskName, final int start,
final int count) {
ResultSetExtractor<List<TaskExecution>> extractor =
new ResultSetExtractor<List<TaskExecution>>() {
private List<TaskExecution> list = new ArrayList<TaskExecution>();
@Override
public List<TaskExecution> extractData(ResultSet rs) throws SQLException,
DataAccessException {
int rowNum = 0;
while (rowNum < start && rs.next()) {
rowNum++;
}
while (rowNum < start + count && rs.next()) {
RowMapper<TaskExecution> rowMapper = new TaskExecutionRowMapper();
list.add(rowMapper.mapRow(rs, rowNum));
rowNum++;
}
return list;
}
};
List<TaskExecution> result = jdbcTemplate.query(getQuery(FIND_TASK_EXECUTIONS),
new Object[] { taskName }, extractor);
return result;
}
@Override
public List<String> getTaskNames() {
return jdbcTemplate.query(getQuery(FIND_TASK_NAMES),
new RowMapper<String>() {
@Override
public String mapRow(ResultSet rs, int rowNum)
throws SQLException {
return rs.getString(1);
}
});
}
private String getQuery(String base) {
return StringUtils.replace(base, "%PREFIX%", tablePrefix);
}
@@ -120,7 +241,7 @@ public class JdbcTaskExecutionDao implements TaskExecutionDao {
* @param executionId The executionId to which the params are associated.
* @param taskParameters The parameters to be stored.
*/
private void insertJobParameters(String executionId, List<String> taskParameters) {
private void insertTaskParameters(String executionId, List<String> taskParameters) {
for (String param : taskParameters) {
insertParameter(executionId, param);
}
@@ -136,4 +257,46 @@ public class JdbcTaskExecutionDao implements TaskExecutionDao {
jdbcTemplate.update(getQuery(CREATE_TASK_PARAMETER), args, argTypes);
}
private List<String> getTaskParameters(String executionId){
final List<String> params= new ArrayList<>();
RowCallbackHandler handler = new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
params.add(rs.getString(2));
}
};
jdbcTemplate.query(getQuery(FIND_PARAMS_FROM_ID), new Object[] { executionId },
handler);
return Collections.unmodifiableList(params);
}
/**
* Re-usable mapper for {@link TaskExecution} instances.
*
* @author Dave Syer
*
*/
private final class TaskExecutionRowMapper implements RowMapper<TaskExecution> {
public TaskExecutionRowMapper() {
}
@Override
public TaskExecution mapRow(ResultSet rs, int rowNum) throws SQLException {
String id = rs.getString(1);
TaskExecution taskExecution=new TaskExecution();
taskExecution.setExecutionId(rs.getString(1));
taskExecution.setStartTime(rs.getTimestamp("START_TIME"));
taskExecution.setEndTime(rs.getTimestamp("END_TIME"));
taskExecution.setExitCode(rs.getInt("EXIT_CODE"));
taskExecution.setExitMessage(rs.getString("EXIT_MESSAGE"));
taskExecution.setStatusCode(rs.getString("STATUS_CODE"));
taskExecution.setTaskName(rs.getString("TASK_NAME"));
taskExecution.setParameters(getTaskParameters(id));
return taskExecution;
}
}
}

View File

@@ -16,8 +16,13 @@
package org.springframework.cloud.task.repository.dao;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -46,7 +51,67 @@ public class MapTaskExecutionDao implements TaskExecutionDao {
taskExecutions.put(taskExecution.getExecutionId(), taskExecution);
}
public Map<String, TaskExecution> getTaskExecutions(){
@Override
public TaskExecution getTaskExecution(String executionId) {
return taskExecutions.get(executionId);
}
@Override
public long getTaskExecutionCount(String taskName) {
int count = 0;
for (Map.Entry<String, TaskExecution> entry : taskExecutions.entrySet()) {
if (entry.getValue().getTaskName().equals(taskName)) {
count++;
}
}
return count;
}
@Override
public Set<TaskExecution> findRunningTaskExecutions(String taskName) {
Set<TaskExecution> result = new HashSet<>();
for (Map.Entry<String, TaskExecution> entry : taskExecutions.entrySet()) {
if (entry.getValue().getTaskName().equals(taskName) &&
entry.getValue().getEndTime() == null) {
result.add(entry.getValue());
}
}
return Collections.unmodifiableSet(result);
}
@Override
public List<TaskExecution> getTaskExecutionsByName(String taskName, int start, int count) {
List<TaskExecution> result = new ArrayList<>();
Set<TaskExecution> filteredSet = new HashSet<>();
for (Map.Entry<String, TaskExecution> entry : taskExecutions.entrySet()) {
if (entry.getValue().getTaskName().equals(taskName)) {
filteredSet.add(entry.getValue());
}
}
int rowNum = 0;
Iterator<TaskExecution> rs = filteredSet.iterator();
while (rowNum < start && rs.hasNext()) {
rs.next();
rowNum++;
}
while (rowNum < start + count && rs.hasNext()) {
result.add(rs.next());
rowNum++;
}
return Collections.unmodifiableList(result);
}
@Override
public List<String> getTaskNames() {
Set<String> result = new HashSet<>();
for (Map.Entry<String, TaskExecution> entry : taskExecutions.entrySet()) {
result.add(entry.getValue().getTaskName());
}
return Collections.unmodifiableList(new ArrayList(result));
}
public Map<String, TaskExecution> getTaskExecutions() {
return Collections.unmodifiableMap(taskExecutions);
}
}

View File

@@ -16,6 +16,9 @@
package org.springframework.cloud.task.repository.dao;
import java.util.List;
import java.util.Set;
import org.springframework.cloud.task.repository.TaskExecution;
/**
@@ -38,4 +41,46 @@ public interface TaskExecutionDao {
* @param taskExecution the taskExecution to be updated.
*/
void updateTaskExecution(TaskExecution taskExecution);
/**
* Retrieves a task execution from the task repository.
*
* @param executionId the uuid associated with the task execution.
* @return a fully qualified TaskExecution instance.
*/
TaskExecution getTaskExecution(String executionId);
/**
* Retrieves current number of task executions for a taskName.
*
* @param taskName the name of the task to search for in the repository.
* @return current number of task executions.
*/
long getTaskExecutionCount(String taskName);
/**
* Retrieves a set of task executions that are running for a taskName.
*
* @param taskName the name of the task to search for in the repository.
* @return set of running task executions.
*/
Set<TaskExecution> findRunningTaskExecutions(String taskName);
/**
* Retrieves a subset of task executions by task name, start location and size.
*
* @param taskName the name of the task to search for in the repository.
* @param start the position of the first entry to be returned from result set.
* @param count the number of entries to return
* @return a list that contains task executions from the query bound by the start
* position and count specified by the user.
*/
List<TaskExecution> getTaskExecutionsByName(String taskName, int start, int count);
/**
* Retrieves a sorted list of distinct task names for the task executions.
*
* @return a list of distinct task names from the task repository..
*/
public List<String> getTaskNames();
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2015 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.cloud.task.repository.support;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.task.repository.TaskExplorer;
import org.springframework.cloud.task.repository.dao.JdbcTaskExecutionDao;
import org.springframework.cloud.task.repository.dao.TaskExecutionDao;
/**
* Automates the creation of a {@link SimpleTaskExplorer} which will retrieve task
* execution data from a database.
*
* @author Glenn Renfro
*/
public class JdbcTaskExplorerFactoryBean {
public static final String DEFAULT_TABLE_PREFIX = "TASK_";
private static final Log logger = LogFactory.getLog(JdbcTaskExplorerFactoryBean.class);
private DataSource dataSource;
private String tablePrefix = DEFAULT_TABLE_PREFIX;
public JdbcTaskExplorerFactoryBean(){
}
public JdbcTaskExplorerFactoryBean(DataSource dataSource) {
if(dataSource != null) {
this.dataSource = dataSource;
}
}
/**
* Sets the table prefix for all the task meta-data tables.
* @param tablePrefix prefix prepended to task meta-data tables
*/
public void setTablePrefix(String tablePrefix) {
this.tablePrefix = tablePrefix;
}
/**
* Returns the a simpleTaskExplorer that utilizes a JdbcTaskExecutionDao
* @return instance of task repository.
*/
public TaskExplorer getObject(){
TaskExplorer taskExplorer = null;
logger.debug(String.format("Creating SimpleTaskExplorer that will use a %s",
JdbcTaskExecutionDao.class.getName()));
taskExplorer = new SimpleTaskExplorer(createJdbcTaskExecutionDao());
return taskExplorer;
}
private TaskExecutionDao createJdbcTaskExecutionDao() {
JdbcTaskExecutionDao dao = new JdbcTaskExecutionDao(dataSource);
dao.setTablePrefix(tablePrefix);
return dao;
}
}

View File

@@ -52,15 +52,15 @@ public class JdbcTaskRepositoryFactoryBean {
}
/**
* Sets the table prefix for all the batch meta-data tables.
* @param tablePrefix prefix prepended to batch meta-data tables
* Sets the table prefix for all the task meta-data tables.
* @param tablePrefix prefix prepended to task meta-data tables
*/
public void setTablePrefix(String tablePrefix) {
this.tablePrefix = tablePrefix;
}
/**
* Returns the a simpleTaskRepository that utilizes a MapTaskExecutionDao
* Returns the a simpleTaskRepository that utilizes a JdbcTaskExecutionDao
* @return instance of task repository.
*/
public TaskRepository getObject(){

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2015 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.cloud.task.repository.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.task.repository.TaskExplorer;
import org.springframework.cloud.task.repository.dao.MapTaskExecutionDao;
/**
* Automates the creation of a {@link SimpleTaskExplorer} which will retrieve task
* execution data from a in-memory map.
*
* @author Glenn Renfro
*/
public class MapTaskExplorerFactoryBean {
private static final Log logger = LogFactory.getLog(MapTaskExplorerFactoryBean.class);
public MapTaskExplorerFactoryBean(){
}
/**
* Returns the a simpleTaskExplorer that utilizes a MapTaskExecutionDao
* @return instance of task repository.
*/
public TaskExplorer getObject(){
TaskExplorer taskExplorer = null;
logger.debug(String.format("Creating SimpleTaskExplorer that will use a %s",
MapTaskExecutionDao.class.getName()));
taskExplorer = new SimpleTaskExplorer(new MapTaskExecutionDao());
return taskExplorer;
}
}

View File

@@ -30,7 +30,7 @@ import org.springframework.cloud.task.repository.TaskExplorer;
*/
public class NoOpTaskExplorer implements TaskExplorer {
public TaskExecution getTaskExecution(Long executionId) {
public TaskExecution getTaskExecution(String executionId) {
return null;
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2015 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.cloud.task.repository.support;
import java.util.List;
import java.util.Set;
import org.springframework.cloud.task.repository.TaskExecution;
import org.springframework.cloud.task.repository.TaskExplorer;
import org.springframework.cloud.task.repository.dao.TaskExecutionDao;
import org.springframework.util.Assert;
/**
* TaskExplorer for that gathers task information from a task repository.
*
* @author Glenn Renfro
*/
public class SimpleTaskExplorer implements TaskExplorer{
private TaskExecutionDao taskExecutionDao;
public SimpleTaskExplorer(TaskExecutionDao taskExecutionDao){
Assert.notNull(taskExecutionDao, "taskExecutionDao must not be null");
this.taskExecutionDao = taskExecutionDao;
}
@Override
public TaskExecution getTaskExecution(String executionId) {
return taskExecutionDao.getTaskExecution(executionId);
}
@Override
public Set<TaskExecution> findRunningTaskExecutions(String taskName) {
return taskExecutionDao.findRunningTaskExecutions(taskName);
}
@Override
public List<String> getTaskNames() {
return taskExecutionDao.getTaskNames();
}
@Override
public long getTaskExecutionCount(String taskName) {
return taskExecutionDao.getTaskExecutionCount(taskName);
}
@Override
public List<TaskExecution> getTaskExecutionsByName(String taskName, int start, int count) {
return taskExecutionDao.getTaskExecutionsByName(taskName, start, count);
}
}