/*
 * Copyright 2013-2014 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.aws.core.config;

import com.amazonaws.AmazonWebServiceClient;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.client.builder.AwsAsyncClientBuilder;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.client.builder.ExecutorFactory;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.RegionUtils;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.cloud.aws.core.region.RegionProvider;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.concurrent.ExecutorService;

/**
 * {@link org.springframework.beans.factory.FactoryBean} class to create {@link AmazonWebServiceClient} instances. This class
 * is responsible to create the respective AmazonWebServiceClient classes because the configuration through Springs's
 * BeanFactory fails due to invalid properties inside the Webservice client classes (see
 * https://github.com/aws/aws-sdk-java/issues/325)
 *
 * @author Agim Emruli
 */
public class AmazonWebserviceClientFactoryBean<T extends AmazonWebServiceClient> extends AbstractFactoryBean<T> {

	private final Class<? extends AmazonWebServiceClient> clientClass;
	private final AWSCredentialsProvider credentialsProvider;
	private RegionProvider regionProvider;
	private Region customRegion;
	private ExecutorService executor;

	public AmazonWebserviceClientFactoryBean(Class<T> clientClass,
											 AWSCredentialsProvider credentialsProvider) {
		this.clientClass = clientClass;
		this.credentialsProvider = credentialsProvider;
	}

	public AmazonWebserviceClientFactoryBean(Class<T> clientClass, AWSCredentialsProvider credentialsProvider, RegionProvider regionProvider) {
		this(clientClass, credentialsProvider);
		setRegionProvider(regionProvider);
	}

	@Override
	public Class<?> getObjectType() {
		return this.clientClass;
	}

	@SuppressWarnings("unchecked")
	@Override
	protected T createInstance() throws Exception {

		String builderName = this.clientClass.getName() + "Builder";
		Class<?> className = ClassUtils.resolveClassName(builderName, ClassUtils.getDefaultClassLoader());

		Method method = ClassUtils.getStaticMethod(className, "standard");
		Assert.notNull(method, "Could not find standard() method in class:'" + className.getName() + "'");

		AwsClientBuilder<?, T> builder = (AwsClientBuilder<?, T>) ReflectionUtils.invokeMethod(method, null);

		if (this.executor != null) {
			AwsAsyncClientBuilder<?, T> asyncBuilder = (AwsAsyncClientBuilder<?, T>) builder;
			asyncBuilder.withExecutorFactory(new ExecutorFactory() {

				@Override
				public ExecutorService newExecutor() {
					return AmazonWebserviceClientFactoryBean.this.executor;
				}
			});
		}

		if (this.credentialsProvider != null) {
			builder.withCredentials(this.credentialsProvider);
		}

		if (this.customRegion != null) {
			builder.withRegion(this.customRegion.getName());
		} else if (this.regionProvider != null) {
			builder.withRegion(this.regionProvider.getRegion().getName());
		}
		return builder.build();
	}

	public void setRegionProvider(RegionProvider regionProvider) {
		this.regionProvider = regionProvider;
	}

	public void setCustomRegion(String customRegionName) {
		this.customRegion = RegionUtils.getRegion(customRegionName);
	}

	public void setExecutor(ExecutorService executor) {
		this.executor = executor;
	}

	@Override
	protected void destroyInstance(T instance) throws Exception {
		instance.shutdown();
	}
}

@@ -1003,24 +1003,62 @@
 egionUtils;%0A
+import com.amazonaws.regions.Regions;%0A
 import org.s
@@ -3814,16 +3814,74 @@
 ame());%0A
+%09%09%7D else %7B%0A%09%09%09builder.withRegion(Regions.DEFAULT_REGION);%0A
 %09%09%7D%0A%09%09re

Use default region if none is configured