/*
 * Copyright 2002-2020 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
 *
 *      https://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.scheduling.annotation;

import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.function.Supplier;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;

/**
 * Abstract base {@code Configuration} class providing common structure for enabling
 * Spring's asynchronous method execution capability.
 *
 * @author Chris Beams
 * @author Juergen Hoeller
 * @author Stephane Nicoll
 * @since 3.1
 * @see EnableAsync
 */
@Configuration(proxyBeanMethods = false)
public abstract class AbstractAsyncConfiguration implements ImportAware {

	@Nullable
	protected AnnotationAttributes enableAsync;

	@Nullable
	protected Supplier<Executor> executor;

	@Nullable
	protected Supplier<AsyncUncaughtExceptionHandler> exceptionHandler;


	@Override
	public void setImportMetadata(AnnotationMetadata importMetadata) {
		this.enableAsync = AnnotationAttributes.fromMap(
				importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
		if (this.enableAsync == null) {
			throw new IllegalArgumentException(
					"@EnableAsync is not present on importing class " + importMetadata.getClassName());
		}
	}

	/**
	 * Collect any {@link AsyncConfigurer} beans through autowiring.
	 */
	@Autowired(required = false)
	void setConfigurers(Collection<AsyncConfigurer> configurers) {
		if (CollectionUtils.isEmpty(configurers)) {
			return;
		}
		if (configurers.size() > 1) {
			throw new IllegalStateException("Only one AsyncConfigurer may exist");
		}
		AsyncConfigurer configurer = configurers.iterator().next();
		this.executor = configurer::getAsyncExecutor;
		this.exceptionHandler = configurer::getAsyncUncaughtExceptionHandler;
	}

}

@@ -17,17 +17,17 @@
 2002-202
-0
+1
  the ori
@@ -685,26 +685,20 @@
 va.util.
-Collection
+List
 ;%0Aimport
@@ -755,24 +755,96 @@
 unction.
-Supplier
+Function;%0Aimport java.util.function.Supplier;%0Aimport java.util.stream.Collectors
 ;%0A%0Aimpor
@@ -908,24 +908,81 @@
 ionHandler;%0A
+import org.springframework.beans.factory.ObjectProvider;%0A
 import org.s
@@ -1364,16 +1364,76 @@
 onUtils;
+%0Aimport org.springframework.util.function.SingletonSupplier;
 %0A%0A/**%0A *
@@ -2466,91 +2466,242 @@
 ired
-(required = false)%0A%09void setConfigurers(Collection%3CAsyncConfigurer%3E configurers) %7B%0A
+%0A%09void setConfigurers(ObjectProvider%3CAsyncConfigurer%3E configurers) %7B%0A%09%09Supplier%3CAsyncConfigurer%3E asyncConfigurer = SingletonSupplier.of(() -%3E %7B%0A%09%09%09List%3CAsyncConfigurer%3E candidates = configurers.stream().collect(Collectors.toList());%0A%09
 %09%09if
@@ -2727,25 +2727,24 @@
 sEmpty(c
-onfigurer
+andidate
 s)) %7B%0A%09%09
@@ -2748,36 +2748,43 @@
 %0A%09%09%09
+%09
 return
+ null
 ;%0A%09%09
+%09
 %7D%0A
+%09
 %09%09if (c
-onfigurer
+andidate
 s.si
@@ -2791,24 +2791,25 @@
 ze() %3E 1) %7B%0A
+%09
 %09%09%09throw new
@@ -2876,91 +2876,93 @@
 ;%0A%09%09
+%09
 %7D%0A%09%09
-AsyncConfigurer configurer = configurers.iterator().next();%0A%09%09this.executor = c
+%09return candidates.get(0);%0A%09%09%7D);%0A%09%09this.executor = adapt(asyncConfigurer, AsyncC
 onfi
@@ -2984,16 +2984,17 @@
 Executor
+)
 ;%0A%09%09this
@@ -3013,17 +3013,45 @@
 ndler = 
-c
+adapt(asyncConfigurer, AsyncC
 onfigure
@@ -3085,16 +3085,282 @@
 nHandler
+);%0A%09%7D%0A%0A%09private %3CT%3E Supplier%3CT%3E adapt(Supplier%3CAsyncConfigurer%3E supplier, Function%3CAsyncConfigurer, T%3E provider) %7B%0A%09%09return () -%3E %7B%0A%09%09%09AsyncConfigurer asyncConfigurer = supplier.get();%0A%09%09%09return (asyncConfigurer != null) ? provider.apply(asyncConfigurer) : null;%0A%09%09%7D
 ;%0A%09%7D%0A%0A%7D%0A

Stop resolving AsyncConfigurer instances eagerly