package org.springframework.cloud.service.document;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.UnknownHostException;

import org.springframework.cloud.service.AbstractServiceConnectorCreator;
import org.springframework.cloud.service.ServiceConnectorConfig;
import org.springframework.cloud.service.ServiceConnectorCreationException;
import org.springframework.cloud.service.common.MongoServiceInfo;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoClientOptions.Builder;
import com.mongodb.MongoClientURI;
import com.mongodb.MongoException;
import com.mongodb.WriteConcern;

/**
 * Simplified access to creating MongoDB service objects.
 *
 * @author Ramnivas Laddad
 * @author Thomas Risberg
 * @author Chris Schaefer
 */
public class MongoDbFactoryCreator extends AbstractServiceConnectorCreator<MongoDbFactory, MongoServiceInfo> {
	@Override
	public MongoDbFactory create(MongoServiceInfo serviceInfo, ServiceConnectorConfig config) {
		try {
			MongoClientOptions.Builder mongoOptionsToUse = getMongoOptions((MongoDbFactoryConfig) config);

			SimpleMongoDbFactory mongoDbFactory = createMongoDbFactory(serviceInfo, mongoOptionsToUse);

			return configure(mongoDbFactory, (MongoDbFactoryConfig) config);
		} catch (UnknownHostException e) {
			throw new ServiceConnectorCreationException(e);
		} catch (MongoException e) {
			throw new ServiceConnectorCreationException(e);
		}
	}

	private SimpleMongoDbFactory createMongoDbFactory(MongoServiceInfo serviceInfo, MongoClientOptions.Builder mongoOptionsToUse) throws UnknownHostException {
		MongoClientURI mongoClientURI = new MongoClientURI(serviceInfo.getUri(), mongoOptionsToUse);
		MongoClient mongo = new MongoClient(mongoClientURI);
		return new SimpleMongoDbFactory(mongo, mongoClientURI.getDatabase());
	}

	private MongoClientOptions.Builder getMongoOptions(MongoDbFactoryConfig config) {
		MongoClientOptions.Builder builder;
		
		Method builderMethod = ClassUtils.getMethodIfAvailable(MongoClientOptions.class, "builder");
		if (builderMethod != null) {
			builder = (Builder) ReflectionUtils.invokeMethod(builderMethod, null);
		} else {
			Constructor<Builder> builderConstructor = ClassUtils.getConstructorIfAvailable(MongoClientOptions.Builder.class);
			try {
				builder = builderConstructor.newInstance(new Object[0]);
			} catch (InstantiationException e) {
				throw new IllegalStateException(e);
			} catch (IllegalAccessException e) {
				throw new IllegalStateException(e);
			} catch (IllegalArgumentException e) {
				throw new IllegalStateException(e);
			} catch (InvocationTargetException e) {
				throw new IllegalStateException(e);
			}
		}

		if (config != null) {
			if (config.getConnectionsPerHost() != null) {
				builder.connectionsPerHost(config.getConnectionsPerHost());
			}
			if (config.getMaxWaitTime() != null) {
				builder.maxWaitTime(config.getMaxWaitTime());
			}
			if (config.getWriteConcern() != null) {
				builder.writeConcern(new WriteConcern(config.getWriteConcern()));
			}
		}

		return builder;
	}

	public SimpleMongoDbFactory configure(SimpleMongoDbFactory mongoDbFactory, MongoDbFactoryConfig config) {
		if (config != null && config.getWriteConcern() != null) {
			WriteConcern writeConcern = WriteConcern.valueOf(config.getWriteConcern());
			if (writeConcern != null) {
				mongoDbFactory.setWriteConcern(writeConcern);
			}
		}
		return mongoDbFactory;
	}
}

@@ -50,131 +50,8 @@
 t;%0A%0A
-import java.lang.reflect.Constructor;%0Aimport java.lang.reflect.InvocationTargetException;%0Aimport java.lang.reflect.Method;%0A
 impo
@@ -84,16 +84,16 @@
 eption;%0A
+
 %0Aimport 
@@ -492,101 +492,8 @@
 ory;
-%0Aimport org.springframework.util.ClassUtils;%0Aimport org.springframework.util.ReflectionUtils;
 %0A%0Aim
@@ -565,55 +565,8 @@
 ns;%0A
-import com.mongodb.MongoClientOptions.Builder;%0A
 impo
@@ -809,16 +809,39 @@
 chaefer%0A
+ * @author Mark Paluch%0A
  */%0Apubl
@@ -2006,744 +2006,40 @@
 lder
-;%0A%09%09%0A%09%09Method builderMethod = ClassUtils.getMethodIfAvailable(MongoClientOptions.class, %22builder%22);%0A%09%09if (builderMethod != null) %7B%0A%09%09%09builder = (Builder) ReflectionUtils.invokeMethod(builderMethod, null);%0A%09%09%7D else %7B%0A%09%09%09Constructor%3CBuilder%3E builderConstructor = ClassUtils.getConstructorIfAvailable(MongoClientOptions.Builder.class);%0A%09%09%09try %7B%0A%09%09%09%09builder = builderConstructor.newInstance(new Object%5B0%5D);%0A%09%09%09%7D catch (InstantiationException e) %7B%0A%09%09%09%09throw new IllegalStateException(e);%0A%09%09%09%7D catch (IllegalAccessException e) %7B%0A%09%09%09%09throw new IllegalStateException(e);%0A%09%09%09%7D catch (IllegalArgumentException e) %7B%0A%09%09%09%09throw new IllegalStateException(e);%0A%09%09%09%7D catch (InvocationTargetException e) %7B%0A%09%09%09%09throw new IllegalStateException(e);%0A%09%09%09%7D%0A%09%09%7D
+ = MongoClientOptions.builder();
 %0A%0A%09%09

Simplify MongoDB configuration.