diff --git a/spring-cloud-task-batch/src/test/java/org/springframework/cloud/task/batch/listener/TaskBatchExecutionListenerTests.java b/spring-cloud-task-batch/src/test/java/org/springframework/cloud/task/batch/listener/TaskBatchExecutionListenerTests.java index 405d2c56..5066ebf0 100644 --- a/spring-cloud-task-batch/src/test/java/org/springframework/cloud/task/batch/listener/TaskBatchExecutionListenerTests.java +++ b/spring-cloud-task-batch/src/test/java/org/springframework/cloud/task/batch/listener/TaskBatchExecutionListenerTests.java @@ -61,6 +61,8 @@ import static org.junit.Assert.assertEquals; */ public class TaskBatchExecutionListenerTests { + public static final String[] ARGS = new String[] {"--spring.cloud.task.closecontext.enable=false"}; + private ConfigurableApplicationContext applicationContext; @After @@ -76,7 +78,7 @@ public class TaskBatchExecutionListenerTests { PropertyPlaceholderAutoConfiguration.class, EmbeddedDataSourceConfiguration.class, BatchAutoConfiguration.class, - TaskBatchAutoConfiguration.class}, new String[0]); + TaskBatchAutoConfiguration.class}, ARGS); TaskExplorer taskExplorer = this.applicationContext.getBean(TaskExplorer.class); @@ -94,7 +96,7 @@ public class TaskBatchExecutionListenerTests { PropertyPlaceholderAutoConfiguration.class, EmbeddedDataSourceConfiguration.class, BatchAutoConfiguration.class, - TaskBatchAutoConfiguration.class}, new String[0]); + TaskBatchAutoConfiguration.class}, ARGS); TaskExplorer taskExplorer = this.applicationContext.getBean(TaskExplorer.class); @@ -112,7 +114,7 @@ public class TaskBatchExecutionListenerTests { PropertyPlaceholderAutoConfiguration.class, EmbeddedDataSourceConfiguration.class, BatchAutoConfiguration.class, - TaskBatchAutoConfiguration.class}, new String[0]); + TaskBatchAutoConfiguration.class}, ARGS); TaskExplorer taskExplorer = this.applicationContext.getBean(TaskExplorer.class); @@ -128,7 +130,7 @@ public class TaskBatchExecutionListenerTests { this.applicationContext = SpringApplication.run(new Object[] {JobConfiguration.class, PropertyPlaceholderAutoConfiguration.class, BatchAutoConfiguration.class, - TaskBatchAutoConfiguration.class}, new String[0]); + TaskBatchAutoConfiguration.class}, ARGS); TaskExplorer taskExplorer = this.applicationContext.getBean(TaskExplorer.class); @@ -145,7 +147,7 @@ public class TaskBatchExecutionListenerTests { this.applicationContext = SpringApplication.run(new Object[] {MultipleJobConfiguration.class, PropertyPlaceholderAutoConfiguration.class, BatchAutoConfiguration.class, - TaskBatchAutoConfiguration.class}, new String[0]); + TaskBatchAutoConfiguration.class}, ARGS); TaskExplorer taskExplorer = this.applicationContext.getBean(TaskExplorer.class); 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 b2e75b8b..e4b20a3c 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 @@ -28,16 +28,18 @@ 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.boot.ApplicationArguments; import org.springframework.boot.ExitCodeEvent; import org.springframework.boot.context.event.ApplicationFailedEvent; +import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.cloud.task.repository.TaskExecution; import org.springframework.cloud.task.repository.TaskNameResolver; import org.springframework.cloud.task.repository.TaskRepository; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.SmartLifecycle; -import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.util.Assert; @@ -50,17 +52,21 @@ import org.springframework.util.Assert; * * - * NOTE: Multiple contexts (including parent/child relationships) will result in - * only the first context refresh that contains this instance being recorded. + * Note: By default, the context will be closed at the completion of a task (once + * the task repository has been updated). This behavior can be configured via the + * property spring.cloud.task.closecontext.enable (defaults to true). * * @author Michael Minella */ public class TaskLifecycleListener implements ApplicationListener, SmartLifecycle { + @Autowired + private ConfigurableApplicationContext context; + @Autowired(required = false) private Collection taskExecutionListeners; @@ -80,6 +86,9 @@ public class TaskLifecycleListener implements ApplicationListener *
  • {@link ContextRefreshedEvent} - Start of a task
  • - *
  • {@link ContextClosedEvent} - Successful end of a task
  • + *
  • {@link ApplicationReadyEvent} - Successful end of a task
  • *
  • {@link ApplicationFailedEvent} - Failure of a task
  • * * @@ -112,8 +121,9 @@ public class TaskLifecycleListener implements ApplicationListener()); @@ -99,10 +99,11 @@ public class TaskExecutionListenerTests { public void testTaskFail() { RuntimeException exception = new RuntimeException(EXCEPTION_MESSAGE); setupContextForTaskExecutionListener(); - context.publishEvent(new ApplicationFailedEvent(new SpringApplication(), new String[0], context, exception)); - context.publishEvent(new ContextClosedEvent(context)); + SpringApplication application = new SpringApplication(); + context.publishEvent(new ApplicationFailedEvent(application, new String[0], context, exception)); DefaultTaskListenerConfiguration.TestTaskExecutionListener taskExecutionListener = context.getBean(DefaultTaskListenerConfiguration.TestTaskExecutionListener.class); + context.publishEvent(new ApplicationReadyEvent(application, new String[0], context)); TaskExecution taskExecution = new TaskExecution(0, 1, "wombat", new Date(), new Date(), null, new ArrayList()); @@ -132,7 +133,7 @@ public class TaskExecutionListenerTests { setupContextForAnnotatedListener(); DefaultAnnotationConfiguration.AnnotatedTaskListener annotatedListener = context.getBean(DefaultAnnotationConfiguration.AnnotatedTaskListener.class); - context.publishEvent(new ContextClosedEvent(context)); + context.publishEvent(new ApplicationReadyEvent(new SpringApplication(), new String[0], context)); TaskExecution taskExecution = new TaskExecution(0, 0, "wombat", new Date(), new Date(), null, new ArrayList()); @@ -147,10 +148,11 @@ public class TaskExecutionListenerTests { public void testAnnotationFail() { RuntimeException exception = new RuntimeException(EXCEPTION_MESSAGE); setupContextForAnnotatedListener(); - context.publishEvent(new ApplicationFailedEvent(new SpringApplication(), new String[0], context, exception)); - context.publishEvent(new ContextClosedEvent(context)); + SpringApplication application = new SpringApplication(); + context.publishEvent(new ApplicationFailedEvent(application, new String[0], context, exception)); DefaultAnnotationConfiguration.AnnotatedTaskListener annotatedListener = context.getBean(DefaultAnnotationConfiguration.AnnotatedTaskListener.class); + context.publishEvent(new ApplicationReadyEvent(application, new String[0], context)); TaskExecution taskExecution = new TaskExecution(0, 1, "wombat", new Date(), new Date(), null, new ArrayList()); 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 4f64ea73..13b20c34 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 @@ -16,11 +16,6 @@ package org.springframework.cloud.task.listener; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -30,21 +25,28 @@ import java.util.Set; import org.junit.After; import org.junit.Before; import org.junit.Test; + 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.boot.context.event.ApplicationReadyEvent; import org.springframework.cloud.task.repository.TaskExecution; import org.springframework.cloud.task.repository.TaskExplorer; import org.springframework.cloud.task.util.TestDefaultConfiguration; +import org.springframework.context.ConfigurableApplicationContext; 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; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + /** * Verifies that the TaskLifecycleListener Methods record the appropriate log header entries and * result codes. @@ -67,12 +69,15 @@ public class TaskLifecycleListenerTests { @After public void tearDown() { - context.close(); + if(context != null && context.isActive()) { + context.close(); + } } @Test public void testTaskCreate() { context.refresh(); + this.taskExplorer = context.getBean(TaskExplorer.class); verifyTaskExecution(0, false, 0, null); } @@ -80,14 +85,16 @@ public class TaskLifecycleListenerTests { public void testTaskCreateWithArgs() { context.register(ArgsConfiguration.class); context.refresh(); + this.taskExplorer = context.getBean(TaskExplorer.class); verifyTaskExecution(2, false, 0, null); } @Test public void testTaskUpdate() { context.refresh(); + this.taskExplorer = context.getBean(TaskExplorer.class); - context.publishEvent(new ContextClosedEvent(context)); + context.publishEvent(new ApplicationReadyEvent(new SpringApplication(), new String[0], context)); verifyTaskExecution(0, true, 0, null); } @@ -96,14 +103,28 @@ public class TaskLifecycleListenerTests { public void testTaskFailedUpdate() { context.refresh(); RuntimeException exception = new RuntimeException("This was expected"); - context.publishEvent(new ApplicationFailedEvent(new SpringApplication(), new String[0], context, exception)); - context.publishEvent(new ContextClosedEvent(context)); + SpringApplication application = new SpringApplication(); + context.publishEvent(new ApplicationFailedEvent(application, new String[0], context, exception)); + this.taskExplorer = context.getBean(TaskExplorer.class); + context.publishEvent(new ApplicationReadyEvent(application, new String[0], context)); verifyTaskExecution(0, true, 1, exception); } + @Test + public void testNoClosingOfContext() { + ConfigurableApplicationContext applicationContext = SpringApplication.run(new Object[] {TestDefaultConfiguration.class, PropertyPlaceholderAutoConfiguration.class}, + new String[] {"--spring.cloud.task.closecontext.enable=false"}); + + try { + assertTrue(applicationContext.isActive()); + } + finally { + applicationContext.close(); + } + } + private void verifyTaskExecution(int numberOfParams, boolean update, Integer exitCode, Throwable exception) { - this.taskExplorer = context.getBean(TaskExplorer.class); Sort sort = new Sort("id"); diff --git a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskRepositoryLoggerTests.java b/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskRepositoryLoggerTests.java deleted file mode 100644 index 1aef4f18..00000000 --- a/spring-cloud-task-core/src/test/java/org/springframework/cloud/task/repository/support/SimpleTaskRepositoryLoggerTests.java +++ /dev/null @@ -1,65 +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.Date; - -import ch.qos.logback.core.Appender; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.task.repository.TaskExecution; -import org.springframework.cloud.task.repository.TaskRepository; -import org.springframework.cloud.task.util.TaskExecutionCreator; -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 SimpleTaskRepository has correct prefixes written to logs. - * @author Glenn Renfro - */ -@RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = TestDefaultConfiguration.class) -public class SimpleTaskRepositoryLoggerTests { - - @Autowired - private TaskRepository taskRepository; - - @Test - public void testCreateTaskExecution() { - final Appender mockAppender = TestVerifierUtils.getMockAppender(); - TaskExecution expectedTaskExecution = - TaskExecutionCreator.createAndStoreTaskExecutionNoParams(taskRepository); - TestVerifierUtils.verifyLogEntryExists(mockAppender, - "Creating: TaskExecution{executionId=" + expectedTaskExecution.getExecutionId()); - } - - @Test - public void testTaskComplete() { - final Appender mockAppender = TestVerifierUtils.getMockAppender(); - TaskExecution expectedTaskExecution = - TaskExecutionCreator.createAndStoreTaskExecutionNoParams(taskRepository); - expectedTaskExecution.setEndTime(new Date()); - TaskExecutionCreator.completeExecution(taskRepository, expectedTaskExecution); - TestVerifierUtils.verifyLogEntryExists(mockAppender, - "Updating: TaskExecution with executionId=" - + expectedTaskExecution.getExecutionId()); - } - -} diff --git a/spring-cloud-task-docs/src/main/asciidoc/features.adoc b/spring-cloud-task-docs/src/main/asciidoc/features.adoc index 918115da..16a23839 100644 --- a/spring-cloud-task-docs/src/main/asciidoc/features.adoc +++ b/spring-cloud-task-docs/src/main/asciidoc/features.adoc @@ -37,7 +37,6 @@ start event. This event is triggered via `SmartLifecycle#start` being triggered Spring Framework. This indicates to the system that all beans are ready for use and is before the execution of any of the `*Runner`s provided by Spring Boot. - NOTE: The recording of a task will only occur upon the successful bootstrapping of an `ApplicationContext`. If the context fails to bootstrap at all, the task's execution will not be recorded. @@ -46,6 +45,10 @@ Upon completion of all of the `*Runner#run` calls from Spring Boot or the failur `ApplicationContext` (indicated via a `ApplicationFailedEvent`), the task execution is updated in the repository with the results. +NOTE: At the completion of a task (all `*Runner#run` methods are called and the task +repository has been updated) the `ApplicationContext` will be closed. This behavior can +be overriden by setting the property `spring.cloud.task.closecontext.enabled` to false. + [[features-task-execution-details]] === The TaskExecution @@ -70,7 +73,7 @@ assumed to be 0. |The time the task was started as indicated by the `SmartLifecycle#start` call. |`endTime` -|The time the task was completed as indicated by the `ContextClosedEvent`. +|The time the task was completed as indicated by the `ApplicationReadyEvent`. |`exitMessage` |Any information available at the time of exit. If an exception is the cause of the end diff --git a/spring-cloud-task-samples/batch-job/src/test/java/io/spring/BatchJobApplicationTests.java b/spring-cloud-task-samples/batch-job/src/test/java/io/spring/BatchJobApplicationTests.java index f6fec0d9..b69bddeb 100644 --- a/spring-cloud-task-samples/batch-job/src/test/java/io/spring/BatchJobApplicationTests.java +++ b/spring-cloud-task-samples/batch-job/src/test/java/io/spring/BatchJobApplicationTests.java @@ -44,9 +44,9 @@ public class BatchJobApplicationTests { final String CREATE_TASK_MESSAGE = "Creating: TaskExecution{executionId="; final String UPDATE_TASK_MESSAGE = "Updating: TaskExecution with executionId="; final String JOB_ASSOCIATION_MESSAGE = "The job execution id "; + final String EXIT_CODE_MESSAGE = "with the following {exitCode=0"; - assertEquals(0, SpringApplication.exit(SpringApplication - .run(BatchJobApplication.class))); + SpringApplication.run(BatchJobApplication.class); String output = this.outputCapture.toString(); assertTrue("Unable to find the timestamp: " + output, @@ -55,6 +55,8 @@ public class BatchJobApplicationTests { output.contains(CREATE_TASK_MESSAGE)); assertTrue("Test results do not show success message: " + output, output.contains(UPDATE_TASK_MESSAGE)); + assertTrue("Test results do not show success message: " + output, + output.contains(EXIT_CODE_MESSAGE)); int i = output.indexOf(JOB_ASSOCIATION_MESSAGE); diff --git a/spring-cloud-task-samples/timestamp/src/test/java/org/springframework/cloud/task/timestamp/TaskApplicationTests.java b/spring-cloud-task-samples/timestamp/src/test/java/org/springframework/cloud/task/timestamp/TaskApplicationTests.java index 831f1307..ca173764 100644 --- a/spring-cloud-task-samples/timestamp/src/test/java/org/springframework/cloud/task/timestamp/TaskApplicationTests.java +++ b/spring-cloud-task-samples/timestamp/src/test/java/org/springframework/cloud/task/timestamp/TaskApplicationTests.java @@ -16,17 +16,18 @@ package org.springframework.cloud.task.timestamp; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.util.regex.Matcher; import java.util.regex.Pattern; import org.junit.Rule; import org.junit.Test; + import org.springframework.boot.SpringApplication; import org.springframework.boot.test.OutputCapture; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + /** * Verifies that the Task Application outputs the correct task log entries. * @@ -42,10 +43,10 @@ public class TaskApplicationTests { final String TEST_DATE_DOTS = "......."; final String CREATE_TASK_MESSAGE = "Creating: TaskExecution{executionId="; final String UPDATE_TASK_MESSAGE = "Updating: TaskExecution with executionId="; + final String EXIT_CODE_MESSAGE = "with the following {exitCode=0"; String[] args = { "--format=yyyy" + TEST_DATE_DOTS }; - assertEquals(0, SpringApplication.exit(SpringApplication - .run(TaskApplication.class, args))); + SpringApplication.run(TaskApplication.class, args); String output = this.outputCapture.toString(); assertTrue("Unable to find the timestamp: " + output, @@ -54,6 +55,8 @@ public class TaskApplicationTests { output.contains(CREATE_TASK_MESSAGE)); assertTrue("Test results do not show success message: " + output, output.contains(UPDATE_TASK_MESSAGE)); + assertTrue("Test results have incorrect exit code: " + output, + output.contains(EXIT_CODE_MESSAGE)); String taskTitle = "Demo Timestamp Task"; Pattern pattern = Pattern.compile(taskTitle); @@ -62,7 +65,6 @@ public class TaskApplicationTests { while (matcher.find()) { count++; } - assertEquals("The number of task titles did not match expected: ", 3, count); + assertEquals("The number of task titles did not match expected: ", 1, count); } - } diff --git a/spring-cloud-task-samples/timestamp/src/test/resources/application.properties b/spring-cloud-task-samples/timestamp/src/test/resources/application.properties index 0103b71b..575294ca 100644 --- a/spring-cloud-task-samples/timestamp/src/test/resources/application.properties +++ b/spring-cloud-task-samples/timestamp/src/test/resources/application.properties @@ -14,5 +14,5 @@ # limitations under the License. # -logging.level.root=DEBUG +logging.level.org.springframework.cloud.task=DEBUG spring.application.name=Demo Timestamp Task