From bd1243cae6d78a3b680d40a235048cb8a2e7cc36 Mon Sep 17 00:00:00 2001 From: Glenn Renfro Date: Thu, 19 Nov 2015 17:29:01 -0500 Subject: [PATCH] Initial commit of POC functionality This commit provides the inital code from a POC of spring-cloud-task functionality. Further cleanup will be done and tracked via future stories. --- .gitignore | 32 ++++ README.adoc | 32 ++++ README.md | 1 - pom.xml | 33 ++++ spring-cloud-task-core/pom.xml | 27 +++ .../cloud/task/annotation/Task.java | 49 ++++++ .../task/config/DefaultTaskConfigurer.java | 44 +++++ .../cloud/task/config/TaskConfigurer.java | 36 ++++ .../cloud/task/config/TaskHandler.java | 115 ++++++++++++ .../task/repository/LoggerTaskRepository.java | 38 ++++ .../cloud/task/repository/TaskExecution.java | 163 ++++++++++++++++++ .../cloud/task/repository/TaskExplorer.java | 72 ++++++++ .../cloud/task/repository/TaskRepository.java | 40 +++++ 13 files changed, 681 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 README.adoc delete mode 100644 README.md create mode 100755 pom.xml create mode 100755 spring-cloud-task-core/pom.xml create mode 100644 spring-cloud-task-core/src/main/java/org/springframework/cloud/task/annotation/Task.java create mode 100644 spring-cloud-task-core/src/main/java/org/springframework/cloud/task/config/DefaultTaskConfigurer.java create mode 100644 spring-cloud-task-core/src/main/java/org/springframework/cloud/task/config/TaskConfigurer.java create mode 100644 spring-cloud-task-core/src/main/java/org/springframework/cloud/task/config/TaskHandler.java create mode 100644 spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/LoggerTaskRepository.java create mode 100644 spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskExecution.java create mode 100644 spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskExplorer.java create mode 100644 spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskRepository.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..38d69ecc --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +*~ +.#* +*# +*.sw* +_site/ +.factorypath +.gradletasknamecache +.DS_Store +/application.yml +/application.properties +asciidoctor.css +atlassian-ide-plugin.xml +bin/ +build/ +dump.rdb +out +spring-shell.log +target/ +test-output + +# Eclipse artifacts, including WTP generated manifests +.classpath +.project +.settings/ +.springBeans +spring-*/src/main/java/META-INF/MANIFEST.MF + +# IDEA artifacts and output dirs +*.iml +*.ipr +*.iws +.idea/* diff --git a/README.adoc b/README.adoc new file mode 100644 index 00000000..ed432e5f --- /dev/null +++ b/README.adoc @@ -0,0 +1,32 @@ += Spring Cloud Task + +Is a project centered around the idea of processing on demand. A user is able to develop +a “task” that can be deployed, executed and removed on demand, yet the result of the +process persists beyond the life of the task for future reporting. + + +== Requirements: + +* Java 7 or Above + +== Build: + +[source,shell,indent=2] +---- +$ mvn clean install +---- + +== Example: + +[source,java,indent=2] +---- +@Task("imSampleB") +public class SampleB implements CommandLineRunner { + + @Override + public void run(String... args) { + System.out.println("hello world"); + } + +} +---- diff --git a/README.md b/README.md deleted file mode 100644 index 2e538a09..00000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -# spring-cloud-task diff --git a/pom.xml b/pom.xml new file mode 100755 index 00000000..d5c5a3d3 --- /dev/null +++ b/pom.xml @@ -0,0 +1,33 @@ + + + + + + + + org.springframework.boot + spring-boot-dependencies + 1.3.0.RELEASE + pom + import + + + + + 4.0.0 + + org.springframework.cloud + spring-cloud-task-parent + pom + spring-cloud-task-parent + 1.0.0.BUILD-SNAPSHOT + Spring Cloud Task Parent + + + spring-cloud-task-core + + + + + diff --git a/spring-cloud-task-core/pom.xml b/spring-cloud-task-core/pom.xml new file mode 100755 index 00000000..702ac861 --- /dev/null +++ b/spring-cloud-task-core/pom.xml @@ -0,0 +1,27 @@ + + + + 4.0.0 + + + org.springframework.cloud + spring-cloud-task-parent + 1.0.0.BUILD-SNAPSHOT + + + org.springframework.cloud + spring-cloud-task-core + jar + spring-cloud-task-core + 1.0.0.BUILD-SNAPSHOT + Spring Cloud Task + + + + org.springframework.boot + spring-boot-starter-aop + + + + diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/annotation/Task.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/annotation/Task.java new file mode 100644 index 00000000..94add499 --- /dev/null +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/annotation/Task.java @@ -0,0 +1,49 @@ +/* + * 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.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.cloud.task.config.DefaultTaskConfigurer; +import org.springframework.context.annotation.Import; +import org.springframework.stereotype.Component; + +/** + * Annotation that identifies a class as a task. This annotation will serve as the + * main “hook” to activate the various Spring Cloud Task features. + * + * @author Glenn Renfro + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +@Component +@Import({ DefaultTaskConfigurer.class }) +public @interface Task { + + /** + * Establishes the name associated with the task. The default is empty. + */ + public String value() default ""; + +} diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/config/DefaultTaskConfigurer.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/config/DefaultTaskConfigurer.java new file mode 100644 index 00000000..11e22494 --- /dev/null +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/config/DefaultTaskConfigurer.java @@ -0,0 +1,44 @@ +/* + * 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.config; + +import org.springframework.cloud.task.repository.LoggerTaskRepository; +import org.springframework.cloud.task.repository.TaskRepository; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; + +/** + * If no TaskConfigurer is present this configuration will be used. + * @author Glenn Renfro + */ +@Configuration +public class DefaultTaskConfigurer { + + @Bean + @Scope("prototype") + public TaskHandler taskHandler() { + return new TaskHandler(); + } + + + @Bean + public TaskRepository taskRepository() { + return new LoggerTaskRepository(); + } + +} diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/config/TaskConfigurer.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/config/TaskConfigurer.java new file mode 100644 index 00000000..7ce5b1fa --- /dev/null +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/config/TaskConfigurer.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.config; + +import org.springframework.cloud.task.repository.TaskRepository; + +/** + * Provides a strategy interface for providing configuration + * customization to the task system. + * + * @author Glenn Renfro + */ +public interface TaskConfigurer { + + /** + * Create a Task Repository for the Task. + * + * @return A TaskRepository + */ + public TaskRepository taskRepository(); + +} diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/config/TaskHandler.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/config/TaskHandler.java new file mode 100644 index 00000000..8bed88f8 --- /dev/null +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/config/TaskHandler.java @@ -0,0 +1,115 @@ +/* + * 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.config; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ExitCodeGenerator; +import org.springframework.cloud.task.annotation.Task; +import org.springframework.cloud.task.repository.TaskExecution; +import org.springframework.cloud.task.repository.TaskRepository; +import org.springframework.context.ApplicationContext; + +/** + * Offers the advice on how to record tasks to the repository for both applicationrunner + * and commandlinerunner spring boot applications. + * + * @author Glenn Renfro + */ +@Aspect +public class TaskHandler { + + @Autowired + private ApplicationContext context; + + @Autowired + private TaskRepository repository; + + private String taskName; + + private String executionId; + + private TaskExecution taskExecution; + + /** + * Looks for any CommandLineRunner.run method with its class annotated with @Task + * and calls the repository implementation to store the start of the task in the repo + * before the run starts. + * + * @param joinPoint + */ + @Before("within( @org.springframework.cloud.task.annotation.Task *) && (execution(* org.springframework.boot.CommandLineRunner.run(..)) || execution(* org.springframework.boot.ApplicationRunner.run(..)))") + public void beforeCommandLineRunner(JoinPoint joinPoint) { + executionId = UUID.randomUUID().toString(); + taskExecution = new TaskExecution(); + + Task a = joinPoint.getTarget().getClass().getAnnotation(Task.class); + taskName = a.value(); + if (taskName == null || taskName.length() == 0) { + taskName = joinPoint.getTarget().getClass().getName(); + } + + taskExecution.setTaskName(taskName); + taskExecution.setStartTime(new Date()); + taskExecution.setExecutionId(executionId); + repository.createTaskExecution(taskExecution); + } + + /** + * Looks for any CommandLineRunner.run method with its class annotated with @Task + * and calls repository implementation to store the exit of the task in the repo after + * run returns result. + * + * @param joinPoint + */ + @AfterReturning("within( @org.springframework.cloud.task.annotation.Task *) && (execution(* org.springframework.boot.CommandLineRunner.run(..)) || execution(* org.springframework.boot.ApplicationRunner.run(..)))") + public void afterReturnCommandLineRunner(JoinPoint joinPoint) { + int result = 0; + List generators = new ArrayList(); + generators + .addAll(context.getBeansOfType(ExitCodeGenerator.class).values()); + for (ExitCodeGenerator generator : generators) { + result = generator.getExitCode(); + } + taskExecution.setEndTime(new Date()); + taskExecution.setExitCode(result); + repository.update(taskExecution); + } + + /** + * Looks for any CommandLineRunner. run method with its class annotated with @Task + * and calls the repository implementation to store the exitCode of 1 + * for the task in the repo in the case of an exception. + * + * @param joinPoint + */ + @AfterThrowing("within( @org.springframework.cloud.task.annotation.Task *) && (execution(* org.springframework.boot.CommandLineRunner.run(..)) || execution(* org.springframework.boot.ApplicationRunner.run(..)))") + public void logExceptionCommandLineRunner(JoinPoint joinPoint) { + taskExecution.setEndTime(new Date()); + taskExecution.setExitCode(1); + repository.update(taskExecution); + } + +} diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/LoggerTaskRepository.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/LoggerTaskRepository.java new file mode 100644 index 00000000..4fb85f7c --- /dev/null +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/LoggerTaskRepository.java @@ -0,0 +1,38 @@ +/* + * 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; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Glenn Renfro + */ +public class LoggerTaskRepository implements TaskRepository { + private final static Logger logger = LoggerFactory.getLogger(LoggerTaskRepository.class); + + @Override + public void update(TaskExecution taskExecution) { + logger.info("Updating: " + taskExecution.toString()); + } + + @Override + public void createTaskExecution(TaskExecution taskExecution) { + logger.info("Creating: " + taskExecution.toString()); + } +} diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskExecution.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskExecution.java new file mode 100644 index 00000000..a07adde5 --- /dev/null +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskExecution.java @@ -0,0 +1,163 @@ +/* + * 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; + +import java.util.Date; +import java.util.List; + +/** + * Represents the state of the Task for each execution. + * + * @author Glenn Renfro + */ + +public class TaskExecution { + + public TaskExecution() { + } + + public TaskExecution(String executionId, int exitCode, String taskName, + Date startTime, Date endTime, String statusCode, + String exitMessage, List parameters) { + this.executionId = executionId; + this.exitCode = exitCode; + this.taskName = taskName; + this.startTime = startTime; + this.endTime = endTime; + this.statusCode = statusCode; + this.exitMessage = exitMessage; + this.parameters = parameters; + } + + /** + * The unique id associated with the task execution. + */ + private String executionId; + + /** + * The recorded exit code for the task. + */ + private int exitCode; + + /** + * User defined name for the task. + */ + private String taskName; + + /** + * Time of when the task was started. + */ + private Date startTime; + + /** + * Timestamp of when the task was completed/terminated. + */ + private Date endTime; + + /** + * TBD. + */ + private String statusCode; + + /** + * Message returned from the task or stacktrace.parameters. + */ + private String exitMessage; + + /** + * The parameters that were used for this task execution. + */ + private List parameters; + + public String getExecutionId() { + return executionId; + } + + public void setExecutionId(String executionId) { + this.executionId = executionId; + } + + public int getExitCode() { + return exitCode; + } + + public void setExitCode(int exitCode) { + this.exitCode = exitCode; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public String getStatusCode() { + return statusCode; + } + + public void setStatusCode(String statusCode) { + this.statusCode = statusCode; + } + + public String getExitMessage() { + return exitMessage; + } + + public void setExitMessage(String exitMessage) { + this.exitMessage = exitMessage; + } + + public List getParameters() { + return parameters; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } + + @Override + public String toString() { + return "TaskExecution{" + + "executionId='" + executionId + '\'' + + ", exitCode=" + exitCode + + ", taskName='" + taskName + '\'' + + ", startTime=" + startTime + + ", endTime=" + endTime + + ", statusCode='" + statusCode + '\'' + + ", exitMessage='" + exitMessage + '\'' + + ", parameters=" + parameters + + '}'; + } +} 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 new file mode 100644 index 00000000..acacfd76 --- /dev/null +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskExplorer.java @@ -0,0 +1,72 @@ +/* + * 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; + +import java.util.List; +import java.util.Set; + +/** + * Offers methods that allow users to query the task executions that are available. + * + * @author Glenn Renfro + */ +public interface TaskExplorer { + + /** + * Retrieve a {@link TaskExecution} by its id. + * + * @param executionId the task execution id + * @return the {@link TaskExecution} with this id, or null if not found + */ + public TaskExecution getTaskExecution(Long executionId); + + + /** + * Retrieve a collection of taskExecutions that have the task name provided. + * + * @param taskName the name of the task + * @return the set of running executions for tasks with the specified name + */ + public Set findRunningTaskExecutions(String taskName); + + /** + * Retrieve a list of available task names. + * + * @return the set of task names that have been executed + */ + public List getTaskNames(); + + /** + * Get number of executions for a taskName. + * + * @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); + + /** + * Get a collection/page of executions + * + * @param taskName the name of the task to be searched + * @param start the position of the first execution to return + * @param count the number of executions to return + * @return list of task executions + */ + public List getTaskExecutionsByName(String taskName, int start, int count); + + +} diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskRepository.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskRepository.java new file mode 100644 index 00000000..f416c8eb --- /dev/null +++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/repository/TaskRepository.java @@ -0,0 +1,40 @@ +/* + * 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; + +/** + * TaskRepository interface offers methods that create and update task execution + * information. The interface will support the following methods: + * + * @author Glenn Renfro + */ +public interface TaskRepository { + + /** + * Notifies the repository that a taskExecution needs to be updated. + * + * @param taskExecution taskExecution to be updated + */ + public void update(TaskExecution taskExecution); + + /** + * Notifies the repository that a taskExecution needs to be created. + * + * @param taskExecution taskExecution to be recorded + */ + public void createTaskExecution(TaskExecution taskExecution); +}