Skip to content

Commit 270d80a

Browse files
committed
TaskLifecycleListener triggers early, and possibly cyclical, bean initialisation
Replaced TaskListenerExecutorFactory with TaskListenerExecutorObjectProviderTests. This delays the need to acquire a bean till it is needed vs at application event time. resolves spring-cloud#448
1 parent 8f680de commit 270d80a

File tree

6 files changed

+228
-70
lines changed

6 files changed

+228
-70
lines changed

spring-cloud-task-core/src/main/java/org/springframework/cloud/task/configuration/SimpleTaskAutoConfiguration.java

+2-11
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
3434
import org.springframework.boot.context.properties.EnableConfigurationProperties;
3535
import org.springframework.cloud.task.listener.TaskLifecycleListener;
36-
import org.springframework.cloud.task.listener.annotation.TaskListenerExecutorFactoryBean;
36+
import org.springframework.cloud.task.listener.annotation.TaskListenerExecutorObjectProvider;
3737
import org.springframework.cloud.task.repository.TaskExplorer;
3838
import org.springframework.cloud.task.repository.TaskNameResolver;
3939
import org.springframework.cloud.task.repository.TaskRepository;
@@ -80,8 +80,6 @@ public class SimpleTaskAutoConfiguration {
8080

8181
private TaskLifecycleListener taskLifecycleListener;
8282

83-
private TaskListenerExecutorFactoryBean taskListenerExecutorFactoryBean;
84-
8583
private PlatformTransactionManager platformTransactionManager;
8684

8785
private TaskExplorer taskExplorer;
@@ -96,12 +94,6 @@ public TaskLifecycleListener taskLifecycleListener() {
9694
return this.taskLifecycleListener;
9795
}
9896

99-
@Bean
100-
public TaskListenerExecutorFactoryBean taskListenerExecutor()
101-
throws Exception {
102-
return this.taskListenerExecutorFactoryBean;
103-
}
104-
10597
@Bean
10698
@ConditionalOnMissingBean
10799
public PlatformTransactionManager transactionManager() {
@@ -144,12 +136,11 @@ protected void initialize() throws Exception {
144136
taskConfigurer.getClass().getName()));
145137

146138
this.taskRepository = taskConfigurer.getTaskRepository();
147-
this.taskListenerExecutorFactoryBean = new TaskListenerExecutorFactoryBean(context);
148139
this.platformTransactionManager = taskConfigurer.getTransactionManager();
149140
this.taskExplorer = taskConfigurer.getTaskExplorer();
150141

151142
this.taskLifecycleListener = new TaskLifecycleListener(this.taskRepository, taskNameResolver(),
152-
this.applicationArguments, taskExplorer, taskProperties);
143+
this.applicationArguments, taskExplorer, taskProperties, new TaskListenerExecutorObjectProvider(context));
153144

154145
initialized = true;
155146
}

spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/TaskLifecycleListener.java

+27-7
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.boot.context.event.ApplicationFailedEvent;
3737
import org.springframework.boot.context.event.ApplicationReadyEvent;
3838
import org.springframework.cloud.task.configuration.TaskProperties;
39+
import org.springframework.cloud.task.listener.annotation.TaskListenerExecutorObjectProvider;
3940
import org.springframework.cloud.task.repository.TaskExecution;
4041
import org.springframework.cloud.task.repository.TaskExplorer;
4142
import org.springframework.cloud.task.repository.TaskNameResolver;
@@ -74,14 +75,20 @@ public class TaskLifecycleListener implements ApplicationListener<ApplicationEve
7475
private ConfigurableApplicationContext context;
7576

7677
@Autowired(required = false)
77-
private Collection<TaskExecutionListener> taskExecutionListeners;
78+
private Collection<TaskExecutionListener> taskExecutionListenersFromContext;
79+
80+
private List<TaskExecutionListener> taskExecutionListeners;
81+
82+
private boolean isTaskExecutionListenersInitialized;
7883

7984
private final static Log logger = LogFactory.getLog(TaskLifecycleListener.class);
8085

8186
private final TaskRepository taskRepository;
8287

8388
private final TaskExplorer taskExplorer;
8489

90+
private final TaskListenerExecutorObjectProvider taskListenerExecutorObjectProvider;
91+
8592
private TaskExecution taskExecution;
8693

8794
private TaskProperties taskProperties;
@@ -108,17 +115,20 @@ public class TaskLifecycleListener implements ApplicationListener<ApplicationEve
108115
public TaskLifecycleListener(TaskRepository taskRepository,
109116
TaskNameResolver taskNameResolver,
110117
ApplicationArguments applicationArguments, TaskExplorer taskExplorer,
111-
TaskProperties taskProperties) {
118+
TaskProperties taskProperties,
119+
TaskListenerExecutorObjectProvider taskListenerExecutorObjectProvider) {
112120
Assert.notNull(taskRepository, "A taskRepository is required");
113121
Assert.notNull(taskNameResolver, "A taskNameResolver is required");
114122
Assert.notNull(taskExplorer, "A taskExplorer is required");
115123
Assert.notNull(taskProperties, "TaskProperties is required");
124+
Assert.notNull(taskListenerExecutorObjectProvider, "A TaskListenerExecutorObjectFactory is required");
116125

117126
this.taskRepository = taskRepository;
118127
this.taskNameResolver = taskNameResolver;
119128
this.applicationArguments = applicationArguments;
120129
this.taskExplorer = taskExplorer;
121130
this.taskProperties = taskProperties;
131+
this.taskListenerExecutorObjectProvider = taskListenerExecutorObjectProvider;
122132
}
123133

124134
/**
@@ -223,6 +233,7 @@ else if (this.listenerFailed || this.applicationFailedEvent != null) {
223233
private void doTaskStart() {
224234

225235
if(!this.started) {
236+
getTaskExecutionListeners();
226237
List<String> args = new ArrayList<>(0);
227238

228239
if(this.applicationArguments != null) {
@@ -258,11 +269,11 @@ private void doTaskStart() {
258269

259270
private TaskExecution invokeOnTaskStartup(TaskExecution taskExecution){
260271
TaskExecution listenerTaskExecution = getTaskExecutionCopy(taskExecution);
261-
if (this.taskExecutionListeners != null) {
272+
List<TaskExecutionListener> startupListenerList = new ArrayList<>(this.taskExecutionListeners);
273+
if (startupListenerList != null) {
262274
try {
263-
List<TaskExecutionListener> starterList = new ArrayList<>(taskExecutionListeners);
264-
Collections.reverse(starterList);
265-
for (TaskExecutionListener taskExecutionListener : starterList) {
275+
Collections.reverse(startupListenerList);
276+
for (TaskExecutionListener taskExecutionListener : startupListenerList) {
266277
taskExecutionListener.onTaskStartup(listenerTaskExecution);
267278
}
268279
}
@@ -300,7 +311,7 @@ private TaskExecution invokeOnTaskEnd(TaskExecution taskExecution){
300311

301312
private TaskExecution invokeOnTaskError(TaskExecution taskExecution, Throwable throwable){
302313
TaskExecution listenerTaskExecution = getTaskExecutionCopy(taskExecution);
303-
if (taskExecutionListeners != null) {
314+
if (this.taskExecutionListeners != null) {
304315
try {
305316
for (TaskExecutionListener taskExecutionListener : this.taskExecutionListeners) {
306317
taskExecutionListener.onTaskFailed(listenerTaskExecution, throwable);
@@ -374,4 +385,13 @@ public int getPhase() {
374385
public void destroy() throws Exception {
375386
this.doTaskEnd();
376387
}
388+
389+
private void getTaskExecutionListeners() {
390+
this.taskExecutionListeners = new ArrayList<>();
391+
this.taskListenerExecutorObjectProvider.getObject();
392+
if(this.taskExecutionListenersFromContext != null) {
393+
this.taskExecutionListeners.addAll(this.taskExecutionListenersFromContext);
394+
}
395+
this.taskExecutionListeners.add(this.taskListenerExecutorObjectProvider.getObject());
396+
}
377397
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
/*
2-
* Copyright 2016 the original author or authors.
2+
* Copyright 2018 the original author or authors.
33
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
1515
*/
1616

1717
package org.springframework.cloud.task.listener.annotation;
@@ -30,18 +30,22 @@
3030
import org.springframework.aop.framework.autoproxy.AutoProxyUtils;
3131
import org.springframework.aop.scope.ScopedObject;
3232
import org.springframework.aop.scope.ScopedProxyUtils;
33+
import org.springframework.beans.BeansException;
3334
import org.springframework.beans.factory.BeanInitializationException;
34-
import org.springframework.beans.factory.FactoryBean;
35+
import org.springframework.beans.factory.ObjectProvider;
3536
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
3637
import org.springframework.cloud.task.listener.TaskExecutionListener;
3738
import org.springframework.context.ConfigurableApplicationContext;
3839
import org.springframework.core.MethodIntrospector;
3940
import org.springframework.core.annotation.AnnotationUtils;
4041

4142
/**
43+
* Initializes TaskListenerExecutor for a task.
44+
*
4245
* @author Glenn Renfro
46+
* @since 2.1.0
4347
*/
44-
public class TaskListenerExecutorFactoryBean implements FactoryBean<TaskExecutionListener> {
48+
public class TaskListenerExecutorObjectProvider implements ObjectProvider<TaskExecutionListener> {
4549

4650
private final static Log logger = LogFactory.getLog(TaskListenerExecutor.class);
4751

@@ -56,29 +60,19 @@ public class TaskListenerExecutorFactoryBean implements FactoryBean<TaskExecutio
5660

5761
private Map<Method, Object> failedTaskInstances;
5862

59-
public TaskListenerExecutorFactoryBean(ConfigurableApplicationContext context){
63+
public TaskListenerExecutorObjectProvider(ConfigurableApplicationContext context){
6064
this.context = context;
6165
}
6266

6367
@Override
64-
public TaskListenerExecutor getObject() throws Exception {
68+
public TaskListenerExecutor getObject() {
6569
beforeTaskInstances = new HashMap<>();
6670
afterTaskInstances = new HashMap<>();
6771
failedTaskInstances = new HashMap<>();
6872
initializeExecutor();
6973
return new TaskListenerExecutor(beforeTaskInstances, afterTaskInstances, failedTaskInstances);
7074
}
7175

72-
@Override
73-
public Class<?> getObjectType() {
74-
return TaskListenerExecutor.class;
75-
}
76-
77-
@Override
78-
public boolean isSingleton() {
79-
return false;
80-
}
81-
8276
private void initializeExecutor( ) {
8377
ConfigurableListableBeanFactory factory = context.getBeanFactory();
8478
for( String beanName : context.getBeanDefinitionNames()) {
@@ -151,6 +145,21 @@ private void processBean(String beanName, final Class<?> type){
151145
}
152146
}
153147

148+
@Override
149+
public TaskExecutionListener getObject(Object... args) throws BeansException {
150+
throw new UnsupportedOperationException("the getObject(Object... args) method is not supported.");
151+
}
152+
153+
@Override
154+
public TaskExecutionListener getIfAvailable() throws BeansException {
155+
throw new UnsupportedOperationException("the getIfAvailable() method is not supported.");
156+
}
157+
158+
@Override
159+
public TaskExecutionListener getIfUnique() throws BeansException {
160+
throw new UnsupportedOperationException("the getIfUnique() method is not supported.");
161+
}
162+
154163
private static class MethodGetter<T extends Annotation> {
155164
public Map<Method, T> getMethods(final Class<?> type, final Class<T> annotationClass){
156165
return MethodIntrospector.selectMethods(type,

spring-cloud-task-core/src/test/java/org/springframework/cloud/task/listener/TaskExecutionListenerTests.java

+1-26
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016 the original author or authors.
2+
* Copyright 2016-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,11 +29,9 @@
2929
import org.springframework.cloud.task.listener.annotation.AfterTask;
3030
import org.springframework.cloud.task.listener.annotation.BeforeTask;
3131
import org.springframework.cloud.task.listener.annotation.FailedTask;
32-
import org.springframework.cloud.task.listener.annotation.TaskListenerExecutorFactoryBean;
3332
import org.springframework.cloud.task.repository.TaskExecution;
3433
import org.springframework.cloud.task.util.TestDefaultConfiguration;
3534
import org.springframework.cloud.task.util.TestListener;
36-
import org.springframework.context.ConfigurableApplicationContext;
3735
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
3836
import org.springframework.context.annotation.Bean;
3937
import org.springframework.context.annotation.Configuration;
@@ -290,12 +288,6 @@ public AnnotatedTaskListener annotatedTaskListener() {
290288
return new AnnotatedTaskListener();
291289
}
292290

293-
@Bean
294-
public TaskListenerExecutorFactoryBean taskListenerExecutor(ConfigurableApplicationContext context) throws Exception
295-
{
296-
return new TaskListenerExecutorFactoryBean(context);
297-
}
298-
299291
public static class AnnotatedTaskListener extends TestListener {
300292

301293
@BeforeTask
@@ -331,12 +323,6 @@ public AnnotatedTaskListener annotatedTaskListener() {
331323
return new AnnotatedTaskListener();
332324
}
333325

334-
@Bean
335-
public TaskListenerExecutorFactoryBean taskListenerExecutor(ConfigurableApplicationContext context) throws Exception
336-
{
337-
return new TaskListenerExecutorFactoryBean(context);
338-
}
339-
340326
public static class AnnotatedTaskListener {
341327

342328
@BeforeTask
@@ -365,11 +351,6 @@ public AnnotatedTaskListener annotatedTaskListener() {
365351
return new AnnotatedTaskListener();
366352
}
367353

368-
@Bean
369-
public TaskListenerExecutorFactoryBean taskListenerExecutor(ConfigurableApplicationContext context) throws Exception
370-
{
371-
return new TaskListenerExecutorFactoryBean(context);
372-
}
373354

374355
public static class AnnotatedTaskListener {
375356

@@ -400,12 +381,6 @@ public AnnotatedTaskListener annotatedTaskListener() {
400381
return new AnnotatedTaskListener();
401382
}
402383

403-
@Bean
404-
public TaskListenerExecutorFactoryBean taskListenerExecutor(ConfigurableApplicationContext context) throws Exception
405-
{
406-
return new TaskListenerExecutorFactoryBean(context);
407-
}
408-
409384
public static class AnnotatedTaskListener extends TestListener{
410385

411386
@BeforeTask

0 commit comments

Comments
 (0)