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 ceb38e4e..bdd05e6e 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 @@ -26,6 +26,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.ApplicationArguments; import org.springframework.cloud.task.listener.TaskLifecycleListener; import org.springframework.cloud.task.repository.TaskNameResolver; import org.springframework.cloud.task.repository.TaskRepository; @@ -60,6 +61,9 @@ public class SimpleTaskConfiguration { @Autowired private ResourceLoader resourceLoader; + @Autowired(required = false) + private ApplicationArguments applicationArguments; + @Value("${spring.class.initialize.enable:true}") private boolean taskInitializationEnable; @@ -78,7 +82,7 @@ public class SimpleTaskConfiguration { @Bean public TaskLifecycleListener taskLifecycleListener() { - return new TaskLifecycleListener(taskRepository(), taskNameResolver()); + return new TaskLifecycleListener(taskRepository(), taskNameResolver(), this.applicationArguments); } @Bean 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 index a83ed17c..7d171ef5 100644 --- 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 @@ -18,11 +18,14 @@ package org.springframework.cloud.task.listener; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.boot.ApplicationArguments; import org.springframework.boot.context.event.ApplicationFailedEvent; import org.springframework.cloud.task.repository.TaskExecution; import org.springframework.cloud.task.repository.TaskNameResolver; @@ -63,16 +66,20 @@ public class TaskLifecycleListener implements ApplicationListener args = new ArrayList<>(0); + + if(this.applicationArguments != null) { + args = Arrays.asList(this.applicationArguments.getSourceArgs()); + } + this.taskExecution = new TaskExecution(this.taskRepository.getNextExecutionId(), 0, this.taskNameResolver.getTaskName(), new Date(), null, null, null, - new ArrayList(0), null); + args, null); this.taskRepository.createTaskExecution(this.taskExecution); } 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 index 0df17353..b9c057ef 100644 --- 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 @@ -17,21 +17,35 @@ package org.springframework.cloud.task.listener; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import ch.qos.logback.core.Appender; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.context.event.ApplicationFailedEvent; +import org.springframework.cloud.task.repository.TaskExecution; +import org.springframework.cloud.task.repository.TaskExplorer; import org.springframework.cloud.task.util.TestDefaultConfiguration; -import org.springframework.cloud.task.util.TestVerifierUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.context.event.ContextClosedEvent; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; /** * Verifies that the TaskLifecycleListener Methods record the appropriate log header entries and @@ -42,14 +56,14 @@ import org.springframework.context.event.ContextClosedEvent; */ public class TaskLifecycleListenerTests { - @Autowired - private TaskLifecycleListener listener; - private AnnotationConfigApplicationContext context; + private TaskExplorer taskExplorer; + @Before public void setUp() { context = new AnnotationConfigApplicationContext(); + context.setId("testTask"); context.register(TestDefaultConfiguration.class, PropertyPlaceholderAutoConfiguration.class); } @@ -60,41 +74,131 @@ public class TaskLifecycleListenerTests { @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()); + verifyTaskExecution(0, false, 0, null); + } + @Test + public void testTaskCreateWithArgs() { + context.register(ArgsConfiguration.class); + context.refresh(); + verifyTaskExecution(2, false, 0, null); } @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()); + + context.publishEvent(new ContextClosedEvent(context)); + + verifyTaskExecution(0, true, 0, null); } @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")); + RuntimeException exception = new RuntimeException("This was expected"); + context.publishEvent(new ApplicationFailedEvent(new SpringApplication(), new String[0], context, exception)); + + verifyTaskExecution(0, true, 1, exception); + } + + private static String stackTraceToString(Throwable exception) { + StringWriter writer = new StringWriter(); + PrintWriter printWriter = new PrintWriter(writer); + + exception.printStackTrace(printWriter); + + return writer.toString(); + } + + private void verifyTaskExecution(int numberOfParams, boolean update, int exitCode, Throwable exception) { + this.taskExplorer = context.getBean(TaskExplorer.class); + + Sort sort = new Sort("id"); + + PageRequest request = new PageRequest(0, Integer.MAX_VALUE, sort); + + Page taskExecutionsByName = this.taskExplorer.findTaskExecutionsByName("testTask", + request); + assertTrue(taskExecutionsByName.iterator().hasNext()); + TaskExecution taskExecution = taskExecutionsByName.iterator().next(); + + assertEquals(numberOfParams, taskExecution.getParameters().size()); + assertEquals(exitCode, taskExecution.getExitCode()); + + if(exception != null) { + assertTrue(taskExecution.getExitMessage().length() > exception.getStackTrace().length); + } + else { + assertNull(taskExecution.getExitMessage()); + } + + if(update) { + assertTrue(taskExecution.getEndTime().getTime() >= taskExecution.getStartTime().getTime()); + } + else { + assertNull(taskExecution.getEndTime()); + } + + assertNull(taskExecution.getExternalExecutionID()); + assertNull(taskExecution.getStatusCode()); + assertEquals("testTask", taskExecution.getTaskName()); + } + + @Configuration + public static class ArgsConfiguration { + + @Bean + public ApplicationArguments args() { + Map args = new HashMap<>(2); + + args.put("foo", "bar"); + args.put("baz", "qux"); + + return new SimpleApplicationArgs(args); + } + } + + private static class SimpleApplicationArgs implements ApplicationArguments { + + private Map args; + + public SimpleApplicationArgs(Map args) { + this.args = args; + } + + @Override + public String[] getSourceArgs() { + String [] sourceArgs = new String[this.args.size()]; + + int i = 0; + for (Map.Entry stringStringEntry : args.entrySet()) { + sourceArgs[i] = "--" + stringStringEntry.getKey() + "=" + stringStringEntry.getValue(); + i++; + } + + return sourceArgs; + } + + @Override + public Set getOptionNames() { + return this.args.keySet(); + } + + @Override + public boolean containsOption(String s) { + return this.args.containsKey(s); + } + + @Override + public List getOptionValues(String s) { + return Arrays.asList(this.args.get(s)); + } + + @Override + public List getNonOptionArgs() { + throw new UnsupportedOperationException("Not supported at this time."); + } } } 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 04fb2a74..fe1204b4 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,10 +16,14 @@ package org.springframework.cloud.task.util; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; import org.springframework.cloud.task.listener.TaskLifecycleListener; +import org.springframework.cloud.task.repository.TaskExplorer; import org.springframework.cloud.task.repository.TaskNameResolver; import org.springframework.cloud.task.repository.TaskRepository; import org.springframework.cloud.task.repository.dao.MapTaskExecutionDao; +import org.springframework.cloud.task.repository.support.SimpleTaskExplorer; import org.springframework.cloud.task.repository.support.SimpleTaskNameResolver; import org.springframework.cloud.task.repository.support.SimpleTaskRepository; import org.springframework.context.annotation.Bean; @@ -33,9 +37,18 @@ import org.springframework.context.annotation.Configuration; @Configuration public class TestDefaultConfiguration { + private MapTaskExecutionDao dao; + + @Autowired(required = false) + private ApplicationArguments applicationArguments; + + public TestDefaultConfiguration() { + this.dao = new MapTaskExecutionDao(); + } + @Bean public TaskRepository taskRepository(){ - return new SimpleTaskRepository(new MapTaskExecutionDao()); + return new SimpleTaskRepository(this.dao); } @Bean @@ -45,6 +58,11 @@ public class TestDefaultConfiguration { @Bean public TaskLifecycleListener taskHandler(){ - return new TaskLifecycleListener(taskRepository(), taskNameResolver()); + return new TaskLifecycleListener(taskRepository(), taskNameResolver(), applicationArguments); + } + + @Bean + public TaskExplorer taskExplorer() { + return new SimpleTaskExplorer(this.dao); } }