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;
*
* - {@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 ApplicationReadyEvent} - 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.
+ * 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