diff --git a/spring-cloud-task-core/pom.xml b/spring-cloud-task-core/pom.xml
index d9efd933..e51ec135 100755
--- a/spring-cloud-task-core/pom.xml
+++ b/spring-cloud-task-core/pom.xml
@@ -67,6 +67,14 @@
org.springframework.batchspring-batch-infrastructure
+
+ org.springframework.batch
+ spring-batch-core
+
+
+ org.springframework.data
+ spring-data-commons
+
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 db951cd8..7cb4ac5d 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
@@ -19,6 +19,9 @@ package org.springframework.cloud.task.repository;
import java.util.List;
import java.util.Set;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+
/**
* Offers methods that allow users to query the task executions that are available.
*
@@ -56,7 +59,14 @@ public interface TaskExplorer {
* @param taskName the name of the task to be searched
* @return the number of running tasks that have the taskname specified
*/
- public long getTaskExecutionCount(String taskName);
+ public long getTaskExecutionCountByTaskName(String taskName);
+
+ /**
+ * Retrieves current number of task executions.
+ *
+ * @return current number of task executions.
+ */
+ long getTaskExecutionCount();
/**
* Get a collection/page of executions
@@ -68,5 +78,13 @@ public interface TaskExplorer {
*/
public List getTaskExecutionsByName(String taskName, int start, int count);
+ /**
+ * Retrieves all the task executions within the pageable constraints sorted by
+ * start date descending, taskExecution id descending.
+ *
+ * @param pageable the constraints for the search
+ * @return page containing the results from the search
+ */
+ public Page findAll(Pageable pageable);
}
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 4c494dae..4a2be0c4 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
@@ -23,12 +23,20 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.TreeMap;
import javax.sql.DataSource;
+import org.springframework.batch.item.database.Order;
import org.springframework.cloud.task.repository.TaskExecution;
+import org.springframework.cloud.task.repository.database.PagingQueryProvider;
+import org.springframework.cloud.task.repository.database.support.SqlPagingQueryProviderFactoryBean;
import org.springframework.dao.EmptyResultDataAccessException;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
@@ -44,6 +52,12 @@ import org.springframework.util.StringUtils;
public class JdbcTaskExecutionDao implements TaskExecutionDao {
+ public static String SELECT_CLAUSE = "TASK_EXECUTION_ID, "
+ + "START_TIME, END_TIME, TASK_NAME, EXIT_CODE, "
+ + "EXIT_MESSAGE, LAST_UPDATED, STATUS_CODE ";
+
+ public static String FROM_CLAUSE = "%PREFIX%EXECUTION";
+
private static final String SAVE_TASK_EXECUTION = "INSERT into %PREFIX%EXECUTION"
+ "(TASK_EXECUTION_ID, START_TIME, END_TIME, TASK_NAME, EXIT_CODE, "
+ "EXIT_MESSAGE, LAST_UPDATED, STATUS_CODE) values (?, ?, ?, ?, ?, ?, ?, ?)";
@@ -68,6 +82,9 @@ public class JdbcTaskExecutionDao implements TaskExecutionDao {
+ "TASK_PARAM from %PREFIX%EXECUTION_PARAMS where TASK_EXECUTION_ID = ?";
private static final String TASK_EXECUTION_COUNT = "SELECT COUNT(*) FROM " +
+ "%PREFIX%EXECUTION ";
+
+ private static final String TASK_EXECUTION_COUNT_BY_NAME = "SELECT COUNT(*) FROM " +
"%PREFIX%EXECUTION where TASK_NAME = ?";
private static final String FIND_RUNNING_TASK_EXECUTIONS = "SELECT TASK_EXECUTION_ID, "
@@ -76,7 +93,7 @@ public class JdbcTaskExecutionDao implements TaskExecutionDao {
+ "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, "
+ private static final String FIND_TASK_EXECUTIONS_BY_NAME = "SELECT TASK_EXECUTION_ID, "
+ "START_TIME, END_TIME, TASK_NAME, EXIT_CODE, "
+ "EXIT_MESSAGE, LAST_UPDATED, STATUS_CODE "
+ "from %PREFIX%EXECUTION where TASK_NAME = ? "
@@ -91,9 +108,18 @@ public class JdbcTaskExecutionDao implements TaskExecutionDao {
private JdbcOperations jdbcTemplate;
+ private DataSource dataSource;
+
+ Map orderMap;
+
public JdbcTaskExecutionDao(DataSource dataSource) {
Assert.notNull(dataSource);
this.jdbcTemplate = new JdbcTemplate(dataSource);
+ this.dataSource = dataSource;
+ orderMap = new TreeMap<>();
+ orderMap.put("START_TIME", Order.DESCENDING);
+ orderMap.put("TASK_EXECUTION_ID", Order.DESCENDING);
+
}
@Override
@@ -156,15 +182,25 @@ public class JdbcTaskExecutionDao implements TaskExecutionDao {
}
@Override
- public long getTaskExecutionCount(String taskName) {
+ public long getTaskExecutionCountByTaskName(String taskName) {
try {
return jdbcTemplate.queryForObject(
- getQuery(TASK_EXECUTION_COUNT), new Object[] { taskName }, Long.class);
+ getQuery(TASK_EXECUTION_COUNT_BY_NAME), new Object[] { taskName }, Long.class);
}
catch (EmptyResultDataAccessException e) {
return 0;
}
+ }
+ @Override
+ public long getTaskExecutionCount() {
+ try {
+ return jdbcTemplate.queryForObject(
+ getQuery(TASK_EXECUTION_COUNT), new Object[] { }, Long.class);
+ }
+ catch (EmptyResultDataAccessException e) {
+ return 0;
+ }
}
@Override
@@ -179,8 +215,8 @@ public class JdbcTaskExecutionDao implements TaskExecutionDao {
@Override
public List getTaskExecutionsByName(String taskName, final int start, final int count) {
- return jdbcTemplate.query(getQuery(FIND_TASK_EXECUTIONS),
- new Object[]{ taskName, count, start + 1 }, new TaskExecutionRowMapper());
+ return jdbcTemplate.query(getQuery(FIND_TASK_EXECUTIONS_BY_NAME),
+ new Object[]{ taskName, count, start }, new TaskExecutionRowMapper());
}
@Override
@@ -188,6 +224,30 @@ public class JdbcTaskExecutionDao implements TaskExecutionDao {
return jdbcTemplate.queryForList(getQuery(FIND_TASK_NAMES), String.class);
}
+ @Override
+ public Page findAll(Pageable pageable) {
+
+ SqlPagingQueryProviderFactoryBean factoryBean = new SqlPagingQueryProviderFactoryBean();
+ factoryBean.setSelectClause(SELECT_CLAUSE);
+ factoryBean.setFromClause(FROM_CLAUSE);
+ factoryBean.setSortKeys(orderMap);
+ factoryBean.setDataSource(dataSource);
+ PagingQueryProvider pagingQueryProvider = null;
+ try {
+ pagingQueryProvider = factoryBean.getObject();
+ pagingQueryProvider.init(dataSource);
+ }
+ catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ String query = pagingQueryProvider.getPageQuery(pageable);
+ List resultList = jdbcTemplate.query(
+ getQuery(query),
+ new Object[]{ },
+ new TaskExecutionRowMapper());
+ return new PageImpl(resultList, pageable, getTaskExecutionCount());
+ }
+
private String getQuery(String base) {
return StringUtils.replace(base, "%PREFIX%", tablePrefix);
}
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 cacc171d..7838f914 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
@@ -27,6 +27,9 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.springframework.cloud.task.repository.TaskExecution;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.Pageable;
/**
* Stores Task Execution Information to a in-memory map.
@@ -57,7 +60,7 @@ public class MapTaskExecutionDao implements TaskExecutionDao {
}
@Override
- public long getTaskExecutionCount(String taskName) {
+ public long getTaskExecutionCountByTaskName(String taskName) {
int count = 0;
for (Map.Entry entry : taskExecutions.entrySet()) {
if (entry.getValue().getTaskName().equals(taskName)) {
@@ -67,6 +70,11 @@ public class MapTaskExecutionDao implements TaskExecutionDao {
return count;
}
+ @Override
+ public long getTaskExecutionCount() {
+ return taskExecutions.size();
+ }
+
@Override
public Set findRunningTaskExecutions(String taskName) {
Set result = getTaskExecutionTreeSet();
@@ -101,6 +109,19 @@ public class MapTaskExecutionDao implements TaskExecutionDao {
return new ArrayList(result);
}
+ @Override
+ public Page findAll(Pageable pageable) {
+ TreeSet sortedSet = getTaskExecutionTreeSet();
+ sortedSet.addAll(taskExecutions.values());
+ List result = new ArrayList<>(sortedSet.descendingSet());
+ int toIndex = (pageable.getOffset() + pageable.getPageSize() > result.size()) ?
+ result.size() : pageable.getOffset() + pageable.getPageSize();
+ return new PageImpl(
+ result.subList(pageable.getOffset(), toIndex),
+ pageable,
+ getTaskExecutionCount());
+ }
+
public Map getTaskExecutions() {
return Collections.unmodifiableMap(taskExecutions);
}
@@ -109,7 +130,11 @@ public class MapTaskExecutionDao implements TaskExecutionDao {
return new TreeSet(new Comparator() {
@Override
public int compare(TaskExecution e1, TaskExecution e2) {
- return e1.getExecutionId().compareTo(e2.getExecutionId());
+ int result = e1.getStartTime().compareTo(e2.getStartTime());
+ if (result == 0){
+ result = e1.getExecutionId().compareTo(e2.getExecutionId());
+ }
+ return result;
}
});
}
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 17560c8c..6a272ba7 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
@@ -20,6 +20,8 @@ import java.util.List;
import java.util.Set;
import org.springframework.cloud.task.repository.TaskExecution;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
/**
* Data Access Object for task executions.
@@ -54,9 +56,16 @@ public interface TaskExecutionDao {
* 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 for the taskName.
+ */
+ long getTaskExecutionCountByTaskName(String taskName);
+
+ /**
+ * Retrieves current number of task executions.
+ *
* @return current number of task executions.
*/
- long getTaskExecutionCount(String taskName);
+ long getTaskExecutionCount();
/**
* Retrieves a set of task executions that are running for a taskName.
@@ -83,4 +92,12 @@ public interface TaskExecutionDao {
* @return a list of distinct task names from the task repository..
*/
public List getTaskNames();
+
+ /**
+ * Retrieves all the task executions within the pageable constraints.
+ * @param pageable the constraints for the search
+ * @return page containing the results from the search
+ */
+
+ public Page findAll(Pageable pageable);
}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/PagingQueryProvider.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/PagingQueryProvider.java
new file mode 100644
index 00000000..b4eef127
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/PagingQueryProvider.java
@@ -0,0 +1,68 @@
+/*
+ * 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.database;
+
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import org.springframework.batch.item.database.Order;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * Interface defining the functionality to be provided for generating paging queries.
+ * @author Glenn Renfro
+ */
+public interface PagingQueryProvider {
+
+ /**
+ * Initialize the query provider using the provided {@link DataSource} if necessary.
+ *
+ * @param dataSource DataSource to use for any initialization
+ */
+ void init(DataSource dataSource) throws Exception;
+
+ /**
+ * The number of parameters that are declared in the query
+ * @return number of parameters
+ */
+ int getParameterCount();
+
+ /**
+ * Indicate whether the generated queries use named parameter syntax.
+ *
+ * @return true if named parameter syntax is used
+ */
+ boolean isUsingNamedParameters();
+
+ /**
+ * The sort keys. A Map of the columns that make up the key and a Boolean indicating ascending or descending
+ * (ascending = true).
+ *
+ * @return the sort keys used to order the query
+ */
+ Map getSortKeys();
+
+ /**
+ *
+ * Generate the query that will provide the jump to item query.
+ *
+ * @param pageable the coordinates to pull the next page from the datasource
+ * @return the generated query
+ */
+ String getPageQuery(Pageable pageable);
+}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/AbstractSqlPagingQueryProvider.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/AbstractSqlPagingQueryProvider.java
new file mode 100644
index 00000000..8c6c18d3
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/AbstractSqlPagingQueryProvider.java
@@ -0,0 +1,169 @@
+/*
+ * 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.database.support;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import org.springframework.batch.item.database.JdbcParameterUtils;
+import org.springframework.batch.item.database.Order;
+import org.springframework.cloud.task.repository.database.PagingQueryProvider;
+import org.springframework.dao.InvalidDataAccessApiUsageException;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Abstract SQL Paging Query Provider to serve as a base class for all provided
+ * SQL paging query providers.
+ *
+ * Any implementation must provide a way to specify the select clause, from
+ * clause and optionally a where clause. It is recommended that there should be an index for
+ * the sort key to provide better performance.
+ *
+ * Provides properties and preparation for the mandatory "selectClause" and
+ * "fromClause" as well as for the optional "whereClause".
+ *
+ * @author Glenn Renfro
+ */
+public abstract class AbstractSqlPagingQueryProvider implements PagingQueryProvider {
+
+ private String selectClause;
+
+ private String fromClause;
+
+ private String whereClause;
+
+ private Map sortKeys = new LinkedHashMap();
+
+ private int parameterCount;
+
+ private boolean usingNamedParameters;
+
+ /**
+ * @param selectClause SELECT clause part of SQL query string
+ */
+ public void setSelectClause(String selectClause) {
+ this.selectClause = removeKeyWord("select", selectClause);
+ }
+
+ /**
+ *
+ * @return SQL SELECT clause part of SQL query string
+ */
+ protected String getSelectClause() {
+ return selectClause;
+ }
+
+ /**
+ * @param fromClause FROM clause part of SQL query string
+ */
+ public void setFromClause(String fromClause) {
+ this.fromClause = removeKeyWord("from", fromClause);
+ }
+
+ /**
+ *
+ * @return SQL FROM clause part of SQL query string
+ */
+ protected String getFromClause() {
+ return fromClause;
+ }
+
+ /**
+ * @param whereClause WHERE clause part of SQL query string
+ */
+ public void setWhereClause(String whereClause) {
+ if (StringUtils.hasText(whereClause)) {
+ this.whereClause = removeKeyWord("where", whereClause);
+ }
+ else {
+ this.whereClause = null;
+ }
+ }
+
+ /**
+ *
+ * @return SQL WHERE clause part of SQL query string
+ */
+ protected String getWhereClause() {
+ return whereClause;
+ }
+
+ /**
+ * @param sortKeys key to use to sort and limit page content
+ */
+ public void setSortKeys(Map sortKeys) {
+ this.sortKeys = sortKeys;
+ }
+
+ /**
+ * A Map<String, Order> of sort columns as the key and {@link Order} for ascending/descending.
+ *
+ * @return sortKey key to use to sort and limit page content
+ */
+ @Override
+ public Map getSortKeys() {
+ return sortKeys;
+ }
+
+ @Override
+ public int getParameterCount() {
+ return parameterCount;
+ }
+
+ @Override
+ public boolean isUsingNamedParameters() {
+ return usingNamedParameters;
+ }
+
+ @Override
+ public void init(DataSource dataSource) throws Exception {
+ Assert.notNull(dataSource);
+ Assert.hasLength(selectClause, "selectClause must be specified");
+ Assert.hasLength(fromClause, "fromClause must be specified");
+ Assert.notEmpty(sortKeys, "sortKey must be specified");
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT ").append(selectClause);
+ sql.append(" FROM ").append(fromClause);
+ if (whereClause != null) {
+ sql.append(" WHERE ").append(whereClause);
+ }
+ List namedParameters = new ArrayList();
+ parameterCount = JdbcParameterUtils.countParameterPlaceholders(sql.toString(), namedParameters);
+ if (namedParameters.size() > 0) {
+ if (parameterCount != namedParameters.size()) {
+ throw new InvalidDataAccessApiUsageException(
+ "You can't use both named parameters and classic \"?\" placeholders: " + sql);
+ }
+ usingNamedParameters = true;
+ }
+ }
+ private String removeKeyWord(String keyWord, String clause) {
+ String temp = clause.trim();
+ String keyWordString = keyWord + " ";
+ if (temp.toLowerCase().startsWith(keyWordString) && temp.length() > keyWordString.length()) {
+ return temp.substring(keyWordString.length());
+ }
+ else {
+ return temp;
+ }
+ }
+}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/HsqlPagingQueryProvider.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/HsqlPagingQueryProvider.java
new file mode 100644
index 00000000..92ee5ee4
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/HsqlPagingQueryProvider.java
@@ -0,0 +1,37 @@
+/*
+ * 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.database.support;
+
+import org.springframework.cloud.task.repository.database.PagingQueryProvider;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * HSQLDB implementation of a {@link PagingQueryProvider} using database specific features.
+ *
+ * @author Glenn Renfro
+ */
+public class HsqlPagingQueryProvider extends AbstractSqlPagingQueryProvider {
+
+ @Override
+ public String getPageQuery(Pageable pageable) {
+ String topClause = new StringBuilder().append("LIMIT ")
+ .append(pageable.getOffset()).append(" ")
+ .append(pageable.getPageSize()).toString();
+ return SqlPagingQueryUtils.generateTopJumpToQuery(this, topClause);
+ }
+
+}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/MySqlPagingQueryProvider.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/MySqlPagingQueryProvider.java
new file mode 100644
index 00000000..2852ae2f
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/MySqlPagingQueryProvider.java
@@ -0,0 +1,36 @@
+/*
+ * 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.database.support;
+
+import org.springframework.cloud.task.repository.database.PagingQueryProvider;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * MySQL implementation of a {@link PagingQueryProvider} using database specific features.
+ *
+ * @author Glenn Renfro
+ */
+public class MySqlPagingQueryProvider extends AbstractSqlPagingQueryProvider {
+ @Override
+ public String getPageQuery(Pageable pageable) {
+ String topClause = new StringBuilder().append("LIMIT ")
+ .append(pageable.getOffset()).append(", ")
+ .append(pageable.getPageSize()).toString();
+ return SqlPagingQueryUtils.generateLimitJumpToQuery(this, topClause);
+ }
+
+}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/OraclePagingQueryProvider.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/OraclePagingQueryProvider.java
new file mode 100644
index 00000000..a639f3cf
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/OraclePagingQueryProvider.java
@@ -0,0 +1,35 @@
+/*
+ * 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.database.support;
+
+import org.springframework.cloud.task.repository.database.PagingQueryProvider;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * Oracle implementation of a {@link PagingQueryProvider} using database specific features.
+ *
+ * @author Glenn Renfro
+ */
+public class OraclePagingQueryProvider extends AbstractSqlPagingQueryProvider {
+
+ @Override
+ public String getPageQuery(Pageable pageable) {
+ int offset = pageable.getOffset()+1;
+ return SqlPagingQueryUtils.generateRowNumSqlQueryWithNesting(this, getSelectClause(), getSelectClause(), false, "TMP_ROW_NUM >= "
+ + offset + " AND TMP_ROW_NUM < " + (offset+pageable.getPageSize()));
+ }
+}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/PostgresPagingQueryProvider.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/PostgresPagingQueryProvider.java
new file mode 100644
index 00000000..18a16c8b
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/PostgresPagingQueryProvider.java
@@ -0,0 +1,36 @@
+/*
+ * 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.database.support;
+
+import org.springframework.cloud.task.repository.database.PagingQueryProvider;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * Postgres implementation of a {@link PagingQueryProvider} using database specific features.
+ *
+ * @author Glenn Renfro
+ */
+public class PostgresPagingQueryProvider extends AbstractSqlPagingQueryProvider {
+
+ @Override
+ public String getPageQuery(Pageable pageable) {
+ String limitClause = new StringBuilder().append("LIMIT ").
+ append(pageable.getPageSize()).append(" OFFSET ").
+ append(pageable.getOffset()).toString();
+ return SqlPagingQueryUtils.generateLimitJumpToQuery(this, limitClause);
+ }
+}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/SqlPagingQueryProviderFactoryBean.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/SqlPagingQueryProviderFactoryBean.java
new file mode 100644
index 00000000..c18dd89b
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/SqlPagingQueryProviderFactoryBean.java
@@ -0,0 +1,168 @@
+/*
+ * 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.database.support;
+
+
+import static org.springframework.cloud.task.repository.support.DatabaseType.HSQL;
+import static org.springframework.cloud.task.repository.support.DatabaseType.MYSQL;
+import static org.springframework.cloud.task.repository.support.DatabaseType.ORACLE;
+import static org.springframework.cloud.task.repository.support.DatabaseType.POSTGRES;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.sql.DataSource;
+
+import org.springframework.batch.item.database.Order;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.cloud.task.repository.database.PagingQueryProvider;
+import org.springframework.cloud.task.repository.support.DatabaseType;
+import org.springframework.jdbc.support.MetaDataAccessException;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Factory bean for {@link PagingQueryProvider} interface. The database type
+ * will be determined from the data source if not provided explicitly. Valid
+ * types are given by the {@link DatabaseType} enum.
+ *
+ * @author Glenn Renfro
+ */
+public class SqlPagingQueryProviderFactoryBean implements FactoryBean {
+
+ private DataSource dataSource;
+
+ private String databaseType;
+
+ private String fromClause;
+
+ private String whereClause;
+
+ private String selectClause;
+
+ private Map sortKeys;
+
+ private Map providers = new HashMap();
+
+
+ {
+ providers.put(HSQL, new HsqlPagingQueryProvider());
+ providers.put(MYSQL, new MySqlPagingQueryProvider());
+ providers.put(POSTGRES, new PostgresPagingQueryProvider());
+ providers.put(ORACLE, new OraclePagingQueryProvider());
+ }
+
+ /**
+ * @param databaseType the databaseType to set
+ */
+ public void setDatabaseType(String databaseType) {
+ Assert.hasText(databaseType, "databaseType must not be empty nor null");
+ this.databaseType = databaseType;
+ }
+
+ /**
+ * @param dataSource the dataSource to set
+ */
+ public void setDataSource(DataSource dataSource) {
+ Assert.notNull(dataSource, "dataSource must not be null");
+ this.dataSource = dataSource;
+ }
+
+ /**
+ * @param fromClause the fromClause to set
+ */
+ public void setFromClause(String fromClause) {
+ Assert.hasText(fromClause, "fromClause must not be empty nor null");
+ this.fromClause = fromClause;
+ }
+
+ /**
+ * @param whereClause the whereClause to set
+ */
+ public void setWhereClause(String whereClause) {
+ this.whereClause = whereClause;
+ }
+
+ /**
+ * @param selectClause the selectClause to set
+ */
+ public void setSelectClause(String selectClause) {
+ Assert.hasText(selectClause, "selectClause must not be empty nor null");
+ this.selectClause = selectClause;
+ }
+
+ /**
+ * @param sortKeys the sortKeys to set
+ */
+ public void setSortKeys(Map sortKeys) {
+ this.sortKeys = sortKeys;
+ }
+
+ /**
+ * Get a {@link PagingQueryProvider} instance using the provided properties
+ * and appropriate for the given database type.
+ *
+ * @see FactoryBean#getObject()
+ */
+ @Override
+ public PagingQueryProvider getObject() throws Exception {
+
+ DatabaseType type;
+ try {
+ type = databaseType != null ? DatabaseType.valueOf(databaseType.toUpperCase()) : DatabaseType
+ .fromMetaData(dataSource);
+ }
+ catch (MetaDataAccessException e) {
+ throw new IllegalArgumentException(
+ "Could not inspect meta data for database type. You have to supply it explicitly.", e);
+ }
+
+ AbstractSqlPagingQueryProvider provider = providers.get(type);
+ Assert.state(provider != null, "Should not happen: missing PagingQueryProvider for DatabaseType=" + type);
+
+ provider.setFromClause(fromClause);
+ provider.setWhereClause(whereClause);
+ provider.setSortKeys(sortKeys);
+ if (StringUtils.hasText(selectClause)) {
+ provider.setSelectClause(selectClause);
+ }
+ provider.init(dataSource);
+
+ return provider;
+
+ }
+
+ /**
+ * Always returns {@link PagingQueryProvider}.
+ *
+ * @see FactoryBean#getObjectType()
+ */
+ @Override
+ public Class getObjectType() {
+ return PagingQueryProvider.class;
+ }
+
+ /**
+ * Always returns true.
+ *
+ * @see FactoryBean#isSingleton()
+ */
+ @Override
+ public boolean isSingleton() {
+ return true;
+ }
+}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/SqlPagingQueryUtils.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/SqlPagingQueryUtils.java
new file mode 100644
index 00000000..7f8acae2
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/database/support/SqlPagingQueryUtils.java
@@ -0,0 +1,143 @@
+/*
+ * 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.database.support;
+
+import java.util.Map;
+
+import org.springframework.batch.item.database.Order;
+
+/**
+ * Utility class that generates the actual SQL statements used by query
+ * providers.
+ *
+ * @author Glenn Renfro
+ */
+public class SqlPagingQueryUtils {
+
+ /**
+ * Generate SQL query string using a LIMIT clause
+ *
+ * @param provider {@link AbstractSqlPagingQueryProvider} providing the
+ * implementation specifics
+ * @param limitClause the implementation specific top clause to be used
+ * @return the generated query
+ */
+ public static String generateLimitJumpToQuery(AbstractSqlPagingQueryProvider provider, String limitClause) {
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT ").append(provider.getSelectClause());
+ sql.append(" FROM ").append(provider.getFromClause());
+ sql.append(provider.getWhereClause() == null ? "" : " WHERE " + provider.getWhereClause());
+ sql.append(" ORDER BY ").append(buildSortClause(provider));
+ sql.append(" " + limitClause);
+
+ return sql.toString();
+ }
+
+ /**
+ * Generate SQL query string using a TOP clause
+ *
+ * @param provider {@link AbstractSqlPagingQueryProvider} providing the
+ * implementation specifics
+ * @param topClause the implementation specific top clause to be used
+ * @return the generated query
+ */
+ public static String generateTopJumpToQuery(AbstractSqlPagingQueryProvider provider, String topClause) {
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT ").append(topClause).append(" ").append(provider.getSelectClause());
+ sql.append(" FROM ").append(provider.getFromClause());
+ sql.append(provider.getWhereClause() == null ? "" : " WHERE " + provider.getWhereClause());
+ sql.append(" ORDER BY ").append(buildSortClause(provider));
+
+ return sql.toString();
+ }
+
+ public static String generateRowNumSqlQueryWithNesting(AbstractSqlPagingQueryProvider provider,
+ String selectClause, boolean remainingPageQuery, String rowNumClause) {
+ return generateRowNumSqlQueryWithNesting(provider, selectClause, selectClause, remainingPageQuery, rowNumClause);
+ }
+
+ public static String generateRowNumSqlQueryWithNesting(AbstractSqlPagingQueryProvider provider,
+ String innerSelectClause, String outerSelectClause, boolean remainingPageQuery, String rowNumClause) {
+
+ StringBuilder sql = new StringBuilder();
+ sql.append("SELECT ").append(outerSelectClause).append(" FROM (SELECT ").append(outerSelectClause)
+ .append(", ").append("ROWNUM as TMP_ROW_NUM");
+ sql.append(" FROM (SELECT ").append(innerSelectClause).append(" FROM ").append(provider.getFromClause());
+ buildWhereClause(provider, remainingPageQuery, sql);
+ sql.append(" ORDER BY ").append(buildSortClause(provider));
+ sql.append(")) WHERE ").append(rowNumClause);
+
+ return sql.toString();
+
+ }
+
+ private static void buildWhereClause(AbstractSqlPagingQueryProvider provider, boolean remainingPageQuery,
+ StringBuilder sql) {
+ if (remainingPageQuery) {
+ sql.append(" WHERE ");
+ if (provider.getWhereClause() != null) {
+ sql.append("(");
+ sql.append(provider.getWhereClause());
+ sql.append(") AND ");
+ }
+
+ }
+ else {
+ sql.append(provider.getWhereClause() == null ? "" : " WHERE " + provider.getWhereClause());
+ }
+ }
+
+ /**
+ * Generates ORDER BY attributes based on the sort keys.
+ *
+ * @param provider {@link AbstractSqlPagingQueryProvider} providing the
+ * implementation specifics
+ * @return a String that can be appended to an ORDER BY clause.
+ */
+ public static String buildSortClause(AbstractSqlPagingQueryProvider provider) {
+ return buildSortClause(provider.getSortKeys());
+ }
+
+ /**
+ * Generates ORDER BY attributes based on the sort keys.
+ *
+ * @param sortKeys generates order by clause from map
+ * @return a String that can be appended to an ORDER BY clause.
+ */
+ public static String buildSortClause(Map sortKeys) {
+ StringBuilder builder = new StringBuilder();
+ String prefix = "";
+
+ for (Map.Entry sortKey : sortKeys.entrySet()) {
+ builder.append(prefix);
+
+ prefix = ", ";
+
+ builder.append(sortKey.getKey());
+
+ if(sortKey.getValue() != null && sortKey.getValue() == Order.DESCENDING) {
+ builder.append(" DESC");
+ }
+ else {
+ builder.append(" ASC");
+ }
+ }
+
+ return builder.toString();
+ }
+
+}
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
deleted file mode 100644
index ae0a36be..00000000
--- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/NoOpTaskExplorer.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.springframework.cloud.task.repository.TaskExecution;
-import org.springframework.cloud.task.repository.TaskExplorer;
-
-/**
- * Provides a no-op TaskExplorer for development purposes.
- *
- * @author Michael Minella
- */
-public class NoOpTaskExplorer implements TaskExplorer {
-
- public TaskExecution getTaskExecution(String executionId) {
- return null;
- }
-
- public Set findRunningTaskExecutions(String taskName) {
- return new HashSet(0);
- }
-
- public List getTaskNames() {
- return new ArrayList(0);
- }
-
- public long getTaskExecutionCount(String taskName) {
- return 0;
- }
-
- public List getTaskExecutionsByName(String taskName, int start, int count) {
- return new ArrayList(0);
- }
-}
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
index 5f7b4529..ec788422 100644
--- 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
@@ -22,6 +22,8 @@ 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.data.domain.Page;
+import org.springframework.data.domain.Pageable;
import org.springframework.util.Assert;
/**
@@ -54,12 +56,23 @@ public class SimpleTaskExplorer implements TaskExplorer{
}
@Override
- public long getTaskExecutionCount(String taskName) {
- return taskExecutionDao.getTaskExecutionCount(taskName);
+ public long getTaskExecutionCountByTaskName(String taskName) {
+ return taskExecutionDao.getTaskExecutionCountByTaskName(taskName);
+ }
+
+ @Override
+ public long getTaskExecutionCount() {
+ return taskExecutionDao.getTaskExecutionCount();
}
@Override
public List getTaskExecutionsByName(String taskName, int start, int count) {
return taskExecutionDao.getTaskExecutionsByName(taskName, start, count);
}
+
+ @Override
+ public Page findAll(Pageable pageable) {
+ return taskExecutionDao.findAll(pageable);
+ }
+
}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/SimpleTaskRepository.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/SimpleTaskRepository.java
index 3d626726..20cf1747 100644
--- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/SimpleTaskRepository.java
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/support/SimpleTaskRepository.java
@@ -29,6 +29,11 @@ import org.springframework.util.Assert;
*/
public class SimpleTaskRepository implements TaskRepository {
+ public static final int MAX_EXIT_MESSAGE_SIZE = 2500;
+ public static final int MAX_TASK_NAME_SIZE = 100;
+ public static final int MAX_STATUS_CODE_SIZE = 10;
+ public static final int MAX_EXECUTION_ID_SIZE = 100;
+
private final static Logger logger = LoggerFactory.getLogger(SimpleTaskRepository.class);
private TaskExecutionDao taskExecutionDao;
@@ -67,7 +72,27 @@ public class SimpleTaskRepository implements TaskRepository {
*/
private void validateTaskExecution(TaskExecution taskExecution) {
Assert.notNull(taskExecution, "taskExecution should not be null");
- Assert.notNull(taskExecution.getExecutionId(), "taskExecutionId should not be null");
+ Assert.hasText(taskExecution.getExecutionId(), "taskExecutionId should not be null");
Assert.notNull(taskExecution.getStartTime(), "TaskExecution start time cannot be null.");
+
+ if (taskExecution.getTaskName() != null &&
+ taskExecution.getTaskName().length() > MAX_TASK_NAME_SIZE) {
+ throw new IllegalArgumentException("TaskName length exceeds "
+ + MAX_TASK_NAME_SIZE + " characters");
+ }
+ if (taskExecution.getStatusCode() != null &&
+ taskExecution.getStatusCode().length() > MAX_STATUS_CODE_SIZE) {
+ throw new IllegalArgumentException("StatusCode length exceeds "
+ + MAX_STATUS_CODE_SIZE + " characters");
+ }
+ if (taskExecution.getExecutionId().length() > MAX_EXECUTION_ID_SIZE) {
+ throw new IllegalArgumentException("ExecutionID length exceeds "
+ + MAX_EXECUTION_ID_SIZE + " characters");
+ }
+ //Trim the exit message
+ if(taskExecution.getExitMessage() != null &&
+ taskExecution.getExitMessage().length() > MAX_EXIT_MESSAGE_SIZE){
+ taskExecution.setExitMessage(taskExecution.getExitMessage().substring(0, MAX_EXIT_MESSAGE_SIZE - 1));
+ }
}
}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/database/support/FindAllPagingQueryProviderTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/database/support/FindAllPagingQueryProviderTests.java
new file mode 100644
index 00000000..5da934bd
--- /dev/null
+++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/database/support/FindAllPagingQueryProviderTests.java
@@ -0,0 +1,81 @@
+/*
+ * 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.database.support;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.springframework.cloud.task.util.TestDBUtils;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+
+/**
+ * @author Glenn Renfro
+ */
+@RunWith(Parameterized.class)
+public class FindAllPagingQueryProviderTests {
+
+ private String databaseProductName;
+ private String expectedQuery;
+ private Pageable pageable = new PageRequest(0, 10);
+
+ @Parameterized.Parameters
+ public static Collection