Coverage Report - com.allanbank.mongodb.MongoClientConfiguration
 
Classes in this File Line Coverage Branch Coverage Complexity
MongoClientConfiguration
90%
334/369
91%
75/82
1.985
 
 1  
 /*
 2  
  * #%L
 3  
  * MongoClientConfiguration.java - mongodb-async-driver - Allanbank Consulting, Inc.
 4  
  * %%
 5  
  * Copyright (C) 2011 - 2014 Allanbank Consulting, Inc.
 6  
  * %%
 7  
  * Licensed under the Apache License, Version 2.0 (the "License");
 8  
  * you may not use this file except in compliance with the License.
 9  
  * You may obtain a copy of the License at
 10  
  * 
 11  
  *      http://www.apache.org/licenses/LICENSE-2.0
 12  
  * 
 13  
  * Unless required by applicable law or agreed to in writing, software
 14  
  * distributed under the License is distributed on an "AS IS" BASIS,
 15  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  
  * See the License for the specific language governing permissions and
 17  
  * limitations under the License.
 18  
  * #L%
 19  
  */
 20  
 package com.allanbank.mongodb;
 21  
 
 22  
 import java.beans.BeanInfo;
 23  
 import java.beans.IntrospectionException;
 24  
 import java.beans.Introspector;
 25  
 import java.beans.PropertyChangeListener;
 26  
 import java.beans.PropertyChangeSupport;
 27  
 import java.beans.PropertyDescriptor;
 28  
 import java.beans.PropertyEditor;
 29  
 import java.beans.PropertyEditorManager;
 30  
 import java.io.IOException;
 31  
 import java.io.Serializable;
 32  
 import java.lang.reflect.InvocationTargetException;
 33  
 import java.lang.reflect.Method;
 34  
 import java.net.InetSocketAddress;
 35  
 import java.net.Socket;
 36  
 import java.nio.charset.Charset;
 37  
 import java.security.MessageDigest;
 38  
 import java.security.NoSuchAlgorithmException;
 39  
 import java.util.ArrayList;
 40  
 import java.util.Collection;
 41  
 import java.util.Collections;
 42  
 import java.util.HashMap;
 43  
 import java.util.Iterator;
 44  
 import java.util.List;
 45  
 import java.util.Locale;
 46  
 import java.util.Map;
 47  
 import java.util.concurrent.ConcurrentHashMap;
 48  
 import java.util.concurrent.Executor;
 49  
 import java.util.concurrent.Executors;
 50  
 import java.util.concurrent.ThreadFactory;
 51  
 import java.util.concurrent.TimeUnit;
 52  
 
 53  
 import javax.net.SocketFactory;
 54  
 import javax.net.ssl.SSLSocketFactory;
 55  
 
 56  
 import com.allanbank.mongodb.bson.io.StringEncoderCache;
 57  
 import com.allanbank.mongodb.error.MongoDbAuthenticationException;
 58  
 import com.allanbank.mongodb.util.IOUtils;
 59  
 import com.allanbank.mongodb.util.ServerNameUtils;
 60  
 import com.allanbank.mongodb.util.log.Log;
 61  
 import com.allanbank.mongodb.util.log.LogFactory;
 62  
 
 63  
 /**
 64  
  * Contains the configuration for the connection(s) to the MongoDB servers.
 65  
  * 
 66  
  * @api.yes This class is part of the driver's API. Public and protected members
 67  
  *          will be deprecated for at least 1 non-bugfix release (version
 68  
  *          numbers are <major>.<minor>.<bugfix>) before being
 69  
  *          removed or modified.
 70  
  * @copyright 2011-2014, Allanbank Consulting, Inc., All Rights Reserved
 71  
  */
 72  0
 public class MongoClientConfiguration implements Cloneable, Serializable {
 73  
 
 74  
     /** The name of the administration database. */
 75  
     public static final String ADMIN_DB_NAME = "admin";
 76  
 
 77  
     /** The default database. */
 78  
     public static final String DEFAULT_DB_NAME = "local";
 79  
 
 80  
     /** The ASCII character encoding. */
 81  1
     public static final Charset UTF8 = Charset.forName("UTF-8");
 82  
 
 83  
     /**
 84  
      * The default maximum number of strings to keep in the string encoder and
 85  
      * decoder cache.
 86  
      */
 87  
     protected static final int DEFAULT_MAX_STRING_CACHE_ENTRIES = 1024;
 88  
 
 89  
     /** The default maximum length byte array / string to cache. */
 90  
     protected static final int DEFAULT_MAX_STRING_CACHE_LENGTH = StringEncoderCache.DEFAULT_MAX_CACHE_LENGTH;
 91  
 
 92  
     /** The logger for the {@link MongoClientConfiguration}. */
 93  1
     private static final Log LOG = LogFactory
 94  
             .getLog(MongoClientConfiguration.class);
 95  
 
 96  
     /** The serialization version for the class. */
 97  
     private static final long serialVersionUID = 2964127883934086500L;
 98  
 
 99  
     /**
 100  
      * Determines if additional servers are auto discovered or if connections
 101  
      * are limited to the ones manually configured.
 102  
      * <p>
 103  
      * Defaults to true, e.g., auto-discover.
 104  
      * </p>
 105  
      */
 106  836
     private boolean myAutoDiscoverServers = true;
 107  
 
 108  
     /**
 109  
      * Determines the model the driver uses for managing connections.
 110  
      * <p>
 111  
      * Defaults to {@link ConnectionModel#RECEIVER_THREAD}.
 112  
      * </p>
 113  
      */
 114  836
     private ConnectionModel myConnectionModel = ConnectionModel.RECEIVER_THREAD;
 115  
 
 116  
     /**
 117  
      * Determines how long to wait (in milliseconds) for a socket connection to
 118  
      * complete.
 119  
      * <p>
 120  
      * Defaults to 0 or forever.
 121  
      * </p>
 122  
      */
 123  836
     private int myConnectTimeout = 0;
 124  
 
 125  
     /**
 126  
      * The credentials for the user. This should be final but for support for
 127  
      * the clone() method.
 128  
      */
 129  
     private ConcurrentHashMap<String, Credential> myCredentials;
 130  
 
 131  
     /**
 132  
      * The default database for the connection. This is used as the database to
 133  
      * authenticate against if the user is not an administrative user.
 134  
      * <p>
 135  
      * Defaults to {@value #DEFAULT_DB_NAME}.
 136  
      * </p>
 137  
      */
 138  836
     private String myDefaultDatabase = DEFAULT_DB_NAME;
 139  
 
 140  
     /**
 141  
      * The default durability for write operations on the server.
 142  
      * <p>
 143  
      * Defaults to {@link Durability#ACK}.
 144  
      * </p>
 145  
      */
 146  836
     private Durability myDefaultDurability = Durability.ACK;
 147  
 
 148  
     /**
 149  
      * The default read preference for a query.
 150  
      * <p>
 151  
      * Defaults to {@link ReadPreference#PRIMARY}.
 152  
      * </p>
 153  
      */
 154  836
     private ReadPreference myDefaultReadPreference = ReadPreference.PRIMARY;
 155  
 
 156  
     /** The executor for responses from the database. */
 157  836
     private transient Executor myExecutor = null;
 158  
 
 159  
     /**
 160  
      * The legacy credentials created via {@link #authenticate(String, String)}
 161  
      * and {@link #setDefaultDatabase(String)}.
 162  
      */
 163  
     private Credential myLegacyCredential;
 164  
 
 165  
     /**
 166  
      * Determines the type of hand off lock to use between threads in the core
 167  
      * of the driver.
 168  
      * <p>
 169  
      * Defaults to {@link LockType#MUTEX}.
 170  
      * </p>
 171  
      */
 172  836
     private LockType myLockType = LockType.MUTEX;
 173  
 
 174  
     /**
 175  
      * The maximum number of strings that may have their encoded form cached.
 176  
      * <p>
 177  
      * Defaults to {@value #DEFAULT_MAX_STRING_CACHE_ENTRIES}.
 178  
      * </p>
 179  
      */
 180  836
     private int myMaxCachedStringEntries = DEFAULT_MAX_STRING_CACHE_ENTRIES;
 181  
 
 182  
     /**
 183  
      * The maximum length for a string that the stream is allowed to cache.This
 184  
      * can be used to stop a single long string from pushing useful values out
 185  
      * of the cache. Setting this value to zero turns off the caching.
 186  
      * <p>
 187  
      * Defaults to {@value #DEFAULT_MAX_STRING_CACHE_LENGTH}.
 188  
      * </p>
 189  
      */
 190  836
     private int myMaxCachedStringLength = DEFAULT_MAX_STRING_CACHE_LENGTH;
 191  
 
 192  
     /**
 193  
      * Determines the maximum number of connections to use.
 194  
      * <p>
 195  
      * Defaults to 3.
 196  
      * </p>
 197  
      * <p>
 198  
      * <em>Note:</em> In the case of connecting to a replica set this setting
 199  
      * limits the number of connections to the primary server. The driver will
 200  
      * create single connections to the secondary servers if queries are issued
 201  
      * with a {@link ReadPreference} other than {@link ReadPreference#PRIMARY}.
 202  
      * </p>
 203  
      */
 204  836
     private int myMaxConnectionCount = 3;
 205  
 
 206  
     /**
 207  
      * Determines the number of read timeouts (a tick) before closing the
 208  
      * connection.
 209  
      * <p>
 210  
      * Defaults to {@link Integer#MAX_VALUE}.
 211  
      * </p>
 212  
      */
 213  836
     private int myMaxIdleTickCount = Integer.MAX_VALUE;
 214  
 
 215  
     /**
 216  
      * Determines the maximum number of pending operations to allow per
 217  
      * connection. The higher the value the more "asynchronous" the driver can
 218  
      * be but risks more operations being in an unknown state on a connection
 219  
      * error. When the connection has this many pending requests additional
 220  
      * requests will block.
 221  
      * <p>
 222  
      * Defaults to 1024.
 223  
      * </p>
 224  
      * <p>
 225  
      * <em>Note:</em> In the case of an connection error it is impossible to
 226  
      * determine which pending operations completed and which did not.
 227  
      * </p>
 228  
      */
 229  836
     private int myMaxPendingOperationsPerConnection = 1024;
 230  
 
 231  
     /**
 232  
      * Determines the maximum number of milliseconds that a secondary can be
 233  
      * behind the primary before they will be excluded from being used for
 234  
      * queries on secondaries.
 235  
      * <p>
 236  
      * Defaults to 5 minutes (300,000).
 237  
      * </p>
 238  
      */
 239  836
     private long myMaxSecondaryLag = TimeUnit.MINUTES.toMillis(5);
 240  
 
 241  
     /**
 242  
      * Determines the minimum number of connections to try and keep open.
 243  
      * <p>
 244  
      * Defaults to 0.
 245  
      * </p>
 246  
      */
 247  836
     private int myMinConnectionCount = 0;
 248  
 
 249  
     /**
 250  
      * Support for emitting property change events to listeners. Not final for
 251  
      * clone.
 252  
      */
 253  
     private PropertyChangeSupport myPropSupport;
 254  
 
 255  
     /**
 256  
      * Determines how long to wait (in milliseconds) for a socket read to
 257  
      * complete.
 258  
      * <p>
 259  
      * Defaults to 0 or never.
 260  
      * </p>
 261  
      */
 262  836
     private int myReadTimeout = 0;
 263  
 
 264  
     /**
 265  
      * Determines how long to wait (in milliseconds) for a broken connection to
 266  
      * reconnect.
 267  
      * <p>
 268  
      * Defaults to 0 or forever.
 269  
      * </p>
 270  
      */
 271  836
     private int myReconnectTimeout = 0;
 272  
 
 273  
     /**
 274  
      * The list of servers to initially attempt to connect to. This should be
 275  
      * final but for support for the clone() method.
 276  
      */
 277  836
     private List<InetSocketAddress> myServers = new ArrayList<InetSocketAddress>();
 278  
 
 279  
     /** The socket factory for creating sockets. */
 280  836
     private transient SocketFactory mySocketFactory = null;
 281  
 
 282  
     /** The factory for creating threads to handle connections. */
 283  836
     private transient ThreadFactory myThreadFactory = null;
 284  
 
 285  
     /**
 286  
      * Determines if the {@link java.net.Socket#setKeepAlive(boolean)
 287  
      * SO_KEEPALIVE} socket option is set.
 288  
      * <p>
 289  
      * Defaults to true, e.g., use SO_KEEPALIVE.
 290  
      * </p>
 291  
      */
 292  836
     private boolean myUsingSoKeepalive = true;
 293  
 
 294  
     /**
 295  
      * Creates a new MongoClientConfiguration.
 296  
      */
 297  
     public MongoClientConfiguration() {
 298  836
         super();
 299  
 
 300  836
         myThreadFactory = Executors.defaultThreadFactory();
 301  836
         myCredentials = new ConcurrentHashMap<String, Credential>();
 302  836
         myPropSupport = new PropertyChangeSupport(this);
 303  836
     }
 304  
 
 305  
     /**
 306  
      * Creates a new MongoClientConfiguration.
 307  
      * 
 308  
      * @param servers
 309  
      *            The initial set of servers to connect to.
 310  
      */
 311  
     public MongoClientConfiguration(final InetSocketAddress... servers) {
 312  43
         this();
 313  
 
 314  95
         for (final InetSocketAddress server : servers) {
 315  52
             addServer(server);
 316  
         }
 317  43
     }
 318  
 
 319  
     /**
 320  
      * Creates a new MongoClientConfiguration.
 321  
      * 
 322  
      * @param other
 323  
      *            The configuration to copy.
 324  
      */
 325  
     public MongoClientConfiguration(final MongoClientConfiguration other) {
 326  3
         this();
 327  
 
 328  3
         myAutoDiscoverServers = other.isAutoDiscoverServers();
 329  3
         myConnectionModel = other.getConnectionModel();
 330  3
         myConnectTimeout = other.getConnectTimeout();
 331  3
         myDefaultDatabase = other.getDefaultDatabase();
 332  3
         myDefaultDurability = other.getDefaultDurability();
 333  3
         myDefaultReadPreference = other.getDefaultReadPreference();
 334  3
         myExecutor = other.getExecutor();
 335  3
         myLockType = other.getLockType();
 336  3
         myMaxCachedStringEntries = other.getMaxCachedStringEntries();
 337  3
         myMaxCachedStringLength = other.getMaxCachedStringLength();
 338  3
         myMaxConnectionCount = other.getMaxConnectionCount();
 339  3
         myMaxIdleTickCount = other.getMaxIdleTickCount();
 340  3
         myMaxPendingOperationsPerConnection = other
 341  
                 .getMaxPendingOperationsPerConnection();
 342  3
         myMaxSecondaryLag = other.getMaxSecondaryLag();
 343  3
         myMinConnectionCount = other.getMinConnectionCount();
 344  3
         myReadTimeout = other.getReadTimeout();
 345  3
         myReconnectTimeout = other.getReconnectTimeout();
 346  3
         mySocketFactory = other.getSocketFactory();
 347  3
         myThreadFactory = other.getThreadFactory();
 348  3
         myUsingSoKeepalive = other.isUsingSoKeepalive();
 349  
 
 350  3
         for (final Credential credential : other.getCredentials()) {
 351  1
             addCredential(credential);
 352  1
         }
 353  3
         for (final InetSocketAddress addr : other.getServerAddresses()) {
 354  6
             addServer(addr);
 355  6
         }
 356  3
     }
 357  
 
 358  
     /**
 359  
      * Creates a new {@link MongoClientConfiguration} instance using a MongoDB
 360  
      * style URL to initialize its state. Further configuration is possible once
 361  
      * the {@link MongoClientConfiguration} has been instantiated.
 362  
      * 
 363  
      * @param mongoDbUri
 364  
      *            The configuration for the connection to MongoDB expressed as a
 365  
      *            MongoDB URL.
 366  
      * @throws IllegalArgumentException
 367  
      *             If the <tt>mongoDbUri</tt> is not a properly formated MongoDB
 368  
      *             style URL.
 369  
      * 
 370  
      * @see <a href="http://www.mongodb.org/display/DOCS/Connections"> MongoDB
 371  
      *      Connections</a>
 372  
      */
 373  
     public MongoClientConfiguration(final MongoDbUri mongoDbUri)
 374  
             throws IllegalArgumentException {
 375  29
         this(mongoDbUri, Durability.ACK);
 376  28
     }
 377  
 
 378  
     /**
 379  
      * Creates a new {@link MongoClientConfiguration} instance using a MongoDB
 380  
      * style URL to initialize its state. Further configuration is possible once
 381  
      * the {@link MongoClientConfiguration} has been instantiated.
 382  
      * 
 383  
      * @param mongoDbUri
 384  
      *            The configuration for the connection to MongoDB expressed as a
 385  
      *            MongoDB URL.
 386  
      * @throws IllegalArgumentException
 387  
      *             If the <tt>mongoDbUri</tt> is not a properly formated MongoDB
 388  
      *             style URL.
 389  
      * 
 390  
      * @see <a href="http://www.mongodb.org/display/DOCS/Connections"> MongoDB
 391  
      *      Connections</a>
 392  
      */
 393  
     public MongoClientConfiguration(final String mongoDbUri)
 394  
             throws IllegalArgumentException {
 395  31
         this(new MongoDbUri(mongoDbUri));
 396  24
     }
 397  
 
 398  
     /**
 399  
      * Creates a new {@link MongoClientConfiguration} instance using a MongoDB
 400  
      * style URL to initialize its state. Further configuration is possible once
 401  
      * the {@link MongoClientConfiguration} has been instantiated.
 402  
      * 
 403  
      * @param mongoDbUri
 404  
      *            The configuration for the connection to MongoDB expressed as a
 405  
      *            MongoDB URL.
 406  
      * @param defaultDurability
 407  
      *            The default durability.
 408  
      * @throws IllegalArgumentException
 409  
      *             If the <tt>mongoDbUri</tt> is not a properly formated MongoDB
 410  
      *             style URL.
 411  
      * 
 412  
      * @see <a href="http://www.mongodb.org/display/DOCS/Connections"> MongoDB
 413  
      *      Connections</a>
 414  
      */
 415  
     protected MongoClientConfiguration(final MongoDbUri mongoDbUri,
 416  
             final Durability defaultDurability) throws IllegalArgumentException {
 417  47
         this();
 418  
 
 419  47
         myDefaultDurability = defaultDurability;
 420  47
         for (final String host : mongoDbUri.getHosts()) {
 421  49
             addServer(host);
 422  49
         }
 423  47
         if (myServers.isEmpty()) {
 424  0
             throw new IllegalArgumentException(
 425  
                     "Must provide at least 1 host to connect to.");
 426  
         }
 427  47
         if (!mongoDbUri.getDatabase().isEmpty()) {
 428  31
             setDefaultDatabase(mongoDbUri.getDatabase());
 429  
         }
 430  
 
 431  47
         final Map<String, String> parameters = mongoDbUri.getParsedOptions();
 432  47
         final Map<String, String> renames = new HashMap<String, String>();
 433  
 
 434  
         // Renames for the standard names.
 435  47
         final Iterator<Map.Entry<String, String>> iter = parameters.entrySet()
 436  
                 .iterator();
 437  110
         while (iter.hasNext()) {
 438  63
             final Map.Entry<String, String> entry = iter.next();
 439  63
             final String name = entry.getKey();
 440  63
             if ("sockettimeoutms".equals(entry.getKey())) {
 441  4
                 renames.put("readtimeout", entry.getValue());
 442  4
                 iter.remove();
 443  
             }
 444  59
             else if ("usesokeepalive".equals(name)) {
 445  2
                 renames.put("usingsokeepalive", entry.getValue());
 446  2
                 iter.remove();
 447  
             }
 448  57
             else if ("minpoolsize".equals(name)) {
 449  1
                 renames.put("minconnectioncount", entry.getValue());
 450  1
                 iter.remove();
 451  
             }
 452  56
             else if ("maxpoolsize".equals(name)) {
 453  1
                 renames.put("maxconnectioncount", entry.getValue());
 454  1
                 iter.remove();
 455  
             }
 456  55
             else if (name.endsWith("ms")) {
 457  4
                 final String newName = name.substring(0,
 458  
                         name.length() - "ms".length());
 459  4
                 renames.put(newName, entry.getValue());
 460  4
                 iter.remove();
 461  
             }
 462  63
         }
 463  47
         parameters.putAll(renames);
 464  
 
 465  
         // Remove the parameters for the Credentials and durability and use the
 466  
         // full URI for the text to feed to the property editor.
 467  47
         parameters.keySet().removeAll(CredentialEditor.MONGODB_URI_FIELDS);
 468  47
         parameters.put("credentials", mongoDbUri.toString());
 469  
 
 470  47
         if (parameters.keySet().removeAll(DurabilityEditor.MONGODB_URI_FIELDS)) {
 471  12
             parameters.put("defaultdurability", mongoDbUri.toString());
 472  
         }
 473  47
         if (parameters.keySet().removeAll(
 474  
                 ReadPreferenceEditor.MONGODB_URI_FIELDS)) {
 475  4
             parameters.put("defaultreadpreference", mongoDbUri.toString());
 476  
         }
 477  
 
 478  
         // Special handling for the SSL parameter.
 479  47
         final String sslValue = parameters.remove("ssl");
 480  47
         if (mongoDbUri.isUseSsl() || (sslValue != null)) {
 481  3
             if (mongoDbUri.isUseSsl() || Boolean.parseBoolean(sslValue)) {
 482  
                 try {
 483  2
                     setSocketFactory((SocketFactory) Class
 484  
                             .forName(
 485  
                                     "com.allanbank.mongodb.extensions.tls.TlsSocketFactory")
 486  
                             .newInstance());
 487  
                 }
 488  2
                 catch (final Throwable error) {
 489  2
                     setSocketFactory(SSLSocketFactory.getDefault());
 490  2
                     LOG.warn("Using the JVM default SSL Socket Factory. "
 491  
                             + "This may allow man-in-the-middle attacks. "
 492  
                             + "See http://www.allanbank.com/mongodb-async-driver/userguide/tls.html");
 493  2
                 }
 494  
             }
 495  
             else {
 496  1
                 setSocketFactory(null);
 497  
             }
 498  
         }
 499  
 
 500  
         try {
 501  47
             final BeanInfo info = Introspector
 502  
                     .getBeanInfo(MongoClientConfiguration.class);
 503  47
             final PropertyDescriptor[] descriptors = info
 504  
                     .getPropertyDescriptors();
 505  47
             final Map<String, PropertyDescriptor> descriptorsByName = new HashMap<String, PropertyDescriptor>();
 506  1363
             for (final PropertyDescriptor descriptor : descriptors) {
 507  1316
                 descriptorsByName
 508  
                         .put(descriptor.getName().toLowerCase(Locale.US),
 509  
                                 descriptor);
 510  
             }
 511  
 
 512  47
             for (final Map.Entry<String, String> param : parameters.entrySet()) {
 513  91
                 final String propName = param.getKey();
 514  91
                 final String propValue = param.getValue();
 515  
 
 516  91
                 final PropertyDescriptor descriptor = descriptorsByName
 517  
                         .get(propName);
 518  91
                 if (descriptor != null) {
 519  
                     try {
 520  80
                         updateFieldValue(descriptor, propName, propValue);
 521  
                     }
 522  2
                     catch (final NumberFormatException nfe) {
 523  2
                         throw new IllegalArgumentException("The '" + propName
 524  
                                 + "' parameter must have a numeric value not '"
 525  
                                 + propValue + "'.", nfe);
 526  
                     }
 527  0
                     catch (final Exception e) {
 528  0
                         throw new IllegalArgumentException("The '" + propName
 529  
                                 + "' parameter's editor could not be set '"
 530  
                                 + propValue + "'.", e);
 531  78
                     }
 532  
                 }
 533  11
                 else if ("uuidrepresentation".equals(propName)) {
 534  1
                     LOG.info("Changing the UUID representation is not supported.");
 535  
                 }
 536  10
                 else if ("replicaset".equals(propName)) {
 537  8
                     LOG.info("Not validating the replica set name is '{}'.",
 538  
                             propValue);
 539  
                 }
 540  
                 else {
 541  2
                     LOG.info("Unknown property '{}' and value '{}'.", propName,
 542  
                             propValue);
 543  
                 }
 544  89
             }
 545  
         }
 546  0
         catch (final IntrospectionException e) {
 547  0
             throw new IllegalArgumentException(
 548  
                     "Could not introspect on the MongoClientConfiguration.");
 549  45
         }
 550  45
     }
 551  
 
 552  
     /**
 553  
      * Adds the specified credentials to the configuration.
 554  
      * 
 555  
      * @param credentials
 556  
      *            The credentials to use when accessing the MongoDB server.
 557  
      * @throws IllegalArgumentException
 558  
      *             If the credentials refer to an unknown authentication type or
 559  
      *             the configuration already has a set of credentials for the
 560  
      *             credentials specified database.
 561  
      */
 562  
     public void addCredential(final Credential credentials)
 563  
             throws IllegalArgumentException {
 564  44
         final List<Credential> old = new ArrayList<Credential>(
 565  
                 myCredentials.values());
 566  
 
 567  
         try {
 568  44
             credentials.loadAuthenticator();
 569  
 
 570  44
             final Credential previous = myCredentials.putIfAbsent(
 571  
                     credentials.getDatabase(), credentials);
 572  44
             if (previous != null) {
 573  0
                 throw new IllegalArgumentException(
 574  
                         "There can only be one set of credentials for each database.");
 575  
             }
 576  
         }
 577  0
         catch (final ClassNotFoundException cnfe) {
 578  0
             throw new IllegalArgumentException(
 579  
                     "Could not load the credentials authenticator.", cnfe);
 580  
         }
 581  0
         catch (final InstantiationException ie) {
 582  0
             throw new IllegalArgumentException(
 583  
                     "Could not load the credentials authenticator.", ie);
 584  
         }
 585  0
         catch (final IllegalAccessException iae) {
 586  0
             throw new IllegalArgumentException(
 587  
                     "Could not load the credentials authenticator.", iae);
 588  44
         }
 589  
 
 590  44
         myPropSupport.firePropertyChange("credentials", old,
 591  
                 new ArrayList<Credential>(myCredentials.values()));
 592  44
     }
 593  
 
 594  
     /**
 595  
      * Adds the specified credentials to the configuration.
 596  
      * 
 597  
      * @param credentials
 598  
      *            The credentials to use when accessing the MongoDB server.
 599  
      * @throws IllegalArgumentException
 600  
      *             If the credentials refer to an unknown authentication type or
 601  
      *             the configuration already has a set of credentials for the
 602  
      *             credentials specified database.
 603  
      */
 604  
     public void addCredential(final Credential.Builder credentials)
 605  
             throws IllegalArgumentException {
 606  1
         addCredential(credentials.build());
 607  1
     }
 608  
 
 609  
     /**
 610  
      * Add a {@link PropertyChangeListener} from the configuration. The listener
 611  
      * will receive notification of all changes to the configuration.
 612  
      * 
 613  
      * @param listener
 614  
      *            The {@link PropertyChangeListener} to be added
 615  
      */
 616  
     public synchronized void addPropertyChangeListener(
 617  
             final PropertyChangeListener listener) {
 618  37
         myPropSupport.addPropertyChangeListener(listener);
 619  37
     }
 620  
 
 621  
     /**
 622  
      * Add a {@link PropertyChangeListener} from the configuration. The listener
 623  
      * will receive notification of all changes to the configuration's specified
 624  
      * property.
 625  
      * 
 626  
      * @param propertyName
 627  
      *            The name of the property to listen on.
 628  
      * @param listener
 629  
      *            The {@link PropertyChangeListener} to be added
 630  
      */
 631  
 
 632  
     public synchronized void addPropertyChangeListener(
 633  
             final String propertyName, final PropertyChangeListener listener) {
 634  0
         myPropSupport.addPropertyChangeListener(propertyName, listener);
 635  0
     }
 636  
 
 637  
     /**
 638  
      * Adds a server to initially attempt to connect to.
 639  
      * 
 640  
      * @param server
 641  
      *            The server to add.
 642  
      */
 643  
     public void addServer(final InetSocketAddress server) {
 644  137
         final List<InetSocketAddress> old = new ArrayList<InetSocketAddress>(
 645  
                 myServers);
 646  
 
 647  137
         myServers.add(server);
 648  
 
 649  137
         myPropSupport.firePropertyChange("servers", old,
 650  
                 Collections.unmodifiableList(myServers));
 651  137
     }
 652  
 
 653  
     /**
 654  
      * Adds a server to initially attempt to connect to.
 655  
      * 
 656  
      * @param server
 657  
      *            The server to add.
 658  
      */
 659  
     public void addServer(final String server) {
 660  67
         addServer(ServerNameUtils.parse(server));
 661  67
     }
 662  
 
 663  
     /**
 664  
      * Sets up the instance to authenticate with the MongoDB servers. This
 665  
      * should be done before using this configuration to instantiate a
 666  
      * {@link Mongo} instance.
 667  
      * 
 668  
      * @param userName
 669  
      *            The user name.
 670  
      * @param password
 671  
      *            the password.
 672  
      * @throws MongoDbAuthenticationException
 673  
      *             On a failure initializing the authentication information.
 674  
      * @deprecated Replaced with the more general {@link Credential} capability.
 675  
      *             Will be removed after the 1.3.0 release.
 676  
      */
 677  
     @Deprecated
 678  
     public void authenticate(final String userName, final String password)
 679  
             throws MongoDbAuthenticationException {
 680  7
         if (myLegacyCredential != null) {
 681  0
             myCredentials.remove(myLegacyCredential.getDatabase());
 682  
         }
 683  7
         myLegacyCredential = Credential.builder().userName(userName)
 684  
                 .password(password.toCharArray())
 685  
                 .database(getDefaultDatabase()).mongodbCR().build();
 686  
 
 687  7
         addCredential(myLegacyCredential);
 688  7
     }
 689  
 
 690  
     /**
 691  
      * Sets up the instance to authenticate with the MongoDB servers. This
 692  
      * should be done before using this configuration to instantiate a
 693  
      * {@link Mongo} instance.
 694  
      * 
 695  
      * @param userName
 696  
      *            The user name.
 697  
      * @param password
 698  
      *            the password.
 699  
      * @throws MongoDbAuthenticationException
 700  
      *             On a failure initializing the authentication information.
 701  
      * @deprecated Replaced with the more general {@link Credential} capability.
 702  
      *             Will be removed after the 1.3.0 release.
 703  
      */
 704  
     @Deprecated
 705  
     public void authenticateAsAdmin(final String userName, final String password)
 706  
             throws MongoDbAuthenticationException {
 707  
 
 708  2
         addCredential(Credential.builder().userName(userName)
 709  
                 .password(password.toCharArray()).database(ADMIN_DB_NAME)
 710  
                 .mongodbCR().build());
 711  2
     }
 712  
 
 713  
     /**
 714  
      * Creates a copy of this MongoClientConfiguration.
 715  
      * <p>
 716  
      * Note: This is not a traditional clone to ensure a deep copy of all
 717  
      * information.
 718  
      * </p>
 719  
      */
 720  
     @Override
 721  
     public MongoClientConfiguration clone() {
 722  2
         MongoClientConfiguration clone = null;
 723  
         try {
 724  2
             clone = (MongoClientConfiguration) super.clone();
 725  
 
 726  2
             clone.myCredentials = new ConcurrentHashMap<String, Credential>();
 727  2
             for (final Credential credential : getCredentials()) {
 728  0
                 clone.addCredential(credential);
 729  0
             }
 730  
 
 731  2
             clone.myServers = new ArrayList<InetSocketAddress>();
 732  2
             for (final InetSocketAddress addr : getServerAddresses()) {
 733  4
                 clone.addServer(addr);
 734  4
             }
 735  
 
 736  2
             clone.myPropSupport = new PropertyChangeSupport(clone);
 737  
         }
 738  0
         catch (final CloneNotSupportedException shouldNotHappen) {
 739  0
             clone = new MongoClientConfiguration(this);
 740  2
         }
 741  2
         return clone;
 742  
     }
 743  
 
 744  
     /**
 745  
      * Returns the model the driver uses for managing connections.
 746  
      * <p>
 747  
      * Defaults to {@link ConnectionModel#RECEIVER_THREAD}.
 748  
      * </p>
 749  
      * 
 750  
      * @return The model used for managing connections.
 751  
      */
 752  
     public ConnectionModel getConnectionModel() {
 753  113
         return myConnectionModel;
 754  
     }
 755  
 
 756  
     /**
 757  
      * Returns how long to wait (in milliseconds) for a socket connection to
 758  
      * complete.
 759  
      * <p>
 760  
      * Defaults to 0 or forever.
 761  
      * </p>
 762  
      * 
 763  
      * @return The time to wait (in milliseconds) for a socket connection to
 764  
      *         complete.
 765  
      */
 766  
     public int getConnectTimeout() {
 767  328
         return myConnectTimeout;
 768  
     }
 769  
 
 770  
     /**
 771  
      * Returns the map of database names to credentials to use to access that
 772  
      * database on the server.
 773  
      * 
 774  
      * @return The map of database names to credentials to use to access that
 775  
      *         database on the server.
 776  
      */
 777  
     public Collection<Credential> getCredentials() {
 778  108
         return Collections.unmodifiableCollection(myCredentials.values());
 779  
     }
 780  
 
 781  
     /**
 782  
      * Returns the default database for the connection.
 783  
      * <p>
 784  
      * This is used as the database to authenticate against if the user is not
 785  
      * an administrative user.
 786  
      * </p>
 787  
      * <p>
 788  
      * Defaults to {@value #DEFAULT_DB_NAME}.
 789  
      * </p>
 790  
      * 
 791  
      * @return The default database value.
 792  
      * @deprecated Replaced with the more general {@link Credential} capability.
 793  
      *             Will be removed after the 1.3.0 release.
 794  
      */
 795  
     @Deprecated
 796  
     public String getDefaultDatabase() {
 797  23
         return myDefaultDatabase;
 798  
     }
 799  
 
 800  
     /**
 801  
      * Returns the default durability for write operations on the server.
 802  
      * <p>
 803  
      * Defaults to {@link Durability#ACK}.
 804  
      * </p>
 805  
      * 
 806  
      * @return The default durability for write operations on the server.
 807  
      */
 808  
     public Durability getDefaultDurability() {
 809  73
         return myDefaultDurability;
 810  
     }
 811  
 
 812  
     /**
 813  
      * Returns the default read preference for a query.
 814  
      * <p>
 815  
      * Defaults to {@link ReadPreference#PRIMARY}.
 816  
      * </p>
 817  
      * 
 818  
      * @return The default read preference for a query.
 819  
      */
 820  
     public ReadPreference getDefaultReadPreference() {
 821  20
         return myDefaultReadPreference;
 822  
     }
 823  
 
 824  
     /**
 825  
      * Returns the executor to use when processing responses from the server.
 826  
      * <p>
 827  
      * By default the executor is <code>null</code> which will cause the reply
 828  
      * handling to execute on the socket's receive thread.
 829  
      * </p>
 830  
      * <p>
 831  
      * Care should be taken to ensure that the executor does not drop requests.
 832  
      * This implies that the
 833  
      * {@link java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy} or
 834  
      * similar should be used as the
 835  
      * {@link java.util.concurrent.RejectedExecutionHandler}.
 836  
      * </p>
 837  
      * 
 838  
      * @return The executor value.
 839  
      */
 840  
     public Executor getExecutor() {
 841  212
         return myExecutor;
 842  
     }
 843  
 
 844  
     /**
 845  
      * Returns the type of hand off lock to use between threads in the core of
 846  
      * the driver.
 847  
      * <p>
 848  
      * Defaults to {@link LockType#MUTEX}.
 849  
      * </p>
 850  
      * 
 851  
      * @return The type of hand off lock used.
 852  
      */
 853  
     public LockType getLockType() {
 854  540
         return myLockType;
 855  
     }
 856  
 
 857  
     /**
 858  
      * Returns the maximum number of strings that may have their encoded form
 859  
      * cached.
 860  
      * <p>
 861  
      * Defaults to {@value #DEFAULT_MAX_STRING_CACHE_ENTRIES}.
 862  
      * </p>
 863  
      * <p>
 864  
      * Note: The caches are maintained per connection and there is a cache for
 865  
      * the encoder and another for the decoder. The results is that caching 25
 866  
      * string with 10 connections can result in 500 cache entries (2 * 25 * 10).
 867  
      * </p>
 868  
      * 
 869  
      * @return The maximum number of strings that may have their encoded form
 870  
      *         cached.
 871  
      */
 872  
     public int getMaxCachedStringEntries() {
 873  79
         return myMaxCachedStringEntries;
 874  
     }
 875  
 
 876  
     /**
 877  
      * Returns the maximum length for a string that the stream is allowed to
 878  
      * cache.This can be used to stop a single long string from pushing useful
 879  
      * values out of the cache. Setting this value to zero turns off the
 880  
      * caching.
 881  
      * <p>
 882  
      * Defaults to {@value #DEFAULT_MAX_STRING_CACHE_LENGTH}.
 883  
      * </p>
 884  
      * <p>
 885  
      * Note: The caches are maintained per connection and there is a cache for
 886  
      * the encoder and another for the decoder. The results is that caching 25
 887  
      * string with 10 connections can result in 500 cache entries (2 * 25 * 10).
 888  
      * </p>
 889  
      * 
 890  
      * @return The maximum length for a string that the stream is allowed to
 891  
      *         cache.
 892  
      */
 893  
     public int getMaxCachedStringLength() {
 894  79
         return myMaxCachedStringLength;
 895  
     }
 896  
 
 897  
     /**
 898  
      * Returns the maximum number of connections to use.
 899  
      * <p>
 900  
      * Defaults to 3.
 901  
      * </p>
 902  
      * <p>
 903  
      * <em>Note:</em> In the case of connecting to a replica set this setting
 904  
      * limits the number of connections to the primary server. The driver will
 905  
      * create single connections to the secondary servers if queries are issued
 906  
      * with a {@link ReadPreference} other than {@link ReadPreference#PRIMARY}.
 907  
      * </p>
 908  
      * 
 909  
      * @return The maximum connections to use.
 910  
      */
 911  
     public int getMaxConnectionCount() {
 912  225
         return myMaxConnectionCount;
 913  
     }
 914  
 
 915  
     /**
 916  
      * Returns the number of read timeouts (a tick) before closing the
 917  
      * connection.
 918  
      * <p>
 919  
      * Defaults to {@link Integer#MAX_VALUE}.
 920  
      * </p>
 921  
      * 
 922  
      * @return The number of read timeouts (a tick) before closing the
 923  
      *         connection.
 924  
      */
 925  
     public int getMaxIdleTickCount() {
 926  16
         return myMaxIdleTickCount;
 927  
     }
 928  
 
 929  
     /**
 930  
      * Returns the maximum number of pending operations to allow per connection.
 931  
      * The higher the value the more "asynchronous" the driver can be but risks
 932  
      * more operations being in an unknown state on a connection error. When the
 933  
      * connection has this many pending connections additional requests will
 934  
      * block.
 935  
      * <p>
 936  
      * Defaults to 1024.
 937  
      * </p>
 938  
      * <p>
 939  
      * <em>Note:</em> In the case of an connection error it is impossible to
 940  
      * determine which pending operations completed and which did not. Setting
 941  
      * this value to 1 results in synchronous operations that wait for
 942  
      * responses.
 943  
      * </p>
 944  
      * 
 945  
      * @return The maximum number of pending operations to allow per connection.
 946  
      */
 947  
     public int getMaxPendingOperationsPerConnection() {
 948  265
         return myMaxPendingOperationsPerConnection;
 949  
     }
 950  
 
 951  
     /**
 952  
      * Returns the maximum number of milliseconds that a secondary can be behind
 953  
      * the primary before they will be excluded from being used for queries on
 954  
      * secondaries.
 955  
      * <p>
 956  
      * Defaults to 5 minutes (300,000).
 957  
      * </p>
 958  
      * 
 959  
      * @return The maximum number of milliseconds that a secondary can be behind
 960  
      *         the primary before they will be excluded from being used for
 961  
      *         queries on secondaries.
 962  
      */
 963  
     public long getMaxSecondaryLag() {
 964  94
         return myMaxSecondaryLag;
 965  
     }
 966  
 
 967  
     /**
 968  
      * Returns the minimum number of connections to try and keep open.
 969  
      * <p>
 970  
      * Defaults to 0.
 971  
      * </p>
 972  
      * 
 973  
      * @return The minimum number of connections to try and keep open.
 974  
      */
 975  
     public int getMinConnectionCount() {
 976  9
         return myMinConnectionCount;
 977  
     }
 978  
 
 979  
     /**
 980  
      * Gets the password hash for authentication with the database.
 981  
      * 
 982  
      * @return The password hash for authentication with the database.
 983  
      * @deprecated Replaced with the more general {@link Credential} capability.
 984  
      *             Will be removed after the 1.3.0 release.
 985  
      */
 986  
     @Deprecated
 987  
     public String getPasswordHash() {
 988  39
         if (!myCredentials.isEmpty()) {
 989  15
             final Credential credentials = myCredentials.entrySet().iterator()
 990  
                     .next().getValue();
 991  
             try {
 992  15
                 final MessageDigest md5 = MessageDigest.getInstance("MD5");
 993  15
                 final byte[] digest = md5.digest((credentials.getUserName()
 994  
                         + ":mongo:" + new String(credentials.getPassword()))
 995  
                         .getBytes(UTF8));
 996  
 
 997  15
                 return IOUtils.toHex(digest);
 998  
             }
 999  0
             catch (final NoSuchAlgorithmException e) {
 1000  0
                 throw new MongoDbAuthenticationException(e);
 1001  
             }
 1002  
 
 1003  
         }
 1004  24
         return null;
 1005  
     }
 1006  
 
 1007  
     /**
 1008  
      * Returns how long to wait (in milliseconds) for a socket read to complete.
 1009  
      * <p>
 1010  
      * Defaults to 0 or never.
 1011  
      * </p>
 1012  
      * 
 1013  
      * @return The time to wait (in milliseconds) for a socket read to complete.
 1014  
      */
 1015  
     public int getReadTimeout() {
 1016  242
         return myReadTimeout;
 1017  
     }
 1018  
 
 1019  
     /**
 1020  
      * Returns how long to wait (in milliseconds) for a broken connection to be
 1021  
      * reconnected.
 1022  
      * <p>
 1023  
      * Defaults to 0 or forever.
 1024  
      * </p>
 1025  
      * 
 1026  
      * @return The time to wait (in milliseconds) for a broken connection to be
 1027  
      *         reconnected.
 1028  
      */
 1029  
     public int getReconnectTimeout() {
 1030  18
         return myReconnectTimeout;
 1031  
     }
 1032  
 
 1033  
     /**
 1034  
      * Returns the list of servers to initially attempt to connect to.
 1035  
      * 
 1036  
      * @return The list of servers to initially attempt to connect to.
 1037  
      */
 1038  
     public List<InetSocketAddress> getServerAddresses() {
 1039  93
         return Collections.unmodifiableList(myServers);
 1040  
     }
 1041  
 
 1042  
     /**
 1043  
      * Returns the list of servers to initially attempt to connect to.
 1044  
      * 
 1045  
      * @return The list of servers to initially attempt to connect to.
 1046  
      */
 1047  
     public List<String> getServers() {
 1048  56
         final List<String> servers = new ArrayList<String>(myServers.size());
 1049  56
         for (final InetSocketAddress addr : myServers) {
 1050  67
             servers.add(ServerNameUtils.normalize(addr));
 1051  67
         }
 1052  56
         return servers;
 1053  
     }
 1054  
 
 1055  
     /**
 1056  
      * Returns the socket factory to use in making connections to the MongoDB
 1057  
      * server.
 1058  
      * <p>
 1059  
      * Defaults to {@link SocketFactory#getDefault() SocketFactory.getDefault()}
 1060  
      * .
 1061  
      * </p>
 1062  
      * 
 1063  
      * @return The socketFactory value.
 1064  
      * @see #setSocketFactory(SocketFactory) setSocketFactory(...) or usage
 1065  
      *      examples and suggestions.
 1066  
      */
 1067  
     public SocketFactory getSocketFactory() {
 1068  213
         if (mySocketFactory == null) {
 1069  126
             mySocketFactory = SocketFactory.getDefault();
 1070  
         }
 1071  213
         return mySocketFactory;
 1072  
     }
 1073  
 
 1074  
     /**
 1075  
      * Returns the thread factory for managing connections.
 1076  
      * 
 1077  
      * @return The thread factory for managing connections.
 1078  
      */
 1079  
     public ThreadFactory getThreadFactory() {
 1080  319
         return myThreadFactory;
 1081  
     }
 1082  
 
 1083  
     /**
 1084  
      * Gets the user name for authenticating with the database.
 1085  
      * 
 1086  
      * @return The user name for authenticating with the database.
 1087  
      * @deprecated Replaced with the more general {@link Credential} capability.
 1088  
      *             Will be removed after the 1.3.0 release.
 1089  
      */
 1090  
     @Deprecated
 1091  
     public String getUserName() {
 1092  38
         if (!myCredentials.isEmpty()) {
 1093  14
             final Credential credentials = myCredentials.entrySet().iterator()
 1094  
                     .next().getValue();
 1095  14
             return credentials.getUserName();
 1096  
         }
 1097  24
         return null;
 1098  
     }
 1099  
 
 1100  
     /**
 1101  
      * Returns true if the user should authenticate as an administrative user.
 1102  
      * 
 1103  
      * @return True if the user should authenticate as an administrative user.
 1104  
      * @deprecated Replaced with the more general {@link Credential} capability.
 1105  
      *             Will be removed after the 1.3.0 release.
 1106  
      */
 1107  
     @Deprecated
 1108  
     public boolean isAdminUser() {
 1109  30
         return myCredentials.containsKey(ADMIN_DB_NAME);
 1110  
     }
 1111  
 
 1112  
     /**
 1113  
      * Returns true if the connection is authenticating. If any credentials have
 1114  
      * been added to this configuration then all connections will use
 1115  
      * authentication.
 1116  
      * 
 1117  
      * @return True if the connections should authenticate with the server.
 1118  
      */
 1119  
     public boolean isAuthenticating() {
 1120  73
         return !myCredentials.isEmpty();
 1121  
     }
 1122  
 
 1123  
     /**
 1124  
      * Returns if additional servers are auto discovered or if connections are
 1125  
      * limited to the ones manually configured.
 1126  
      * <p>
 1127  
      * Defaults to true, e.g., auto-discover.
 1128  
      * </p>
 1129  
      * 
 1130  
      * @return True if additional servers are auto discovered
 1131  
      */
 1132  
     public boolean isAutoDiscoverServers() {
 1133  90
         return myAutoDiscoverServers;
 1134  
     }
 1135  
 
 1136  
     /**
 1137  
      * Returns if the {@link java.net.Socket#setKeepAlive(boolean) SO_KEEPALIVE}
 1138  
      * socket option is set.
 1139  
      * <p>
 1140  
      * Defaults to true, e.g., use SO_KEEPALIVE.
 1141  
      * </p>
 1142  
      * 
 1143  
      * @return True if the {@link java.net.Socket#setKeepAlive(boolean)
 1144  
      *         SO_KEEPALIVE} socket option is set.
 1145  
      */
 1146  
     public boolean isUsingSoKeepalive() {
 1147  233
         return myUsingSoKeepalive;
 1148  
     }
 1149  
 
 1150  
     /**
 1151  
      * Removes a {@link PropertyChangeListener} from the configuration. The
 1152  
      * listener will no longer receive notification of all changes to the
 1153  
      * configuration.
 1154  
      * 
 1155  
      * @param listener
 1156  
      *            The {@link PropertyChangeListener} to be removed
 1157  
      */
 1158  
     public synchronized void removePropertyChangeListener(
 1159  
             final PropertyChangeListener listener) {
 1160  34
         myPropSupport.removePropertyChangeListener(listener);
 1161  34
     }
 1162  
 
 1163  
     /**
 1164  
      * Removes a {@link PropertyChangeListener} from the configuration. The
 1165  
      * listener will no longer receive notification of all changes to the
 1166  
      * configuration's specified property.
 1167  
      * 
 1168  
      * @param propertyName
 1169  
      *            The name of the property that was listened on.
 1170  
      * @param listener
 1171  
      *            The {@link PropertyChangeListener} to be removed
 1172  
      */
 1173  
 
 1174  
     public synchronized void removePropertyChangeListener(
 1175  
             final String propertyName, final PropertyChangeListener listener) {
 1176  0
         myPropSupport.removePropertyChangeListener(propertyName, listener);
 1177  0
     }
 1178  
 
 1179  
     /**
 1180  
      * Sets if additional servers are auto discovered or if connections are
 1181  
      * limited to the ones manually configured.
 1182  
      * <p>
 1183  
      * Defaults to true, e.g., auto-discover.
 1184  
      * </p>
 1185  
      * 
 1186  
      * @param autoDiscoverServers
 1187  
      *            The new value for auto-discovering servers.
 1188  
      */
 1189  
     public void setAutoDiscoverServers(final boolean autoDiscoverServers) {
 1190  26
         final boolean old = myAutoDiscoverServers;
 1191  
 
 1192  26
         myAutoDiscoverServers = autoDiscoverServers;
 1193  
 
 1194  26
         myPropSupport.firePropertyChange("autoDiscoverServers", old,
 1195  
                 myAutoDiscoverServers);
 1196  26
     }
 1197  
 
 1198  
     /**
 1199  
      * Sets the model the driver uses for managing connections.
 1200  
      * <p>
 1201  
      * Defaults to {@link ConnectionModel#RECEIVER_THREAD}.
 1202  
      * </p>
 1203  
      * 
 1204  
      * @param connectionModel
 1205  
      *            The new value for the model the driver uses for managing
 1206  
      *            connections.
 1207  
      */
 1208  
     public void setConnectionModel(final ConnectionModel connectionModel) {
 1209  3
         final ConnectionModel old = myConnectionModel;
 1210  
 
 1211  3
         myConnectionModel = connectionModel;
 1212  
 
 1213  3
         myPropSupport.firePropertyChange("connectionModel", old,
 1214  
                 myConnectionModel);
 1215  3
     }
 1216  
 
 1217  
     /**
 1218  
      * Sets how long to wait (in milliseconds) for a socket connection to
 1219  
      * complete.
 1220  
      * 
 1221  
      * @param connectTimeout
 1222  
      *            The time to wait (in milliseconds) for a socket connection to
 1223  
      *            complete.
 1224  
      */
 1225  
     public void setConnectTimeout(final int connectTimeout) {
 1226  6
         final int old = myConnectTimeout;
 1227  
 
 1228  6
         myConnectTimeout = connectTimeout;
 1229  
 
 1230  6
         myPropSupport.firePropertyChange("connectTimeout", old,
 1231  
                 myConnectTimeout);
 1232  6
     }
 1233  
 
 1234  
     /**
 1235  
      * Sets the credentials to use to access the server. This removes all
 1236  
      * existing credentials.
 1237  
      * 
 1238  
      * @param credentials
 1239  
      *            The credentials to use to access the server..
 1240  
      * @throws IllegalArgumentException
 1241  
      *             If the credentials refer to an unknown authentication type or
 1242  
      *             the configuration already has a set of credentials for the
 1243  
      *             credentials specified database.
 1244  
      */
 1245  
     public void setCredentials(final Collection<Credential> credentials) {
 1246  47
         final List<Credential> old = new ArrayList<Credential>(
 1247  
                 myCredentials.values());
 1248  
 
 1249  47
         myCredentials.clear();
 1250  47
         for (final Credential credential : credentials) {
 1251  8
             addCredential(credential);
 1252  8
         }
 1253  
 
 1254  47
         myPropSupport.firePropertyChange("credentials", old,
 1255  
                 new ArrayList<Credential>(myCredentials.values()));
 1256  47
     }
 1257  
 
 1258  
     /**
 1259  
      * Sets the default database for the connection.
 1260  
      * <p>
 1261  
      * This is used as the database to authenticate against if the user is not
 1262  
      * an administrative user.
 1263  
      * </p>
 1264  
      * <p>
 1265  
      * Defaults to {@value #DEFAULT_DB_NAME}.
 1266  
      * </p>
 1267  
      * 
 1268  
      * @param defaultDatabase
 1269  
      *            The new default database value.
 1270  
      * @deprecated Replaced with the more general {@link Credential} capability.
 1271  
      *             Will be removed after the 1.3.0 release.
 1272  
      */
 1273  
     @Deprecated
 1274  
     public void setDefaultDatabase(final String defaultDatabase) {
 1275  39
         final String old = myDefaultDatabase;
 1276  
 
 1277  39
         myDefaultDatabase = defaultDatabase;
 1278  
 
 1279  39
         if (myLegacyCredential != null) {
 1280  2
             final List<Credential> oldCredentials = new ArrayList<Credential>(
 1281  
                     myCredentials.values());
 1282  2
             myCredentials.remove(myLegacyCredential.getDatabase());
 1283  2
             myPropSupport.firePropertyChange("credentials", oldCredentials,
 1284  
                     new ArrayList<Credential>(myCredentials.values()));
 1285  
 
 1286  2
             myLegacyCredential = Credential.builder()
 1287  
                     .userName(myLegacyCredential.getUserName())
 1288  
                     .password(myLegacyCredential.getPassword())
 1289  
                     .database(defaultDatabase).mongodbCR().build();
 1290  2
             addCredential(myLegacyCredential);
 1291  
         }
 1292  
 
 1293  39
         myPropSupport.firePropertyChange("defaultDatabase", old,
 1294  
                 myDefaultDatabase);
 1295  39
     }
 1296  
 
 1297  
     /**
 1298  
      * Sets the default durability for write operations on the server to the new
 1299  
      * value.
 1300  
      * 
 1301  
      * @param defaultDurability
 1302  
      *            The default durability for write operations on the server.
 1303  
      */
 1304  
     public void setDefaultDurability(final Durability defaultDurability) {
 1305  45
         final Durability old = myDefaultDurability;
 1306  
 
 1307  45
         myDefaultDurability = defaultDurability;
 1308  
 
 1309  45
         myPropSupport.firePropertyChange("defaultDurability", old,
 1310  
                 myDefaultDurability);
 1311  45
     }
 1312  
 
 1313  
     /**
 1314  
      * Sets the value of the default read preference for a query.
 1315  
      * <p>
 1316  
      * Defaults to {@link ReadPreference#PRIMARY} if <code>null</code> is set.
 1317  
      * </p>
 1318  
      * 
 1319  
      * @param defaultReadPreference
 1320  
      *            The default read preference for a query.
 1321  
      */
 1322  
     public void setDefaultReadPreference(
 1323  
             final ReadPreference defaultReadPreference) {
 1324  9
         final ReadPreference old = myDefaultReadPreference;
 1325  
 
 1326  9
         if (defaultReadPreference == null) {
 1327  2
             myDefaultReadPreference = ReadPreference.PRIMARY;
 1328  
         }
 1329  
         else {
 1330  7
             myDefaultReadPreference = defaultReadPreference;
 1331  
         }
 1332  
 
 1333  9
         myPropSupport.firePropertyChange("defaultReadPreference", old,
 1334  
                 myDefaultReadPreference);
 1335  9
     }
 1336  
 
 1337  
     /**
 1338  
      * Sets the value of executor for replies from the server.
 1339  
      * <p>
 1340  
      * By default the executor is <code>null</code> which will cause the reply
 1341  
      * handling to execute on the socket's receive thread.
 1342  
      * </p>
 1343  
      * <p>
 1344  
      * Care should be taken to ensure that the executor does not drop requests.
 1345  
      * This implies that the
 1346  
      * {@link java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy} or
 1347  
      * similar should be used as the
 1348  
      * {@link java.util.concurrent.RejectedExecutionHandler}.
 1349  
      * </p>
 1350  
      * 
 1351  
      * @param executor
 1352  
      *            The new value for the executor.
 1353  
      */
 1354  
     public void setExecutor(final Executor executor) {
 1355  3
         final Executor old = myExecutor;
 1356  
 
 1357  3
         myExecutor = executor;
 1358  
 
 1359  3
         myPropSupport.firePropertyChange("executor", old, myExecutor);
 1360  3
     }
 1361  
 
 1362  
     /**
 1363  
      * Sets the type of hand off lock to use between threads in the core of the
 1364  
      * driver.
 1365  
      * <p>
 1366  
      * Defaults to {@link LockType#MUTEX}.
 1367  
      * </p>
 1368  
      * 
 1369  
      * @param lockType
 1370  
      *            The new value for the type of hand off lock used.
 1371  
      */
 1372  
     public void setLockType(final LockType lockType) {
 1373  5
         final LockType old = myLockType;
 1374  
 
 1375  5
         myLockType = lockType;
 1376  
 
 1377  5
         myPropSupport.firePropertyChange("lockType", old, myLockType);
 1378  5
     }
 1379  
 
 1380  
     /**
 1381  
      * Sets the value of maximum number of strings that may have their encoded
 1382  
      * form cached.
 1383  
      * <p>
 1384  
      * Defaults to {@value #DEFAULT_MAX_STRING_CACHE_ENTRIES}.
 1385  
      * </p>
 1386  
      * <p>
 1387  
      * Note: The caches are maintained per {@link MongoClient} instance.
 1388  
      * </p>
 1389  
      * 
 1390  
      * @param maxCacheEntries
 1391  
      *            The new value for the maximum number of strings that may have
 1392  
      *            their encoded form cached.
 1393  
      */
 1394  
     public void setMaxCachedStringEntries(final int maxCacheEntries) {
 1395  1
         final int old = myMaxCachedStringEntries;
 1396  
 
 1397  1
         myMaxCachedStringEntries = maxCacheEntries;
 1398  
 
 1399  1
         myPropSupport.firePropertyChange("maxCachedStringEntries", old,
 1400  
                 myMaxCachedStringEntries);
 1401  1
     }
 1402  
 
 1403  
     /**
 1404  
      * Sets the value of length for a string that may be cached. This can be
 1405  
      * used to stop a single long string from pushing useful values out of the
 1406  
      * cache. Setting this value to zero turns off the caching.
 1407  
      * <p>
 1408  
      * Defaults to {@value #DEFAULT_MAX_STRING_CACHE_LENGTH}.
 1409  
      * </p>
 1410  
      * <p>
 1411  
      * Note: The caches are maintained per {@link MongoClient} instance.
 1412  
      * </p>
 1413  
      * 
 1414  
      * @param maxlength
 1415  
      *            The new value for the length for a string that the encoder is
 1416  
      *            allowed to cache.
 1417  
      */
 1418  
     public void setMaxCachedStringLength(final int maxlength) {
 1419  1
         final int old = myMaxCachedStringLength;
 1420  
 
 1421  1
         myMaxCachedStringLength = maxlength;
 1422  
 
 1423  1
         myPropSupport.firePropertyChange("maxCachedStringLength", old,
 1424  
                 myMaxCachedStringLength);
 1425  1
     }
 1426  
 
 1427  
     /**
 1428  
      * Sets the maximum number of connections to use.
 1429  
      * <p>
 1430  
      * Defaults to 3.
 1431  
      * </p>
 1432  
      * <p>
 1433  
      * <em>Note:</em> In the case of connecting to a replica set this setting
 1434  
      * limits the number of connections to the primary server. The driver will
 1435  
      * create single connections to the secondary servers if queries are issued
 1436  
      * with a {@link ReadPreference} other than {@link ReadPreference#PRIMARY}.
 1437  
      * </p>
 1438  
      * 
 1439  
      * @param maxConnectionCount
 1440  
      *            New maximum number of connections to use.
 1441  
      */
 1442  
     public void setMaxConnectionCount(final int maxConnectionCount) {
 1443  21
         final int old = myMaxConnectionCount;
 1444  
 
 1445  21
         myMaxConnectionCount = maxConnectionCount;
 1446  
 
 1447  21
         myPropSupport.firePropertyChange("maxConnectionCount", old,
 1448  
                 myMaxConnectionCount);
 1449  21
     }
 1450  
 
 1451  
     /**
 1452  
      * Sets the value of the number of read timeouts (a tick) before closing the
 1453  
      * connection.
 1454  
      * 
 1455  
      * @param idleTickCount
 1456  
      *            The new value for the number of read timeouts (a tick) before
 1457  
      *            closing the connection.
 1458  
      */
 1459  
     public void setMaxIdleTickCount(final int idleTickCount) {
 1460  1
         final int old = myMaxIdleTickCount;
 1461  
 
 1462  1
         myMaxIdleTickCount = idleTickCount;
 1463  
 
 1464  1
         myPropSupport.firePropertyChange("maxIdleTickCount", old,
 1465  
                 myMaxIdleTickCount);
 1466  1
     }
 1467  
 
 1468  
     /**
 1469  
      * Sets the maximum number of pending operations to allow per connection.
 1470  
      * The higher the value the more "asynchronous" the driver can be but risks
 1471  
      * more operations being in an unknown state on a connection error. When the
 1472  
      * connection has this many pending connections additional requests will
 1473  
      * block.
 1474  
      * 
 1475  
      * @param maxPendingOperationsPerConnection
 1476  
      *            The new maximum number of pending operations to allow per
 1477  
      *            connection.
 1478  
      */
 1479  
     public void setMaxPendingOperationsPerConnection(
 1480  
             final int maxPendingOperationsPerConnection) {
 1481  4
         final int old = myMaxPendingOperationsPerConnection;
 1482  
 
 1483  4
         myMaxPendingOperationsPerConnection = maxPendingOperationsPerConnection;
 1484  
 
 1485  4
         myPropSupport.firePropertyChange("maxPendingOperationsPerConnection",
 1486  
                 old, myMaxPendingOperationsPerConnection);
 1487  4
     }
 1488  
 
 1489  
     /**
 1490  
      * Sets the maximum number of milliseconds that a secondary can be behind
 1491  
      * the primary before they will be excluded from being used for queries on
 1492  
      * secondaries.
 1493  
      * <p>
 1494  
      * Defaults to 5 minutes (300,000).
 1495  
      * </p>
 1496  
      * 
 1497  
      * @param maxSecondaryLag
 1498  
      *            The new value for the maximum number of milliseconds that a
 1499  
      *            secondary can be behind the primary before they will be
 1500  
      *            excluded from being used for queries on secondaries.
 1501  
      */
 1502  
     public void setMaxSecondaryLag(final long maxSecondaryLag) {
 1503  4
         final long old = myMaxSecondaryLag;
 1504  
 
 1505  4
         myMaxSecondaryLag = maxSecondaryLag;
 1506  
 
 1507  4
         myPropSupport.firePropertyChange("maxSecondaryLag", Long.valueOf(old),
 1508  
                 Long.valueOf(myMaxSecondaryLag));
 1509  4
     }
 1510  
 
 1511  
     /**
 1512  
      * Sets the value of the minimum number of connections to try and keep open.
 1513  
      * 
 1514  
      * @param minimumConnectionCount
 1515  
      *            The new value for the minimum number of connections to try and
 1516  
      *            keep open.
 1517  
      */
 1518  
     public void setMinConnectionCount(final int minimumConnectionCount) {
 1519  1
         final int old = myMinConnectionCount;
 1520  
 
 1521  1
         myMinConnectionCount = minimumConnectionCount;
 1522  
 
 1523  1
         myPropSupport.firePropertyChange("minConnectionCount", old,
 1524  
                 myMinConnectionCount);
 1525  1
     }
 1526  
 
 1527  
     /**
 1528  
      * @param readTimeout
 1529  
      *            The time to wait (in milliseconds) for a socket read to
 1530  
      *            complete.
 1531  
      */
 1532  
     public void setReadTimeout(final int readTimeout) {
 1533  32
         final int old = myReadTimeout;
 1534  
 
 1535  32
         myReadTimeout = readTimeout;
 1536  
 
 1537  32
         myPropSupport.firePropertyChange("readTimeout", old, myReadTimeout);
 1538  32
     }
 1539  
 
 1540  
     /**
 1541  
      * Sets how long to wait (in milliseconds) for a broken connection to
 1542  
      * reconnect.
 1543  
      * 
 1544  
      * @param connectTimeout
 1545  
      *            The time to wait (in milliseconds) for a broken connection to
 1546  
      *            reconnect.
 1547  
      */
 1548  
     public void setReconnectTimeout(final int connectTimeout) {
 1549  9
         final int old = myReconnectTimeout;
 1550  
 
 1551  9
         myReconnectTimeout = connectTimeout;
 1552  
 
 1553  9
         myPropSupport.firePropertyChange("reconnectTimeout", old,
 1554  
                 myReconnectTimeout);
 1555  9
     }
 1556  
 
 1557  
     /**
 1558  
      * Sets the servers to initially attempt to connect to.
 1559  
      * 
 1560  
      * @param servers
 1561  
      *            The servers to connect to.
 1562  
      */
 1563  
     public void setServers(final List<InetSocketAddress> servers) {
 1564  4
         final List<InetSocketAddress> old = new ArrayList<InetSocketAddress>(
 1565  
                 myServers);
 1566  
 
 1567  4
         myServers.clear();
 1568  4
         if (servers != null) {
 1569  2
             for (final InetSocketAddress server : servers) {
 1570  4
                 addServer(server);
 1571  4
             }
 1572  
         }
 1573  
 
 1574  4
         myPropSupport.firePropertyChange("servers", old,
 1575  
                 Collections.unmodifiableList(myServers));
 1576  4
     }
 1577  
 
 1578  
     /**
 1579  
      * Sets the socket factory to use in making connections to the MongoDB
 1580  
      * server. Setting the SocketFactory to null resets the factory to the
 1581  
      * default.
 1582  
      * <p>
 1583  
      * Defaults to {@link SocketFactory#getDefault()
 1584  
      * SocketFactory.getDefault().}
 1585  
      * </p>
 1586  
      * <p>
 1587  
      * For SSL based connections this can be an appropriately configured
 1588  
      * {@link javax.net.ssl.SSLSocketFactory}.
 1589  
      * </p>
 1590  
      * <p>
 1591  
      * Other {@link Socket} and {@link InetSocketAddress} implementations with
 1592  
      * an appropriate {@link SocketFactory} implementation can be used with the
 1593  
      * driver. The driver only ever calls the
 1594  
      * {@link SocketFactory#createSocket()} method and then connects the socket
 1595  
      * passing the server's {@link InetSocketAddress}.
 1596  
      * </p>
 1597  
      * <p>
 1598  
      * See the <a href="http://code.google.com/p/junixsocket">junixsocket
 1599  
      * Project</a> for an example of a {@link Socket} and
 1600  
      * {@link InetSocketAddress} implementations for UNIX Domain Sockets that
 1601  
      * can be wrapped with SocketFactory similar to the following:<blockquote>
 1602  
      * <code><pre>
 1603  
      *  public class AFUNIXSocketFactory extends SocketFactory {
 1604  
      *      public Socket createSocket() throws java.io.IOException {
 1605  
      *          return new org.newsclub.net.unix.AFUNIXSocket.newInstance();
 1606  
      *      }
 1607  
      * 
 1608  
      *      public Socket createSocket(String host, int port) throws SocketException {
 1609  
      *          throw new SocketException("AFUNIX socket does not support connections to a host/port");
 1610  
      *      }
 1611  
      * 
 1612  
      *      public Socket createSocket(InetAddress host, int port) throws SocketException {
 1613  
      *          throw new SocketException("AFUNIX socket does not support connections to a host/port");
 1614  
      *      }
 1615  
      * 
 1616  
      *      public Socket createSocket(String host, int port, InetAddress localHost,
 1617  
      *              int localPort) throws SocketException {
 1618  
      *          throw new SocketException("AFUNIX socket does not support connections to a host/port");
 1619  
      *      }
 1620  
      * 
 1621  
      *      public Socket createSocket(InetAddress address, int port,
 1622  
      *              InetAddress localAddress, int localPort) throws SocketException {
 1623  
      *          throw new SocketException("AFUNIX socket does not support connections to a host/port");
 1624  
      *      }
 1625  
      *  }
 1626  
      * </pre></code></blockquote>
 1627  
      * </p>
 1628  
      * 
 1629  
      * @param socketFactory
 1630  
      *            The socketFactory value.
 1631  
      * 
 1632  
      * @see <a href="http://code.google.com/p/junixsocket">junixsocket
 1633  
      *      Project</a>
 1634  
      */
 1635  
     public void setSocketFactory(final SocketFactory socketFactory) {
 1636  13
         final SocketFactory old = mySocketFactory;
 1637  
 
 1638  13
         if (socketFactory == null) {
 1639  2
             mySocketFactory = SocketFactory.getDefault();
 1640  
         }
 1641  
         else {
 1642  11
             mySocketFactory = socketFactory;
 1643  
         }
 1644  
 
 1645  13
         myPropSupport.firePropertyChange("socketFactory", old, mySocketFactory);
 1646  13
     }
 1647  
 
 1648  
     /**
 1649  
      * Sets the thread factory for managing connections to the new value.
 1650  
      * 
 1651  
      * @param factory
 1652  
      *            The thread factory for managing connections.
 1653  
      */
 1654  
     public void setThreadFactory(final ThreadFactory factory) {
 1655  2
         final ThreadFactory old = myThreadFactory;
 1656  
 
 1657  2
         myThreadFactory = factory;
 1658  
 
 1659  2
         myPropSupport.firePropertyChange("threadFactory", old, myThreadFactory);
 1660  2
     }
 1661  
 
 1662  
     /**
 1663  
      * Sets if the {@link java.net.Socket#setKeepAlive(boolean) SO_KEEPALIVE}
 1664  
      * socket option is set.
 1665  
      * <p>
 1666  
      * Defaults to true, e.g., use SO_KEEPALIVE.
 1667  
      * </p>
 1668  
      * 
 1669  
      * @param usingSoKeepalive
 1670  
      *            The new value for using SO_KEEPALIVE.
 1671  
      */
 1672  
     public void setUsingSoKeepalive(final boolean usingSoKeepalive) {
 1673  4
         final boolean old = myUsingSoKeepalive;
 1674  
 
 1675  4
         myUsingSoKeepalive = usingSoKeepalive;
 1676  
 
 1677  4
         myPropSupport.firePropertyChange("usingSoKeepalive", old,
 1678  
                 myUsingSoKeepalive);
 1679  4
     }
 1680  
 
 1681  
     /**
 1682  
      * Reads the serialized configuration and sets the transient field to known
 1683  
      * values.
 1684  
      * 
 1685  
      * @param stream
 1686  
      *            The stream to read from.
 1687  
      * @throws IOException
 1688  
      *             On a failure reading from the stream.
 1689  
      * @throws ClassNotFoundException
 1690  
      *             On a failure locating a type in the stream.
 1691  
      */
 1692  
     private void readObject(final java.io.ObjectInputStream stream)
 1693  
             throws IOException, ClassNotFoundException {
 1694  1
         stream.defaultReadObject();
 1695  
 
 1696  1
         myExecutor = null;
 1697  1
         mySocketFactory = null;
 1698  1
         myThreadFactory = null;
 1699  1
     }
 1700  
 
 1701  
     /**
 1702  
      * Updates this configurations value based on the field's descriptor, name
 1703  
      * and value.
 1704  
      * 
 1705  
      * @param descriptor
 1706  
      *            The field's descriptor.
 1707  
      * @param propName
 1708  
      *            The name of the field.
 1709  
      * @param propValue
 1710  
      *            The value for the field.
 1711  
      * @throws IllegalArgumentException
 1712  
      *             On a number of errors.
 1713  
      */
 1714  
     @SuppressWarnings({ "unchecked", "rawtypes" })
 1715  
     private void updateFieldValue(final PropertyDescriptor descriptor,
 1716  
             final String propName, final String propValue)
 1717  
             throws IllegalArgumentException {
 1718  
 
 1719  
         try {
 1720  80
             final Class<?> fieldType = descriptor.getPropertyType();
 1721  80
             final Method writer = descriptor.getWriteMethod();
 1722  
             PropertyEditor editor;
 1723  80
             final Class<?> editorClass = descriptor.getPropertyEditorClass();
 1724  80
             if (editorClass != null) {
 1725  45
                 editor = (PropertyEditor) editorClass.newInstance();
 1726  
             }
 1727  
             else {
 1728  35
                 editor = PropertyEditorManager.findEditor(fieldType);
 1729  
             }
 1730  
 
 1731  80
             if (editor != null) {
 1732  80
                 final Method reader = descriptor.getReadMethod();
 1733  80
                 editor.setValue(reader.invoke(this));
 1734  80
                 editor.setAsText(propValue);
 1735  78
                 writer.invoke(this, editor.getValue());
 1736  78
             }
 1737  0
             else if (fieldType.isEnum()) {
 1738  0
                 final Class<? extends Enum> c = (Class<? extends Enum>) fieldType;
 1739  0
                 writer.invoke(this, Enum.valueOf(c, propValue));
 1740  0
             }
 1741  
             else {
 1742  0
                 throw new IllegalArgumentException("The '" + propName
 1743  
                         + "' parameter could not be set " + "to the value '"
 1744  
                         + propValue + "'. No editor available.");
 1745  
 
 1746  
             }
 1747  
         }
 1748  0
         catch (final InstantiationException e) {
 1749  0
             throw new IllegalArgumentException(e);
 1750  
         }
 1751  0
         catch (final IllegalAccessException e) {
 1752  0
             throw new IllegalArgumentException(e);
 1753  
         }
 1754  0
         catch (final InvocationTargetException e) {
 1755  0
             throw new IllegalArgumentException(e);
 1756  78
         }
 1757  78
     }
 1758  
 }