Skip to content

Commit

Permalink
OGM-810 Allowing to set MongoDB driver options under the prefix 'hibe…
Browse files Browse the repository at this point in the history
…rnate.ogm.mongodb.driver'

- Updating DatastoreInitializationTest to make use of 'hibernate.ogm.mongodb.driver.serverSelectionTimeout' which is the determining timeout value in the Mongo 3 driver
- Adding new test MongoDBConfigurationTest
- Doc update
  • Loading branch information
hferentschik authored and gunnarmorling committed Aug 21, 2015
1 parent 864d9c6 commit ce3c42a
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 158 deletions.
Expand Up @@ -87,11 +87,10 @@ This property has no default value.
This property is ignored if the username isn't specified.
hibernate.ogm.error_handler::
The fully-qualified class name, class object or an instance of `ErrorHandler` to get notified upon errors during flushes (see <<ogm-api-error-handler>>)
hibernate.ogm.mongodb.connection_timeout::
Defines the timeout used by the driver
when the connection to the MongoDB instance is initiated.
This configuration is expressed in milliseconds.
The default value is `5000`.
hibernate.ogm.mongodb.driver.*::
Defines a prefix for all options which should be passed through to the MongoDB driver.
For available options refer to the JavaDocs of link:http://api.mongodb.org/java/3.0/com/mongodb/MongoClientOptions.Builder.html[MongoClientOptions.Builder]. All `String`, `int` and `boolean` properties
can be set, eg `hibernate.ogm.mongodb.driver.serverSelectionTimeout`.
hibernate.ogm.mongodb.authentication_mechanism::
Define the authentication mechanism to use. Possible values are:

Expand Down Expand Up @@ -3113,7 +3112,7 @@ when using the JP-QL query support. This includes:
* `ORDER BY`
* inner `JOIN` on embedded collections
* projections of regular and embedded properties

Queries using these constructs will be transformed into equivalent native MongoDB queries.

[NOTE]
Expand Down
Expand Up @@ -55,11 +55,6 @@ public final class MongoDBProperties implements DocumentStoreProperties {
*/
public static final String READ_PREFERENCE = "hibernate.ogm.mongodb.read_preference";

/**
* The timeout used at the connection to the MongoDB instance. This value is set in milliseconds. Defaults to 5000.
*/
public static final String TIMEOUT = "hibernate.ogm.mongodb.connection_timeout";

/**
* Configuration property for specifying how to store association documents. Only applicable if
* {@link DocumentStoreProperties#ASSOCIATIONS_STORE} is set to {@link AssociationStorageType#ASSOCIATION_DOCUMENT}.
Expand All @@ -78,6 +73,13 @@ public final class MongoDBProperties implements DocumentStoreProperties {
*/
public static final String AUTHENTICATION_MECHANISM = "hibernate.ogm.mongodb.authentication_mechanism";

/**
* Property prefix for MongoDB driver settings which needs to be passed on to the driver. Refer to
* the options of {@link com.mongodb.MongoClientOptions.Builder} for a list of available properties.
* All string, int and boolean builder methods can be configured, eg {@code hibernate.ogm.mongodb.driver.maxWaitTime = 1000}.
*/
public static final String MONGO_DRIVER_SETTINGS_PREFIX = "hibernate.ogm.mongodb.driver";

private MongoDBProperties() {
}
}
Expand Up @@ -6,10 +6,18 @@
*/
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 com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;

import org.hibernate.HibernateException;
import org.hibernate.ogm.cfg.spi.DocumentStoreConfiguration;
import org.hibernate.ogm.datastore.mongodb.MongoDBProperties;
import org.hibernate.ogm.datastore.mongodb.impl.MongoDBDatastoreProvider;
Expand All @@ -20,41 +28,25 @@
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 org.hibernate.ogm.util.configurationreader.spi.PropertyValidator;

import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;

/**
* Configuration for {@link MongoDBDatastoreProvider}.
*
* @author Guillaume Scheibel &lt;guillaume.scheibel@gmail.com&gt;
* @author Gunnar Morling
* @author Hardy Ferentschik
*/
public class MongoDBConfiguration extends DocumentStoreConfiguration {

public static final String DEFAULT_ASSOCIATION_STORE = "Associations";

/**
* The default value used to set the timeout during the connection to the MongoDB instance This value is set in
* milliseconds.
*
* @see MongoDBProperties#TIMEOUT
*/
private static final int DEFAULT_TIMEOUT = 5000;

private static final int DEFAULT_PORT = 27017;

private static final Log log = LoggerFactory.getLogger();

private static final TimeoutValidator TIMEOUT_VALIDATOR = new TimeoutValidator();

private final int timeout;
private final WriteConcern writeConcern;
private final ReadPreference readPreference;
private final AuthenticationMechanismType authenticationMechanism;
private final ConfigurationPropertyReader propertyReader;

/**
* Creates a new {@link MongoDBConfiguration}.
Expand All @@ -65,10 +57,7 @@ public class MongoDBConfiguration extends DocumentStoreConfiguration {
public MongoDBConfiguration(ConfigurationPropertyReader propertyReader, OptionsContext globalOptions) {
super( propertyReader, DEFAULT_PORT );

this.timeout = propertyReader.property( MongoDBProperties.TIMEOUT, int.class )
.withDefault( DEFAULT_TIMEOUT )
.withValidator( TIMEOUT_VALIDATOR )
.getValue();
this.propertyReader = propertyReader;
this.authenticationMechanism = propertyReader.property( MongoDBProperties.AUTHENTICATION_MECHANISM, AuthenticationMechanismType.class )
.withDefault( AuthenticationMechanismType.BEST )
.getValue();
Expand All @@ -82,14 +71,64 @@ public MongoDBConfiguration(ConfigurationPropertyReader propertyReader, OptionsC
* @return the {@link MongoClientOptions} corresponding to the {@link MongoDBConfiguration}
*/
public MongoClientOptions buildOptions() {
MongoClientOptions.Builder optionsBuilder = new MongoClientOptions.Builder();
MongoClientOptions.Builder optionsBuilder = MongoClientOptions.builder();

optionsBuilder.connectTimeout( timeout );
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;
}

public List<MongoCredential> buildCredentials() {
if ( getUsername() != null ) {
return Collections.singletonList(
Expand All @@ -102,14 +141,4 @@ public List<MongoCredential> buildCredentials() {
}
return null;
}

private static class TimeoutValidator implements PropertyValidator<Integer> {

@Override
public void validate(Integer value) throws HibernateException {
if ( value < 0 ) {
throw log.mongoDBTimeOutIllegalValue( value );
}
}
}
}
Expand Up @@ -6,11 +6,17 @@
*/
package org.hibernate.ogm.datastore.mongodb.impl;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.MongoException;
import com.mongodb.ServerAddress;

import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.ogm.cfg.spi.Hosts;
import org.hibernate.ogm.datastore.mongodb.MongoDBDialect;
Expand All @@ -30,13 +36,6 @@
import org.hibernate.service.spi.Startable;
import org.hibernate.service.spi.Stoppable;

import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.MongoException;
import com.mongodb.ServerAddress;

/**
* Provides access to a MongoDB instance
*
Expand All @@ -45,8 +44,6 @@
*/
public class MongoDBDatastoreProvider extends BaseDatastoreProvider implements Startable, Stoppable, Configurable, ServiceRegistryAwareService {

private static final int AUTHENTICATION_FAILED_CODE = 18;

private static final Log log = LoggerFactory.getLogger();

private ServiceRegistryImplementor serviceRegistry;
Expand Down Expand Up @@ -136,9 +133,6 @@ protected MongoClient createMongoClient(MongoDBConfiguration config) {
? new MongoClient( serverAddresses, clientOptions )
: new MongoClient( serverAddresses, credentials, clientOptions );
}
catch (UnknownHostException e) {
throw log.mongoOnUnknownHost( config.toString() );
}
catch (RuntimeException e) {
throw log.unableToInitializeMongoDB( e );
}
Expand Down Expand Up @@ -185,12 +179,10 @@ private DB extractDatabase(MongoClient mongo, MongoDBConfiguration config) {
return mongo.getDB( databaseName );
}
catch (MongoException me) {
switch ( me.getCode() ) {
case AUTHENTICATION_FAILED_CODE:
throw log.authenticationFailed( config.getUsername() );
default:
throw log.unableToConnectToDatastore( config.getHosts().toString(), me );
}
// The Mongo driver allows not to determine the cause of the error, eg failing authentication, anymore
// by error code. At best the message contains some more information.
// See also http://stackoverflow.com/questions/30455152/check-mongodb-authentication-with-java-3-0-driver
throw log.unableToConnectToDatastore( me.getMessage(), me );
}
}
}
Expand Up @@ -13,7 +13,6 @@
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.ogm.cfg.OgmProperties;
import org.hibernate.ogm.datastore.mongodb.MongoDBProperties;
import org.jboss.logging.annotations.Cause;
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
Expand All @@ -38,9 +37,6 @@ public interface Log extends org.hibernate.ogm.util.impl.Log {
@Message(id = 1203, value = "Unable to find or initialize a connection to the MongoDB server")
HibernateException unableToInitializeMongoDB(@Cause RuntimeException e);

@Message(id = 1205, value = "Could not resolve MongoDB hostname [%s]")
HibernateException mongoOnUnknownHost(String hostname);

@LogMessage(level = INFO)
@Message(id = 1206, value = "Mongo database named [%s] is not defined. Creating it!")
void creatingDatabase(String dbName);
Expand All @@ -56,18 +52,8 @@ public interface Log extends org.hibernate.ogm.util.impl.Log {
@Message(id = 1210, value = "Removed [%d] associations")
void removedAssociation(int nAffected);

@LogMessage(level = INFO)
@Message(id = 1211, value = "Using write concern { w : %s, wtimeout : %s, fsync : %s, journaled : %s, continueOnErrorForInsert : %s } if not explicitly configured otherwise for specific entities")
void usingWriteConcern(String writeStrategy, int wtimeout, boolean fsync, boolean journaled, boolean continueOnErrorForInsert);

@Message(id = 1213, value = "MongoDB authentication failed with username [%s]" )
HibernateException authenticationFailed(String username);

@Message(id = 1214, value = "Unable to connect to MongoDB instance %1$s" )
HibernateException unableToConnectToDatastore(String host, @Cause Exception e);

@Message(id = 1215, value = "The value set for the configuration property" + MongoDBProperties.TIMEOUT + " must be a number greater than 0. Found '[%s]'.")
HibernateException mongoDBTimeOutIllegalValue(int timeout);
@Message(id = 1214, value = "Unable to connect to MongoDB instance: %1$s" )
HibernateException unableToConnectToDatastore(String message, @Cause Exception e);

@Message(id = 1217, value = "The following native query does neither specify the collection name nor is its result type mapped to an entity: %s")
HibernateException unableToDetermineCollectionName(String nativeQuery);
Expand Down Expand Up @@ -106,4 +92,7 @@ public interface Log extends org.hibernate.ogm.util.impl.Log {
@Message(id = 1225, value = "This WriteConcern has been deprecated or removed by MongoDB: %s")
HibernateException writeConcernDeprecated(String writeConcern);

@Message(id = 1226, value = "Unable to use reflection on invoke method '%s#%s' via reflection.")
HibernateException unableToInvokeMethodViaReflection(String clazz, String method);

}
Expand Up @@ -18,18 +18,6 @@
*/
public enum WriteConcernType {


/**
* No exceptions are raised, even for network issues.
*
* This write concern has been deprecated. There is no replacement for this write concern.
* The closest would be to use {@link #UNACKNOWLEDGED}, then catch and ignore any exceptions of type MongoSocketException.
*
* @deprecated This WriteConcern is no longer supported by MongoDB
*/
@Deprecated
ERRORS_IGNORED(null),

/**
* Write operations that use this write concern will wait for acknowledgement from the primary server before
* returning. Exceptions are raised for network issues, and server errors.
Expand Down

0 comments on commit ce3c42a

Please sign in to comment.