/*
* Hibernate OGM, Domain model persistence for NoSQL datastores
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.ogm.datastore.mongodb.configuration.impl;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.ogm.cfg.spi.DocumentStoreConfiguration;
import org.hibernate.ogm.datastore.mongodb.MongoDBProperties;
import org.hibernate.ogm.datastore.mongodb.impl.MongoDBDatastoreProvider;
import org.hibernate.ogm.datastore.mongodb.logging.impl.Log;
import org.hibernate.ogm.datastore.mongodb.logging.impl.LoggerFactory;
import org.hibernate.ogm.datastore.mongodb.options.AuthenticationMechanismType;
import org.hibernate.ogm.datastore.mongodb.options.impl.ReadPreferenceOption;
import org.hibernate.ogm.datastore.mongodb.options.impl.WriteConcernOption;
import org.hibernate.ogm.options.spi.OptionsContext;
import org.hibernate.ogm.util.configurationreader.spi.ConfigurationPropertyReader;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
/**
* Configuration for {@link MongoDBDatastoreProvider}.
*
* @author Guillaume Scheibel <guillaume.scheibel@gmail.com>
* @author Gunnar Morling
* @author Hardy Ferentschik
*/
public class MongoDBConfiguration extends DocumentStoreConfiguration {
public static final String DEFAULT_ASSOCIATION_STORE = "Associations";
public static final String DEFAULT_AUTHENTICATION_DATABASE = "admin";
private static final int DEFAULT_PORT = 27017;
private static final Log log = LoggerFactory.getLogger();
private final WriteConcern writeConcern;
private final ReadPreference readPreference;
private final AuthenticationMechanismType authenticationMechanism;
private final ConfigurationPropertyReader propertyReader;
private final String authenticationDatabaseName;
/**
* Creates a new {@link MongoDBConfiguration}.
*
* @param propertyReader provides access to configuration values given via {@code persistence.xml} etc.
* @param globalOptions global settings given via an option configurator
*/
public MongoDBConfiguration(ConfigurationPropertyReader propertyReader, OptionsContext globalOptions) {
super( propertyReader, DEFAULT_PORT );
this.propertyReader = propertyReader;
this.authenticationMechanism = propertyReader.property( MongoDBProperties.AUTHENTICATION_MECHANISM, AuthenticationMechanismType.class )
.withDefault( AuthenticationMechanismType.BEST )
.getValue();
this.authenticationDatabaseName = propertyReader.property( MongoDBProperties.AUTHENTICATION_DATABASE, String.class )
.withDefault( DEFAULT_AUTHENTICATION_DATABASE )
.getValue();
this.writeConcern = globalOptions.getUnique( WriteConcernOption.class );
this.readPreference = globalOptions.getUnique( ReadPreferenceOption.class );
}
/**
* Create a {@link MongoClientOptions} using the {@link MongoDBConfiguration}.
*
* @return the {@link MongoClientOptions} corresponding to the {@link MongoDBConfiguration}
*/
public MongoClientOptions buildOptions() {
MongoClientOptions.Builder optionsBuilder = MongoClientOptions.builder();
optionsBuilder.writeConcern( writeConcern );
optionsBuilder.readPreference( readPreference );
Map<String, Method> settingsMap = createSettingsMap();
for ( Map.Entry<String, Method> entry : settingsMap.entrySet() ) {
String setting = MongoDBProperties.MONGO_DRIVER_SETTINGS_PREFIX + "." + entry.getKey();
// we know that there is exactly one parameter
Class<?> type = entry.getValue().getParameterTypes()[0];
// for reflection purposes we need to deal with wrapper classes
if ( int.class.equals( type ) ) {
type = Integer.class;
}
if ( boolean.class.equals( type ) ) {
type = Boolean.class;
}
Object property = propertyReader.property( setting, type ).withDefault( null ).getValue();
if ( property == null ) {
continue;
}
Method settingMethod = entry.getValue();
try {
settingMethod.invoke( optionsBuilder, property );
}
catch ( InvocationTargetException | IllegalAccessException e ) {
throw log.unableToInvokeMethodViaReflection(
settingMethod.getDeclaringClass().getName(),
settingMethod.getName()
);
}
}
return optionsBuilder.build();
}
private Map<String, Method> createSettingsMap() {
Map<String, Method> settingsMap = new HashMap<>();
Method[] methods = MongoClientOptions.Builder.class.getDeclaredMethods();
for ( Method method : methods ) {
if ( method.getParameterTypes().length == 1 ) {
Class<?> parameterType = method.getParameterTypes()[0];
// we just care of string, int and boolean setters
if ( String.class.equals( parameterType )
|| int.class.equals( parameterType )
|| boolean.class.equals( parameterType ) ) {
settingsMap.put( method.getName(), method );
}
}
}
return settingsMap;
}
private String getAuthenticationDatabaseName() {
return authenticationDatabaseName;
}
public List<MongoCredential> buildCredentials() {
if ( getUsername() != null ) {
return Collections.singletonList(
authenticationMechanism.createCredential(
getUsername(),
getAuthenticationDatabaseName(),
getPassword()
)
);
}
return null;
}
}