diff --git a/README.adoc b/README.adoc
index 23886f3a..bad4262e 100644
--- a/README.adoc
+++ b/README.adoc
@@ -25,16 +25,15 @@ $ mvn -s settings.xml clean install
public class MyApp {
@Bean
- public MyTask myTask() {
- return new MyTask();
+ public MyTaskApplication myTask() {
+ return new MyTaskApplication();
}
public static void main(String[] args) {
SpringApplication.run(MyApp.class);
}
- @Task("HelloWorldTask")
- public static class MyTask implements CommandLineRunner {
+ public static class MyTaskApplication implements CommandLineRunner {
@Override
public void run(String... strings) throws Exception {
diff --git a/pom.xml b/pom.xml
index b4bf90b7..f2bb2da0 100755
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
- * Enable Spring Task features and provide a base configuration for setting up - * {@link Task} objects in an @Configuration class. + * Enable Spring Task features. * *
* @Configuration
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskConfiguration.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskConfiguration.java
index a0a21fa8..4c5ff1c4 100644
--- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskConfiguration.java
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskConfiguration.java
@@ -23,15 +23,17 @@ import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.task.listener.TaskLifecycleListener;
import org.springframework.cloud.task.repository.TaskRepository;
import org.springframework.cloud.task.repository.support.TaskDatabaseInitializer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Scope;
import org.springframework.core.io.ResourceLoader;
+import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
@@ -41,13 +43,12 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
*
* @author Glenn Renfro
*/
-@EnableTransactionManagement
@Configuration
+@EnableTransactionManagement
public class SimpleTaskConfiguration {
protected static final Log logger = LogFactory.getLog(SimpleTaskConfiguration.class);
-
@Autowired
private ApplicationContext context;
@@ -66,17 +67,23 @@ public class SimpleTaskConfiguration {
private TaskConfigurer configurer;
- @Bean
- @Scope("prototype")
- public TaskHandler taskHandler() {
- return new TaskHandler();
- }
+ private PlatformTransactionManager transactionManager;
@Bean
public TaskRepository taskRepository(){
return taskRepository;
}
+ @Bean
+ public TaskLifecycleListener taskLifecycleListener() {
+ return new TaskLifecycleListener(taskRepository());
+ }
+
+ @Bean
+ public PlatformTransactionManager transactionManager() {
+ return this.transactionManager;
+ }
+
/**
* Sets up the basic components by extracting them from the {@link TaskConfigurer}, defaulting to some
* sensible values as long as a unique DataSource is available.
@@ -93,6 +100,7 @@ public class SimpleTaskConfiguration {
logger.debug(String.format("Using %s TaskConfigurer",
configurer.getClass().getName()));
taskRepository = configurer.getTaskRepository();
+ transactionManager = configurer.getTransactionManager();
initialized = true;
}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskConfigurer.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskConfigurer.java
index b77cb94f..f36da054 100644
--- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskConfigurer.java
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskConfigurer.java
@@ -16,7 +16,9 @@
package org.springframework.cloud.task.configuration;
+import org.springframework.cloud.task.repository.TaskExplorer;
import org.springframework.cloud.task.repository.TaskRepository;
+import org.springframework.transaction.PlatformTransactionManager;
/**
* Provides a strategy interface for providing configuration
@@ -33,4 +35,7 @@ public interface TaskConfigurer {
*/
public TaskRepository getTaskRepository();
+ public PlatformTransactionManager getTransactionManager();
+
+ public TaskExplorer getTaskExplorer();
}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskHandler.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskHandler.java
deleted file mode 100644
index db4460bf..00000000
--- a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/TaskHandler.java
+++ /dev/null
@@ -1,124 +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.configuration;
-
-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.ApplicationRunner;
-import org.springframework.boot.CommandLineRunner;
-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
- * {@link org.springframework.boot.ApplicationRunner} and
- * {@link org.springframework.boot.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 {@link 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 the point where the run method in a {@link CommandLineRunner} or
- * {@link ApplicationRunner} is executed
- */
- @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 {@link 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 the point where the run method in a {@link CommandLineRunner} or
- * {@link ApplicationRunner} is executed
- */
- @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 {@link 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 the point where the run method in a {@link CommandLineRunner} or
- * {@link ApplicationRunner} is executed
- */
- @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);
- }
-
- public TaskExecution getTaskExecution() {
- return taskExecution;
- }
-}
diff --git a/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskLifecycleListener.java b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskLifecycleListener.java
new file mode 100644
index 00000000..5bb62f59
--- /dev/null
+++ b/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskLifecycleListener.java
@@ -0,0 +1,170 @@
+/*
+ * 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.listener;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Date;
+import java.util.UUID;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.beans.BeansException;
+import org.springframework.boot.context.event.ApplicationFailedEvent;
+import org.springframework.cloud.task.repository.TaskExecution;
+import org.springframework.cloud.task.repository.TaskRepository;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextClosedEvent;
+import org.springframework.context.event.ContextRefreshedEvent;
+
+/**
+ * Monitors the lifecycle of a task. This listener will record both the start and end of
+ * a task in the registered {@link TaskRepository}.
+ *
+ * The following events are used to identify the start and end of a task:
+ *
+ *
+ * - {@link ContextRefreshedEvent} - Used to identify the start of a task. A task
+ * is expected to contain a single application context.
+ * - {@link ContextClosedEvent} - Used to identify the successful end of a task.
+ * - {@link ApplicationFailedEvent} - Used to identify the failure of a task.
+ *
+ *
+ * NOTE: Multiple contexts (including parent/child relationships) will result in
+ * only the first context refresh that contains this instance being recorded.
+ *
+ * @author Michael Minella
+ */
+public class TaskLifecycleListener implements ApplicationListener,
+ ApplicationContextAware {
+
+ private final static Logger logger = LoggerFactory.getLogger(TaskLifecycleListener.class);
+
+ private final TaskRepository taskRepository;
+
+ private TaskExecution taskExecution;
+
+ private boolean started = false;
+
+ private ApplicationContext applicationContext;
+
+ /**
+ * @param taskRepository The repository to record executions in.
+ */
+ public TaskLifecycleListener(TaskRepository taskRepository) {
+ this.taskRepository = taskRepository;
+ }
+
+ /**
+ * Utilizes {@link ApplicationEvent}s to determine the start, end, and failure of a
+ * task. Specifically:
+ *
+ * - {@link ContextRefreshedEvent} - Start of a task
+ * - {@link ContextClosedEvent} - Successful end of a task
+ * - {@link ApplicationFailedEvent} - Failure of a task
+ *
+ *
+ * @param applicationEvent The application being listened for.
+ */
+ @Override
+ public void onApplicationEvent(ApplicationEvent applicationEvent) {
+ if(applicationEvent instanceof ContextRefreshedEvent) {
+ doTaskStart();
+ started = true;
+ }
+ else if(applicationEvent instanceof ContextClosedEvent) {
+ doTaskEnd();
+ }
+ else if(applicationEvent instanceof ApplicationFailedEvent) {
+ doTaskFailed(((ApplicationFailedEvent) applicationEvent).getException());
+ }
+ }
+
+ private void doTaskFailed(Throwable exception) {
+
+ if(started) {
+ this.taskExecution.setEndTime(new Date());
+ //TODO: get exit code from new event thrown by Boot.
+ this.taskExecution.setExitCode(1);
+ this.taskExecution.setExitMessage(stackTraceToString(exception));
+
+ taskRepository.update(taskExecution);
+ }
+ else {
+ logger.error("An event to fail a task has been received for a task that has " +
+ "not yet started.");
+ }
+ }
+
+ private String stackTraceToString(Throwable exception) {
+ StringWriter writer = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(writer);
+
+ exception.printStackTrace(printWriter);
+
+ return writer.toString();
+ }
+
+ private void doTaskEnd() {
+ if(started) {
+ this.taskExecution.setEndTime(new Date());
+ //TODO: get exit code from new event thrown by Boot.
+ this.taskExecution.setExitCode(0);
+
+ taskRepository.update(taskExecution);
+ }
+ else {
+ logger.error("An event to end a task has been received for a task that has " +
+ "not yet started.");
+ }
+ }
+
+ private void doTaskStart() {
+
+ if(!started) {
+
+ String executionId = UUID.randomUUID().toString();
+ this.taskExecution = new TaskExecution();
+
+ this.taskExecution.setTaskName(applicationContext.getId());
+ this.taskExecution.setStartTime(new Date());
+ this.taskExecution.setExecutionId(executionId);
+ this.taskRepository.createTaskExecution(this.taskExecution);
+ }
+ else {
+ logger.error("Multiple start events have been received. The first one was " +
+ "recorded.");
+ }
+ }
+
+ /**
+ * Used for testing purposes
+ * @return the current {@link TaskExecution}
+ */
+ TaskExecution getTaskExecution() {
+ return this.taskExecution;
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext)
+ throws BeansException {
+ this.applicationContext = applicationContext;
+ }
+}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/TaskHandlerDefaultTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/TaskHandlerDefaultTests.java
deleted file mode 100644
index 19a4a6ff..00000000
--- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/TaskHandlerDefaultTests.java
+++ /dev/null
@@ -1,87 +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;
-
-import static org.junit.Assert.assertEquals;
-
-import ch.qos.logback.core.Appender;
-import org.aspectj.lang.JoinPoint;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
-import org.springframework.cloud.task.configuration.TaskHandler;
-import org.springframework.cloud.task.repository.TaskExecution;
-import org.springframework.cloud.task.util.TestDefaultConfiguration;
-import org.springframework.cloud.task.util.TestVerifierUtils;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-
-/**
- * Verifies that the TaskHandler Methods record the appropriate log header entries and
- * result codes.
- *
- * @author Glenn Renfro
- */
-@RunWith(SpringJUnit4ClassRunner.class)
-@ContextConfiguration(classes = {TestDefaultConfiguration.class, PropertyPlaceholderAutoConfiguration.class})
-public class TaskHandlerDefaultTests {
-
- @Autowired
- private TaskHandler taskHandler;
-
- @Autowired
- private JoinPoint joinPoint;
-
- @Test
- public void testTaskException() {
- taskHandler.beforeCommandLineRunner(joinPoint);
- final Appender mockAppender = TestVerifierUtils.getMockAppender();
- taskHandler.logExceptionCommandLineRunner(joinPoint);
- TestVerifierUtils.verifyLogEntryExists(mockAppender,
- "Updating: TaskExecution{executionId='" +
- taskHandler.getTaskExecution().getExecutionId());
- TaskExecution taskExecution = taskHandler.getTaskExecution();
- assertEquals("exit code for exception should be 1", taskExecution.getExitCode(),
- 1);
- }
-
- @Test
- public void testTaskCreate() {
- final Appender mockAppender = TestVerifierUtils.getMockAppender();
- taskHandler.beforeCommandLineRunner(joinPoint);
- TestVerifierUtils.verifyLogEntryExists(mockAppender,
- "Creating: TaskExecution{executionId='" +
- taskHandler.getTaskExecution().getExecutionId());
- assertEquals("Create should report that exit code is zero",
- 0, taskHandler.getTaskExecution().getExitCode());
-
- }
-
- @Test
- public void testTaskUpdate() {
- taskHandler.beforeCommandLineRunner(joinPoint);
- final Appender mockAppender = TestVerifierUtils.getMockAppender();
- taskHandler.afterReturnCommandLineRunner(joinPoint);
- TestVerifierUtils.verifyLogEntryExists(mockAppender,
- "Updating: TaskExecution{executionId='" +
- taskHandler.getTaskExecution().getExecutionId());
- assertEquals("Update should report that exit code is zero",
- 0, taskHandler.getTaskExecution().getExitCode());
- }
-
-}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TestConfiguration.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TestConfiguration.java
new file mode 100644
index 00000000..1930a885
--- /dev/null
+++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/configuration/TestConfiguration.java
@@ -0,0 +1,80 @@
+/*
+ * 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.configuration;
+
+import javax.sql.DataSource;
+
+import org.springframework.batch.support.transaction.ResourcelessTransactionManager;
+import org.springframework.beans.factory.annotation.Autowired;
+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.dao.TaskExecutionDao;
+import org.springframework.cloud.task.repository.support.SimpleTaskExplorer;
+import org.springframework.cloud.task.repository.support.SimpleTaskRepository;
+import org.springframework.cloud.task.repository.support.TaskDatabaseInitializer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+import org.springframework.transaction.PlatformTransactionManager;
+
+/**
+ * @author Michael Minella
+ */
+
+@Configuration
+public class TestConfiguration {
+
+ @Autowired(required = false)
+ private DataSource dataSource;
+
+ @Autowired(required = false)
+ private ResourceLoader resourceLoader;
+
+ @Bean
+ public TaskRepository taskRepository(TaskExecutionDao taskExecutionDao){
+ return new SimpleTaskRepository(taskExecutionDao);
+ }
+
+ @Bean
+ public PlatformTransactionManager transactionManager() {
+ if(dataSource == null) {
+ return new ResourcelessTransactionManager();
+ }
+ else {
+ TaskDatabaseInitializer.initializeDatabase(dataSource, this.resourceLoader);
+
+ return new DataSourceTransactionManager(dataSource);
+ }
+ }
+
+ @Bean
+ public TaskExplorer taskExplorer(TaskExecutionDao taskExecutionDao) {
+ return new SimpleTaskExplorer(taskExecutionDao);
+ }
+
+ @Bean
+ public TaskExecutionDao taskExecutionDao() {
+ if(dataSource != null) {
+ return new JdbcTaskExecutionDao(dataSource);
+ }
+ else {
+ return new MapTaskExecutionDao();
+ }
+ }
+}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/listener/TaskLifecycleListenerTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/listener/TaskLifecycleListenerTests.java
new file mode 100644
index 00000000..e0c12071
--- /dev/null
+++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/listener/TaskLifecycleListenerTests.java
@@ -0,0 +1,100 @@
+/*
+ * 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.listener;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import ch.qos.logback.core.Appender;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
+import org.springframework.boot.context.event.ApplicationFailedEvent;
+import org.springframework.cloud.task.util.TestDefaultConfiguration;
+import org.springframework.cloud.task.util.TestVerifierUtils;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.event.ContextClosedEvent;
+
+/**
+ * Verifies that the TaskLifecycleListener Methods record the appropriate log header entries and
+ * result codes.
+ *
+ * @author Glenn Renfro
+ * @author Michael Minella
+ */
+public class TaskLifecycleListenerTests {
+
+ @Autowired
+ private TaskLifecycleListener listener;
+
+ private AnnotationConfigApplicationContext context;
+
+ @Before
+ public void setUp() {
+ context = new AnnotationConfigApplicationContext();
+ context.register(TestDefaultConfiguration.class, PropertyPlaceholderAutoConfiguration.class);
+ }
+
+ @After
+ public void tearDown() {
+ context.close();
+ }
+
+ @Test
+ public void testTaskCreate() {
+ final Appender mockAppender = TestVerifierUtils.getMockAppender();
+ context.refresh();
+ this.listener = context.getBean(TaskLifecycleListener.class);
+ TestVerifierUtils.verifyLogEntryExists(mockAppender,
+ "Creating: TaskExecution{executionId='" +
+ listener.getTaskExecution().getExecutionId());
+ assertEquals("Create should report that exit code is zero",
+ 0, listener.getTaskExecution().getExitCode());
+
+ }
+
+ @Test
+ public void testTaskUpdate() {
+ context.refresh();
+ final Appender mockAppender = TestVerifierUtils.getMockAppender();
+ this.listener = context.getBean(TaskLifecycleListener.class);
+ this.listener.onApplicationEvent(new ContextClosedEvent(context));
+ TestVerifierUtils.verifyLogEntryExists(mockAppender,
+ "Updating: TaskExecution{executionId='" +
+ listener.getTaskExecution().getExecutionId());
+ assertEquals("Update should report that exit code is zero",
+ 0, listener.getTaskExecution().getExitCode());
+ }
+
+ @Test
+ public void testTaskFailedUpdate() {
+ context.refresh();
+ final Appender mockAppender = TestVerifierUtils.getMockAppender();
+ this.listener = context.getBean(TaskLifecycleListener.class);
+ listener.onApplicationEvent(new ApplicationFailedEvent(new SpringApplication(), new String[]{}, context, new RuntimeException("This was expected")));
+ TestVerifierUtils.verifyLogEntryExists(mockAppender,
+ "Updating: TaskExecution{executionId='" +
+ listener.getTaskExecution().getExecutionId());
+ assertEquals("Update should report that exit code is one",
+ 1, listener.getTaskExecution().getExitCode());
+ assertTrue("Stack trace missing from exit message", listener.getTaskExecution().getExitMessage().startsWith("java.lang.RuntimeException: This was expected"));
+ }
+}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/dao/JdbcTaskExecutionDaoTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/dao/JdbcTaskExecutionDaoTests.java
index 9624404c..6f80e49b 100644
--- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/dao/JdbcTaskExecutionDaoTests.java
+++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/dao/JdbcTaskExecutionDaoTests.java
@@ -18,47 +18,37 @@ package org.springframework.cloud.task.repository.dao;
import javax.sql.DataSource;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
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.configuration.TestConfiguration;
import org.springframework.cloud.task.repository.TaskExecution;
import org.springframework.cloud.task.util.TestDBUtils;
import org.springframework.cloud.task.util.TestVerifierUtils;
-import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.dao.DuplicateKeyException;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Executes unit tests on JdbcTaskExecutionDao.
*
* @author Glenn Renfro
*/
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = {TestConfiguration.class,
+ EmbeddedDataSourceConfiguration.class,
+ PropertyPlaceholderAutoConfiguration.class})
public class JdbcTaskExecutionDaoTests {
+ @Autowired
private DataSource dataSource;
- private AnnotationConfigApplicationContext context;
-
- @Before
- public void setup() {
- this.context = new AnnotationConfigApplicationContext();
- this.context.register(TestConfiguration.class,
- EmbeddedDataSourceConfiguration.class,
- PropertyPlaceholderAutoConfiguration.class);
- this.context.refresh();
- dataSource = this.context.getBean(DataSource.class);
- }
-
- @After
- public void close() {
- if (this.context != null) {
- this.context.close();
- }
- }
-
@Test
+ @DirtiesContext
public void saveTaskExecution() {
JdbcTaskExecutionDao dao = new JdbcTaskExecutionDao(dataSource);
TaskExecution expectedTaskExecution = TestVerifierUtils.createSampleTaskExecutionNoParam();
@@ -69,6 +59,7 @@ public class JdbcTaskExecutionDaoTests {
}
@Test(expected = DuplicateKeyException.class)
+ @DirtiesContext
public void duplicateSaveTaskExecution() {
JdbcTaskExecutionDao dao = new JdbcTaskExecutionDao(dataSource);
TaskExecution expectedTaskExecution = TestVerifierUtils.createSampleTaskExecutionNoParam();
@@ -77,6 +68,7 @@ public class JdbcTaskExecutionDaoTests {
}
@Test
+ @DirtiesContext
public void updateTaskExecution() {
JdbcTaskExecutionDao dao = new JdbcTaskExecutionDao(dataSource);
@@ -88,14 +80,11 @@ public class JdbcTaskExecutionDaoTests {
}
@Test(expected = IllegalStateException.class)
+ @DirtiesContext
public void updateTaskExecutionWithNoCreate() {
JdbcTaskExecutionDao dao = new JdbcTaskExecutionDao(dataSource);
TaskExecution expectedTaskExecution = TestVerifierUtils.createSampleTaskExecutionNoParam();
dao.updateTaskExecution(expectedTaskExecution);
}
-
- @EnableTask
- protected static class TestConfiguration {
- }
}
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
index b98beb04..47a229a8 100644
--- 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
@@ -40,16 +40,18 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
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.configuration.TestConfiguration;
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;
+import org.springframework.core.io.ResourceLoader;
/**
* @author Glenn Renfro
@@ -59,12 +61,18 @@ public class SimpleTaskExplorerTests {
private AnnotationConfigApplicationContext context;
+ @Autowired(required = false)
private DataSource dataSource;
+ @Autowired
private TaskExecutionDao dao;
+ @Autowired
private TaskExplorer taskExplorer;
+ @Autowired
+ private ResourceLoader resourceLoader;
+
private DaoType testType;
@Parameterized.Parameters
@@ -84,9 +92,11 @@ public class SimpleTaskExplorerTests {
if(testType == DaoType.jdbc){
initializeJdbcExplorerTest();
- }else{
+ }
+ else{
initializeMapExplorerTest();
}
+
taskExplorer = new SimpleTaskExplorer(dao);
}
@@ -242,23 +252,27 @@ public class SimpleTaskExplorerTests {
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);
+
+ context.getAutowireCapableBeanFactory().autowireBeanProperties(this,
+ AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
}
- private void initializeMapExplorerTest(){
- dao = new MapTaskExecutionDao();
+ private void initializeMapExplorerTest() {
+ this.context = new AnnotationConfigApplicationContext();
+ this.context.register(TestConfiguration.class,
+ PropertyPlaceholderAutoConfiguration.class);
+ this.context.refresh();
+
+ context.getAutowireCapableBeanFactory().autowireBeanProperties(this,
+ AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, false);
}
+
private enum DaoType{jdbc, map}
}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskRepositoryJdbcTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskRepositoryJdbcTests.java
index efaafeaa..49216580 100644
--- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskRepositoryJdbcTests.java
+++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskRepositoryJdbcTests.java
@@ -21,9 +21,10 @@ import javax.sql.DataSource;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+
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.configuration.SimpleTaskConfiguration;
import org.springframework.cloud.task.repository.TaskExecution;
import org.springframework.cloud.task.repository.TaskRepository;
import org.springframework.cloud.task.util.TaskExecutionCreator;
@@ -47,7 +48,7 @@ public class SimpleTaskRepositoryJdbcTests {
@Before
public void setup() {
this.context = new AnnotationConfigApplicationContext();
- this.context.register(TestConfiguration.class,
+ this.context.register(SimpleTaskConfiguration.class,
EmbeddedDataSourceConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
@@ -92,10 +93,5 @@ public class SimpleTaskRepositoryJdbcTests {
dataSource, expectedTaskExecution.getExecutionId());
TestVerifierUtils.verifyTaskExecution(expectedTaskExecution, actualTaskExecution);
}
-
- @EnableTask
- protected static class TestConfiguration {
- }
-
}
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 e466e7a8..eba0d539 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
@@ -27,12 +27,12 @@ import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+
import org.springframework.beans.factory.BeanCreationException;
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.configuration.TaskConfigurer;
-import org.springframework.cloud.task.repository.TaskRepository;
+import org.springframework.cloud.task.configuration.SimpleTaskConfiguration;
+import org.springframework.cloud.task.configuration.TestConfiguration;
import org.springframework.cloud.task.repository.dao.MapTaskExecutionDao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
@@ -88,7 +88,7 @@ public class TaskDatabaseInitializerTests {
@Test(expected = BeanCreationException.class)
public void testMultipleDataSourcesContext() throws Exception {
this.context = new AnnotationConfigApplicationContext();
- this.context.register( TestConfiguration.class,
+ this.context.register( SimpleTaskConfiguration.class,
EmbeddedDataSourceConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
DataSource dataSource = mock(DataSource.class);
@@ -96,19 +96,6 @@ public class TaskDatabaseInitializerTests {
this.context.refresh();
}
- @EnableTask
- protected static class TestConfiguration {
- }
-
- @EnableTask
- protected static class TestCustomConfiguration implements TaskConfigurer {
- @Override
- public TaskRepository getTaskRepository() {
- return new SimpleTaskRepository(new MapTaskExecutionDao());
- }
- }
-
@Configuration
- protected static class EmptyConfiguration {
- }
+ public static class EmptyConfiguration {}
}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TaskBasic.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TaskBasic.java
deleted file mode 100644
index 901f5341..00000000
--- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TaskBasic.java
+++ /dev/null
@@ -1,36 +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.util;
-
-import org.springframework.boot.CommandLineRunner;
-import org.springframework.cloud.task.annotation.Task;
-import org.springframework.stereotype.Component;
-
-/**
- * Basic {@link CommandLineRunner} implementation, with task annotation.
- * @author Glenn Renfro
- */
-@Task
-@Component
-public class TaskBasic implements CommandLineRunner{
-
-
- @Override
- public void run(String... args) {
- //noop
- }
-}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestDefaultConfiguration.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestDefaultConfiguration.java
index 59b3b30e..ae3b6307 100644
--- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestDefaultConfiguration.java
+++ b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestDefaultConfiguration.java
@@ -16,8 +16,7 @@
package org.springframework.cloud.task.util;
-import org.aspectj.lang.JoinPoint;
-import org.springframework.cloud.task.configuration.TaskHandler;
+ import org.springframework.cloud.task.listener.TaskLifecycleListener;
import org.springframework.cloud.task.repository.TaskRepository;
import org.springframework.cloud.task.repository.dao.MapTaskExecutionDao;
import org.springframework.cloud.task.repository.support.SimpleTaskRepository;
@@ -38,14 +37,7 @@ public class TestDefaultConfiguration {
}
@Bean
- public TaskHandler taskHandler(){
- return new TaskHandler();
+ public TaskLifecycleListener taskHandler(){
+ return new TaskLifecycleListener(taskRepository());
}
-
- @Bean
- public JoinPoint joinPoint(){
- return new TestJoinPoint();
- }
-
-
}
diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestJoinPoint.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestJoinPoint.java
deleted file mode 100644
index 676de5d1..00000000
--- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/util/TestJoinPoint.java
+++ /dev/null
@@ -1,64 +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.util;
-
-import org.aspectj.lang.JoinPoint;
-import org.aspectj.lang.Signature;
-import org.aspectj.lang.reflect.SourceLocation;
-
-/**
- * Stubbed out join point for testing purposes.
- *
- * @author Glenn Renfro
- */
-public class TestJoinPoint implements JoinPoint {
- public String toShortString() {
- return null;
- }
-
- public String toLongString() {
- return null;
- }
-
- public Object getThis() {
- return null;
- }
-
- public Object getTarget() {
- return new TaskBasic();
- }
-
- public Object[] getArgs() {
- return new Object[0];
- }
-
- public Signature getSignature() {
- return null;
- }
-
- public SourceLocation getSourceLocation() {
- return null;
- }
-
- public String getKind() {
- return null;
- }
-
- public StaticPart getStaticPart() {
- return null;
- }
-}
diff --git a/spring-cloud-task-samples/timestamp/src/main/java/org/springframework/cloud/task/timestamp/TaskApplication.java b/spring-cloud-task-samples/timestamp/src/main/java/org/springframework/cloud/task/timestamp/TaskApplication.java
index bd2b7309..22be8c48 100644
--- a/spring-cloud-task-samples/timestamp/src/main/java/org/springframework/cloud/task/timestamp/TaskApplication.java
+++ b/spring-cloud-task-samples/timestamp/src/main/java/org/springframework/cloud/task/timestamp/TaskApplication.java
@@ -22,13 +22,13 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import org.slf4j.LoggerFactory;
+
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.cloud.task.annotation.EnableTask;
-import org.springframework.cloud.task.annotation.Task;
+import org.springframework.cloud.task.configuration.EnableTask;
import org.springframework.context.annotation.Bean;
/**
@@ -49,9 +49,8 @@ public class TaskApplication {
}
/**
- * A commandline runner that prints a timestamp and is annotated with @Task.
+ * A commandline runner that prints a timestamp.
*/
- @Task("Demo Timestamp Task")
public class TimestampTask implements CommandLineRunner {
private final org.slf4j.Logger logger = LoggerFactory.getLogger(TimestampTask.class);
diff --git a/spring-cloud-task-samples/timestamp/src/main/resources/application.properties b/spring-cloud-task-samples/timestamp/src/main/resources/application.properties
new file mode 100644
index 00000000..db005fb6
--- /dev/null
+++ b/spring-cloud-task-samples/timestamp/src/main/resources/application.properties
@@ -0,0 +1 @@
+spring.application.name=Demo Timestamp Task