From b62cb184c07b41e27ebe889b53536368eb06275b Mon Sep 17 00:00:00 2001 From: Glenn Renfro Date: Wed, 9 Dec 2015 18:13:03 -0500 Subject: [PATCH] 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 --- .../configuration/DefaultTaskConfigurer.java | 14 +- .../cloud/task/repository/TaskExplorer.java | 2 +- .../repository/dao/JdbcTaskExecutionDao.java | 167 +++++++++++- .../repository/dao/MapTaskExecutionDao.java | 67 ++++- .../task/repository/dao/TaskExecutionDao.java | 45 ++++ .../support/JdbcTaskExplorerFactoryBean.java | 79 ++++++ .../JdbcTaskRepositoryFactoryBean.java | 6 +- .../support/MapTaskExplorerFactoryBean.java | 50 ++++ .../repository/support/NoOpTaskExplorer.java | 2 +- .../support/SimpleTaskExplorer.java | 65 +++++ .../support/NoOpTaskExplorerTests.java | 2 +- .../support/SimpleTaskExplorerTests.java | 247 ++++++++++++++++++ .../support/TaskDatabaseInitializerTests.java | 3 +- .../cloud/task/util/TestVerifierUtils.java | 26 ++ 14 files changed, 762 insertions(+), 13 deletions(-) create mode 100644 spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/JdbcTaskExplorerFactoryBean.java create mode 100644 spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/MapTaskExplorerFactoryBean.java create mode 100644 spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/SimpleTaskExplorer.java create mode 100644 spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskExplorerTests.java diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/DefaultTaskConfigurer.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/DefaultTaskConfigurer.java index d40b1c18..48648afa 100644 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/DefaultTaskConfigurer.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/DefaultTaskConfigurer.java @@ -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(); } } diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskExplorer.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskExplorer.java index acacfd76..db951cd8 100644 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskExplorer.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskExplorer.java @@ -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); /** diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/dao/JdbcTaskExecutionDao.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/dao/JdbcTaskExecutionDao.java index 53dd710c..cef6ae2a 100644 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/dao/JdbcTaskExecutionDao.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/dao/JdbcTaskExecutionDao.java @@ -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 findRunningTaskExecutions(String taskName) { + final Set result = new HashSet(); + 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 getTaskExecutionsByName(String taskName, final int start, + final int count) { + ResultSetExtractor> extractor = + new ResultSetExtractor>() { + + private List list = new ArrayList(); + + @Override + public List extractData(ResultSet rs) throws SQLException, + DataAccessException { + int rowNum = 0; + while (rowNum < start && rs.next()) { + rowNum++; + } + while (rowNum < start + count && rs.next()) { + RowMapper rowMapper = new TaskExecutionRowMapper(); + list.add(rowMapper.mapRow(rs, rowNum)); + rowNum++; + } + return list; + } + + }; + + List result = jdbcTemplate.query(getQuery(FIND_TASK_EXECUTIONS), + new Object[] { taskName }, extractor); + + return result; + } + + @Override + public List getTaskNames() { + return jdbcTemplate.query(getQuery(FIND_TASK_NAMES), + new RowMapper() { + @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 taskParameters) { + private void insertTaskParameters(String executionId, List 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 getTaskParameters(String executionId){ + final List 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 { + + 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; + } + } + } diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/dao/MapTaskExecutionDao.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/dao/MapTaskExecutionDao.java index 5bb7d547..24a65637 100644 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/dao/MapTaskExecutionDao.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/dao/MapTaskExecutionDao.java @@ -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 getTaskExecutions(){ + @Override + public TaskExecution getTaskExecution(String executionId) { + return taskExecutions.get(executionId); + } + + @Override + public long getTaskExecutionCount(String taskName) { + int count = 0; + for (Map.Entry entry : taskExecutions.entrySet()) { + if (entry.getValue().getTaskName().equals(taskName)) { + count++; + } + } + return count; + } + + @Override + public Set findRunningTaskExecutions(String taskName) { + Set result = new HashSet<>(); + for (Map.Entry entry : taskExecutions.entrySet()) { + if (entry.getValue().getTaskName().equals(taskName) && + entry.getValue().getEndTime() == null) { + result.add(entry.getValue()); + } + } + return Collections.unmodifiableSet(result); + } + + @Override + public List getTaskExecutionsByName(String taskName, int start, int count) { + List result = new ArrayList<>(); + Set filteredSet = new HashSet<>(); + for (Map.Entry entry : taskExecutions.entrySet()) { + if (entry.getValue().getTaskName().equals(taskName)) { + filteredSet.add(entry.getValue()); + } + } + int rowNum = 0; + Iterator 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 getTaskNames() { + Set result = new HashSet<>(); + for (Map.Entry entry : taskExecutions.entrySet()) { + result.add(entry.getValue().getTaskName()); + } + return Collections.unmodifiableList(new ArrayList(result)); + } + + public Map getTaskExecutions() { return Collections.unmodifiableMap(taskExecutions); } } diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/dao/TaskExecutionDao.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/dao/TaskExecutionDao.java index b22c38f0..17560c8c 100644 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/dao/TaskExecutionDao.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/dao/TaskExecutionDao.java @@ -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 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 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 getTaskNames(); } diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/JdbcTaskExplorerFactoryBean.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/JdbcTaskExplorerFactoryBean.java new file mode 100644 index 00000000..5f25eac1 --- /dev/null +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/JdbcTaskExplorerFactoryBean.java @@ -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; + } + +} diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/JdbcTaskRepositoryFactoryBean.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/JdbcTaskRepositoryFactoryBean.java index 284fe20d..1ccca7ac 100644 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/JdbcTaskRepositoryFactoryBean.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/JdbcTaskRepositoryFactoryBean.java @@ -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(){ diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/MapTaskExplorerFactoryBean.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/MapTaskExplorerFactoryBean.java new file mode 100644 index 00000000..fb749ad4 --- /dev/null +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/MapTaskExplorerFactoryBean.java @@ -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; + } + +} diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/NoOpTaskExplorer.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/NoOpTaskExplorer.java index 7a609e56..ae0a36be 100644 --- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/NoOpTaskExplorer.java +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/NoOpTaskExplorer.java @@ -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; } diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/SimpleTaskExplorer.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/SimpleTaskExplorer.java new file mode 100644 index 00000000..5f7b4529 --- /dev/null +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/SimpleTaskExplorer.java @@ -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 findRunningTaskExecutions(String taskName) { + return taskExecutionDao.findRunningTaskExecutions(taskName); + } + + @Override + public List getTaskNames() { + return taskExecutionDao.getTaskNames(); + } + + @Override + public long getTaskExecutionCount(String taskName) { + return taskExecutionDao.getTaskExecutionCount(taskName); + } + + @Override + public List getTaskExecutionsByName(String taskName, int start, int count) { + return taskExecutionDao.getTaskExecutionsByName(taskName, start, count); + } +} diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/NoOpTaskExplorerTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/NoOpTaskExplorerTests.java index 5d657586..77c26b4f 100644 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/NoOpTaskExplorerTests.java +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/NoOpTaskExplorerTests.java @@ -34,7 +34,7 @@ public class NoOpTaskExplorerTests { @Test public void testGetTaskExecution() throws Exception { - assertNull(taskExplorer.getTaskExecution(3l)); + assertNull(taskExplorer.getTaskExecution("abc")); } @Test diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskExplorerTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskExplorerTests.java new file mode 100644 index 00000000..0c47eb41 --- /dev/null +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskExplorerTests.java @@ -0,0 +1,247 @@ +/* + * 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 static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import javax.sql.DataSource; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration; +import org.springframework.cloud.task.annotation.EnableTask; +import org.springframework.cloud.task.repository.TaskExecution; +import org.springframework.cloud.task.repository.TaskExplorer; +import org.springframework.cloud.task.repository.dao.JdbcTaskExecutionDao; +import org.springframework.cloud.task.repository.dao.MapTaskExecutionDao; +import org.springframework.cloud.task.repository.dao.TaskExecutionDao; +import org.springframework.cloud.task.util.TestVerifierUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +/** + * @author Glenn Renfro + */ +@RunWith(Parameterized.class) +public class SimpleTaskExplorerTests { + + private AnnotationConfigApplicationContext context; + + private DataSource dataSource; + + private TaskExecutionDao dao; + + private TaskExplorer taskExplorer; + + private DaoType testType; + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(new Object[] { + DaoType.jdbc , DaoType.map }); + } + + public SimpleTaskExplorerTests(DaoType testType) { + this.testType = testType; + } + @Rule + public ExpectedException expected = ExpectedException.none(); + + @Before + public void testDefaultContext() throws Exception { + + if(testType == DaoType.jdbc){ + initializeJdbcExplorerTest(); + }else{ + initializeMapExplorerTest(); + } + taskExplorer = new SimpleTaskExplorer(dao); + } + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void getTaskExecution() { + final int TEST_COUNT = 5; + Map expectedResults = new HashMap<>(); + for (int i = 0; i < TEST_COUNT; i++) { + TaskExecution expectedTaskExecution = createAndSaveTaskExecution(); + expectedResults.put(expectedTaskExecution.getExecutionId(), + expectedTaskExecution); + } + for (String taskExecutionId : expectedResults.keySet()) { + TaskExecution actualTaskExecution = + taskExplorer.getTaskExecution(taskExecutionId); + assertNotNull(String.format( + "expected a taskExecution but got null for test type %s", testType), + actualTaskExecution); + TestVerifierUtils.verifyTaskExecution(expectedResults.get(taskExecutionId), + actualTaskExecution); + } + } + + @Test + public void getTaskCountByTaskName() { + final int TEST_COUNT = 5; + Map expectedResults = new HashMap<>(); + for (int i = 0; i < TEST_COUNT; i++) { + TaskExecution expectedTaskExecution = createAndSaveTaskExecution(); + expectedResults.put(expectedTaskExecution.getExecutionId(), + expectedTaskExecution); + } + for (Map.Entry entry : expectedResults.entrySet()) { + String taskName = entry.getValue().getTaskName(); + assertEquals(String.format( + "task count for task name did not match expected result for testType %s", + testType), + 1, taskExplorer.getTaskExecutionCount(taskName)); + } + } + + @Test + public void findRunningTasks() { + final int TEST_COUNT = 2; + final int COMPLETE_COUNT = 5; + final String TASK_NAME = "FOOBAR"; + + Map expectedResults = new HashMap<>(); + //Store completed jobs + for (int i = 0; i < COMPLETE_COUNT; i++) { + createAndSaveTaskExecution(); + } + + for (int i = 0; i < TEST_COUNT; i++) { + TaskExecution expectedTaskExecution = new TaskExecution(); + expectedTaskExecution.setStartTime(new Date()); + expectedTaskExecution.setExecutionId(UUID.randomUUID().toString()); + expectedTaskExecution.setTaskName(TASK_NAME); + dao.saveTaskExecution(expectedTaskExecution); + expectedResults.put(expectedTaskExecution.getExecutionId(), expectedTaskExecution); + } + Set actualResults = taskExplorer.findRunningTaskExecutions(TASK_NAME); + assertEquals(String.format( + "Running task count for task name did not match expected result for testType %s", + testType), TEST_COUNT, actualResults.size()); + + for (TaskExecution result : actualResults) { + assertTrue(String.format( + "result returned from repo %s not expected for testType %s", + result.getExecutionId(), testType), + expectedResults.containsKey(result.getExecutionId())); + assertNull(String.format("result had non null for endTime for the testType %s", + testType), result.getEndTime()); + } + } + + @Test + public void findTasksByName() { + final int TEST_COUNT = 5; + final int COMPLETE_COUNT = 7; + final int RESULT_SET_SIZE = 3; + final String TASK_NAME = "FOOBAR"; + + Map expectedResults = new HashMap<>(); + //Store completed jobs + for (int i = 0; i < COMPLETE_COUNT; i++) { + createAndSaveTaskExecution(); + } + + for (int i = 0; i < TEST_COUNT; i++) { + TaskExecution expectedTaskExecution = TestVerifierUtils.createSampleTaskExecutionNoParam(); + expectedTaskExecution.setTaskName(TASK_NAME); + dao.saveTaskExecution(expectedTaskExecution); + expectedResults.put(expectedTaskExecution.getExecutionId(), expectedTaskExecution); + } + List resultSet = taskExplorer.getTaskExecutionsByName(TASK_NAME, 1, RESULT_SET_SIZE); + assertEquals(String.format( + "Running task count for task name did not match expected result for testType %s", + testType), RESULT_SET_SIZE, resultSet.size()); + + for (TaskExecution result : resultSet) { + assertTrue(String.format("result returned from %s repo %s not expected", + testType, result.getExecutionId()), + expectedResults.containsKey(result.getExecutionId())); + assertEquals( + String.format("taskName for taskExecution is incorrect for testType %s", + testType), TASK_NAME, result.getTaskName()); + } + } + + @Test + public void getTaskNames() { + final int TEST_COUNT = 5; + Set expectedResults = new HashSet<>(); + for (int i = 0; i < TEST_COUNT; i++) { + TaskExecution expectedTaskExecution = createAndSaveTaskExecution(); + expectedResults.add(expectedTaskExecution.getTaskName()); + } + List actualTaskNames = taskExplorer.getTaskNames(); + for (String taskName : actualTaskNames) { + assertTrue(String.format("taskName was not in expected results for testType %s", + testType), expectedResults.contains(taskName)); + } + } + + private TaskExecution createAndSaveTaskExecution() { + TaskExecution taskExecution = TestVerifierUtils.createSampleTaskExecution(); + dao.saveTaskExecution(taskExecution); + return taskExecution; + } + + @EnableTask + protected static class TestConfiguration { + } + + private void initializeJdbcExplorerTest(){ + this.context = new AnnotationConfigApplicationContext(); + this.context.register(TestConfiguration.class, + EmbeddedDataSourceConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + dataSource = this.context.getBean(DataSource.class); + dao = new JdbcTaskExecutionDao(dataSource); + } + + private void initializeMapExplorerTest(){ + dao = new MapTaskExecutionDao(); + } + + private enum DaoType{jdbc, map} +} diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/TaskDatabaseInitializerTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/TaskDatabaseInitializerTests.java index 4636f9d3..e466e7a8 100644 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/TaskDatabaseInitializerTests.java +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/TaskDatabaseInitializerTests.java @@ -67,10 +67,9 @@ public class TaskDatabaseInitializerTests { .queryForList("select * from TASK_EXECUTION").size()); } -// @Test + @Test public void testNoDatabase() throws Exception { SimpleTaskRepository repository = new SimpleTaskRepository(new MapTaskExecutionDao()); - this.context.refresh(); assertThat(repository.getTaskExecutionDao(), instanceOf(MapTaskExecutionDao.class)); MapTaskExecutionDao dao = (MapTaskExecutionDao) repository.getTaskExecutionDao(); assertEquals(0, dao.getTaskExecutions().size()); diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestVerifierUtils.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestVerifierUtils.java index 13286525..ae4ef7a7 100644 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestVerifierUtils.java +++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestVerifierUtils.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; +import java.util.List; import java.util.Random; import java.util.Set; import java.util.UUID; @@ -45,6 +46,8 @@ import org.springframework.cloud.task.repository.TaskExecution; */ public class TestVerifierUtils { + public static final int PARAM_SIZE = 5; + /** * Creates a mock {@link Appender} to be added to the root logger. * @@ -94,6 +97,29 @@ public class TestVerifierUtils { exitMessage, new ArrayList()); } + /** + * Creates a fully populated TaskExecution for testing. + * + * @return + */ + public static TaskExecution createSampleTaskExecution() { + Random randomGenerator = new Random(); + int exitCode = randomGenerator.nextInt(); + Date startTime = new Date(); + Date endTime = new Date(); + String executionId = UUID.randomUUID().toString(); + String taskName = UUID.randomUUID().toString(); + String exitMessage = UUID.randomUUID().toString(); + String statusCode = UUID.randomUUID().toString().substring(0, 9); + List params = new ArrayList<>(PARAM_SIZE); + for (int i = 0 ; i < PARAM_SIZE ; i++){ + params.add(UUID.randomUUID().toString()); + } + return new TaskExecution(executionId, exitCode, taskName, + startTime, endTime, statusCode, + exitMessage, params); + } + /** * Verifies that all the fields in between the expected and actual are the same; *