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 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 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 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 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 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 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 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 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 private ReadPreference myDefaultReadPreference = ReadPreference.PRIMARY;
155
156 /** The executor for responses from the database. */
157 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 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 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 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 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 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 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 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 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 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 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 private List<InetSocketAddress> myServers = new ArrayList<InetSocketAddress>();
278
279 /** The socket factory for creating sockets. */
280 private transient SocketFactory mySocketFactory = null;
281
282 /** The factory for creating threads to handle connections. */
283 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 private boolean myUsingSoKeepalive = true;
293
294 /**
295 * Creates a new MongoClientConfiguration.
296 */
297 public MongoClientConfiguration() {
298 super();
299
300 myThreadFactory = Executors.defaultThreadFactory();
301 myCredentials = new ConcurrentHashMap<String, Credential>();
302 myPropSupport = new PropertyChangeSupport(this);
303 }
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 this();
313
314 for (final InetSocketAddress server : servers) {
315 addServer(server);
316 }
317 }
318
319 /**
320 * Creates a new MongoClientConfiguration.
321 *
322 * @param other
323 * The configuration to copy.
324 */
325 public MongoClientConfiguration(final MongoClientConfiguration other) {
326 this();
327
328 myAutoDiscoverServers = other.isAutoDiscoverServers();
329 myConnectionModel = other.getConnectionModel();
330 myConnectTimeout = other.getConnectTimeout();
331 myDefaultDatabase = other.getDefaultDatabase();
332 myDefaultDurability = other.getDefaultDurability();
333 myDefaultReadPreference = other.getDefaultReadPreference();
334 myExecutor = other.getExecutor();
335 myLockType = other.getLockType();
336 myMaxCachedStringEntries = other.getMaxCachedStringEntries();
337 myMaxCachedStringLength = other.getMaxCachedStringLength();
338 myMaxConnectionCount = other.getMaxConnectionCount();
339 myMaxIdleTickCount = other.getMaxIdleTickCount();
340 myMaxPendingOperationsPerConnection = other
341 .getMaxPendingOperationsPerConnection();
342 myMaxSecondaryLag = other.getMaxSecondaryLag();
343 myMinConnectionCount = other.getMinConnectionCount();
344 myReadTimeout = other.getReadTimeout();
345 myReconnectTimeout = other.getReconnectTimeout();
346 mySocketFactory = other.getSocketFactory();
347 myThreadFactory = other.getThreadFactory();
348 myUsingSoKeepalive = other.isUsingSoKeepalive();
349
350 for (final Credential credential : other.getCredentials()) {
351 addCredential(credential);
352 }
353 for (final InetSocketAddress addr : other.getServerAddresses()) {
354 addServer(addr);
355 }
356 }
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 this(mongoDbUri, Durability.ACK);
376 }
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 this(new MongoDbUri(mongoDbUri));
396 }
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 this();
418
419 myDefaultDurability = defaultDurability;
420 for (final String host : mongoDbUri.getHosts()) {
421 addServer(host);
422 }
423 if (myServers.isEmpty()) {
424 throw new IllegalArgumentException(
425 "Must provide at least 1 host to connect to.");
426 }
427 if (!mongoDbUri.getDatabase().isEmpty()) {
428 setDefaultDatabase(mongoDbUri.getDatabase());
429 }
430
431 final Map<String, String> parameters = mongoDbUri.getParsedOptions();
432 final Map<String, String> renames = new HashMap<String, String>();
433
434 // Renames for the standard names.
435 final Iterator<Map.Entry<String, String>> iter = parameters.entrySet()
436 .iterator();
437 while (iter.hasNext()) {
438 final Map.Entry<String, String> entry = iter.next();
439 final String name = entry.getKey();
440 if ("sockettimeoutms".equals(entry.getKey())) {
441 renames.put("readtimeout", entry.getValue());
442 iter.remove();
443 }
444 else if ("usesokeepalive".equals(name)) {
445 renames.put("usingsokeepalive", entry.getValue());
446 iter.remove();
447 }
448 else if ("minpoolsize".equals(name)) {
449 renames.put("minconnectioncount", entry.getValue());
450 iter.remove();
451 }
452 else if ("maxpoolsize".equals(name)) {
453 renames.put("maxconnectioncount", entry.getValue());
454 iter.remove();
455 }
456 else if (name.endsWith("ms")) {
457 final String newName = name.substring(0,
458 name.length() - "ms".length());
459 renames.put(newName, entry.getValue());
460 iter.remove();
461 }
462 }
463 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 parameters.keySet().removeAll(CredentialEditor.MONGODB_URI_FIELDS);
468 parameters.put("credentials", mongoDbUri.toString());
469
470 if (parameters.keySet().removeAll(DurabilityEditor.MONGODB_URI_FIELDS)) {
471 parameters.put("defaultdurability", mongoDbUri.toString());
472 }
473 if (parameters.keySet().removeAll(
474 ReadPreferenceEditor.MONGODB_URI_FIELDS)) {
475 parameters.put("defaultreadpreference", mongoDbUri.toString());
476 }
477
478 // Special handling for the SSL parameter.
479 final String sslValue = parameters.remove("ssl");
480 if (mongoDbUri.isUseSsl() || (sslValue != null)) {
481 if (mongoDbUri.isUseSsl() || Boolean.parseBoolean(sslValue)) {
482 try {
483 setSocketFactory((SocketFactory) Class
484 .forName(
485 "com.allanbank.mongodb.extensions.tls.TlsSocketFactory")
486 .newInstance());
487 }
488 catch (final Throwable error) {
489 setSocketFactory(SSLSocketFactory.getDefault());
490 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 }
494 }
495 else {
496 setSocketFactory(null);
497 }
498 }
499
500 try {
501 final BeanInfo info = Introspector
502 .getBeanInfo(MongoClientConfiguration.class);
503 final PropertyDescriptor[] descriptors = info
504 .getPropertyDescriptors();
505 final Map<String, PropertyDescriptor> descriptorsByName = new HashMap<String, PropertyDescriptor>();
506 for (final PropertyDescriptor descriptor : descriptors) {
507 descriptorsByName
508 .put(descriptor.getName().toLowerCase(Locale.US),
509 descriptor);
510 }
511
512 for (final Map.Entry<String, String> param : parameters.entrySet()) {
513 final String propName = param.getKey();
514 final String propValue = param.getValue();
515
516 final PropertyDescriptor descriptor = descriptorsByName
517 .get(propName);
518 if (descriptor != null) {
519 try {
520 updateFieldValue(descriptor, propName, propValue);
521 }
522 catch (final NumberFormatException nfe) {
523 throw new IllegalArgumentException("The '" + propName
524 + "' parameter must have a numeric value not '"
525 + propValue + "'.", nfe);
526 }
527 catch (final Exception e) {
528 throw new IllegalArgumentException("The '" + propName
529 + "' parameter's editor could not be set '"
530 + propValue + "'.", e);
531 }
532 }
533 else if ("uuidrepresentation".equals(propName)) {
534 LOG.info("Changing the UUID representation is not supported.");
535 }
536 else if ("replicaset".equals(propName)) {
537 LOG.info("Not validating the replica set name is '{}'.",
538 propValue);
539 }
540 else {
541 LOG.info("Unknown property '{}' and value '{}'.", propName,
542 propValue);
543 }
544 }
545 }
546 catch (final IntrospectionException e) {
547 throw new IllegalArgumentException(
548 "Could not introspect on the MongoClientConfiguration.");
549 }
550 }
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 final List<Credential> old = new ArrayList<Credential>(
565 myCredentials.values());
566
567 try {
568 credentials.loadAuthenticator();
569
570 final Credential previous = myCredentials.putIfAbsent(
571 credentials.getDatabase(), credentials);
572 if (previous != null) {
573 throw new IllegalArgumentException(
574 "There can only be one set of credentials for each database.");
575 }
576 }
577 catch (final ClassNotFoundException cnfe) {
578 throw new IllegalArgumentException(
579 "Could not load the credentials authenticator.", cnfe);
580 }
581 catch (final InstantiationException ie) {
582 throw new IllegalArgumentException(
583 "Could not load the credentials authenticator.", ie);
584 }
585 catch (final IllegalAccessException iae) {
586 throw new IllegalArgumentException(
587 "Could not load the credentials authenticator.", iae);
588 }
589
590 myPropSupport.firePropertyChange("credentials", old,
591 new ArrayList<Credential>(myCredentials.values()));
592 }
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 addCredential(credentials.build());
607 }
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 myPropSupport.addPropertyChangeListener(listener);
619 }
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 myPropSupport.addPropertyChangeListener(propertyName, listener);
635 }
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 final List<InetSocketAddress> old = new ArrayList<InetSocketAddress>(
645 myServers);
646
647 myServers.add(server);
648
649 myPropSupport.firePropertyChange("servers", old,
650 Collections.unmodifiableList(myServers));
651 }
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 addServer(ServerNameUtils.parse(server));
661 }
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 if (myLegacyCredential != null) {
681 myCredentials.remove(myLegacyCredential.getDatabase());
682 }
683 myLegacyCredential = Credential.builder().userName(userName)
684 .password(password.toCharArray())
685 .database(getDefaultDatabase()).mongodbCR().build();
686
687 addCredential(myLegacyCredential);
688 }
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 addCredential(Credential.builder().userName(userName)
709 .password(password.toCharArray()).database(ADMIN_DB_NAME)
710 .mongodbCR().build());
711 }
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 MongoClientConfiguration clone = null;
723 try {
724 clone = (MongoClientConfiguration) super.clone();
725
726 clone.myCredentials = new ConcurrentHashMap<String, Credential>();
727 for (final Credential credential : getCredentials()) {
728 clone.addCredential(credential);
729 }
730
731 clone.myServers = new ArrayList<InetSocketAddress>();
732 for (final InetSocketAddress addr : getServerAddresses()) {
733 clone.addServer(addr);
734 }
735
736 clone.myPropSupport = new PropertyChangeSupport(clone);
737 }
738 catch (final CloneNotSupportedException shouldNotHappen) {
739 clone = new MongoClientConfiguration(this);
740 }
741 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 if (!myCredentials.isEmpty()) {
989 final Credential credentials = myCredentials.entrySet().iterator()
990 .next().getValue();
991 try {
992 final MessageDigest md5 = MessageDigest.getInstance("MD5");
993 final byte[] digest = md5.digest((credentials.getUserName()
994 + ":mongo:" + new String(credentials.getPassword()))
995 .getBytes(UTF8));
996
997 return IOUtils.toHex(digest);
998 }
999 catch (final NoSuchAlgorithmException e) {
1000 throw new MongoDbAuthenticationException(e);
1001 }
1002
1003 }
1004 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 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 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 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 final List<String> servers = new ArrayList<String>(myServers.size());
1049 for (final InetSocketAddress addr : myServers) {
1050 servers.add(ServerNameUtils.normalize(addr));
1051 }
1052 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 if (mySocketFactory == null) {
1069 mySocketFactory = SocketFactory.getDefault();
1070 }
1071 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 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 if (!myCredentials.isEmpty()) {
1093 final Credential credentials = myCredentials.entrySet().iterator()
1094 .next().getValue();
1095 return credentials.getUserName();
1096 }
1097 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 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 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 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 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 myPropSupport.removePropertyChangeListener(listener);
1161 }
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 myPropSupport.removePropertyChangeListener(propertyName, listener);
1177 }
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 final boolean old = myAutoDiscoverServers;
1191
1192 myAutoDiscoverServers = autoDiscoverServers;
1193
1194 myPropSupport.firePropertyChange("autoDiscoverServers", old,
1195 myAutoDiscoverServers);
1196 }
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 final ConnectionModel old = myConnectionModel;
1210
1211 myConnectionModel = connectionModel;
1212
1213 myPropSupport.firePropertyChange("connectionModel", old,
1214 myConnectionModel);
1215 }
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 final int old = myConnectTimeout;
1227
1228 myConnectTimeout = connectTimeout;
1229
1230 myPropSupport.firePropertyChange("connectTimeout", old,
1231 myConnectTimeout);
1232 }
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 final List<Credential> old = new ArrayList<Credential>(
1247 myCredentials.values());
1248
1249 myCredentials.clear();
1250 for (final Credential credential : credentials) {
1251 addCredential(credential);
1252 }
1253
1254 myPropSupport.firePropertyChange("credentials", old,
1255 new ArrayList<Credential>(myCredentials.values()));
1256 }
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 final String old = myDefaultDatabase;
1276
1277 myDefaultDatabase = defaultDatabase;
1278
1279 if (myLegacyCredential != null) {
1280 final List<Credential> oldCredentials = new ArrayList<Credential>(
1281 myCredentials.values());
1282 myCredentials.remove(myLegacyCredential.getDatabase());
1283 myPropSupport.firePropertyChange("credentials", oldCredentials,
1284 new ArrayList<Credential>(myCredentials.values()));
1285
1286 myLegacyCredential = Credential.builder()
1287 .userName(myLegacyCredential.getUserName())
1288 .password(myLegacyCredential.getPassword())
1289 .database(defaultDatabase).mongodbCR().build();
1290 addCredential(myLegacyCredential);
1291 }
1292
1293 myPropSupport.firePropertyChange("defaultDatabase", old,
1294 myDefaultDatabase);
1295 }
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 final Durability old = myDefaultDurability;
1306
1307 myDefaultDurability = defaultDurability;
1308
1309 myPropSupport.firePropertyChange("defaultDurability", old,
1310 myDefaultDurability);
1311 }
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 final ReadPreference old = myDefaultReadPreference;
1325
1326 if (defaultReadPreference == null) {
1327 myDefaultReadPreference = ReadPreference.PRIMARY;
1328 }
1329 else {
1330 myDefaultReadPreference = defaultReadPreference;
1331 }
1332
1333 myPropSupport.firePropertyChange("defaultReadPreference", old,
1334 myDefaultReadPreference);
1335 }
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 final Executor old = myExecutor;
1356
1357 myExecutor = executor;
1358
1359 myPropSupport.firePropertyChange("executor", old, myExecutor);
1360 }
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 final LockType old = myLockType;
1374
1375 myLockType = lockType;
1376
1377 myPropSupport.firePropertyChange("lockType", old, myLockType);
1378 }
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 final int old = myMaxCachedStringEntries;
1396
1397 myMaxCachedStringEntries = maxCacheEntries;
1398
1399 myPropSupport.firePropertyChange("maxCachedStringEntries", old,
1400 myMaxCachedStringEntries);
1401 }
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 final int old = myMaxCachedStringLength;
1420
1421 myMaxCachedStringLength = maxlength;
1422
1423 myPropSupport.firePropertyChange("maxCachedStringLength", old,
1424 myMaxCachedStringLength);
1425 }
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 final int old = myMaxConnectionCount;
1444
1445 myMaxConnectionCount = maxConnectionCount;
1446
1447 myPropSupport.firePropertyChange("maxConnectionCount", old,
1448 myMaxConnectionCount);
1449 }
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 final int old = myMaxIdleTickCount;
1461
1462 myMaxIdleTickCount = idleTickCount;
1463
1464 myPropSupport.firePropertyChange("maxIdleTickCount", old,
1465 myMaxIdleTickCount);
1466 }
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 final int old = myMaxPendingOperationsPerConnection;
1482
1483 myMaxPendingOperationsPerConnection = maxPendingOperationsPerConnection;
1484
1485 myPropSupport.firePropertyChange("maxPendingOperationsPerConnection",
1486 old, myMaxPendingOperationsPerConnection);
1487 }
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 final long old = myMaxSecondaryLag;
1504
1505 myMaxSecondaryLag = maxSecondaryLag;
1506
1507 myPropSupport.firePropertyChange("maxSecondaryLag", Long.valueOf(old),
1508 Long.valueOf(myMaxSecondaryLag));
1509 }
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 final int old = myMinConnectionCount;
1520
1521 myMinConnectionCount = minimumConnectionCount;
1522
1523 myPropSupport.firePropertyChange("minConnectionCount", old,
1524 myMinConnectionCount);
1525 }
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 final int old = myReadTimeout;
1534
1535 myReadTimeout = readTimeout;
1536
1537 myPropSupport.firePropertyChange("readTimeout", old, myReadTimeout);
1538 }
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 final int old = myReconnectTimeout;
1550
1551 myReconnectTimeout = connectTimeout;
1552
1553 myPropSupport.firePropertyChange("reconnectTimeout", old,
1554 myReconnectTimeout);
1555 }
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 final List<InetSocketAddress> old = new ArrayList<InetSocketAddress>(
1565 myServers);
1566
1567 myServers.clear();
1568 if (servers != null) {
1569 for (final InetSocketAddress server : servers) {
1570 addServer(server);
1571 }
1572 }
1573
1574 myPropSupport.firePropertyChange("servers", old,
1575 Collections.unmodifiableList(myServers));
1576 }
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 final SocketFactory old = mySocketFactory;
1637
1638 if (socketFactory == null) {
1639 mySocketFactory = SocketFactory.getDefault();
1640 }
1641 else {
1642 mySocketFactory = socketFactory;
1643 }
1644
1645 myPropSupport.firePropertyChange("socketFactory", old, mySocketFactory);
1646 }
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 final ThreadFactory old = myThreadFactory;
1656
1657 myThreadFactory = factory;
1658
1659 myPropSupport.firePropertyChange("threadFactory", old, myThreadFactory);
1660 }
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 final boolean old = myUsingSoKeepalive;
1674
1675 myUsingSoKeepalive = usingSoKeepalive;
1676
1677 myPropSupport.firePropertyChange("usingSoKeepalive", old,
1678 myUsingSoKeepalive);
1679 }
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 stream.defaultReadObject();
1695
1696 myExecutor = null;
1697 mySocketFactory = null;
1698 myThreadFactory = null;
1699 }
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 final Class<?> fieldType = descriptor.getPropertyType();
1721 final Method writer = descriptor.getWriteMethod();
1722 PropertyEditor editor;
1723 final Class<?> editorClass = descriptor.getPropertyEditorClass();
1724 if (editorClass != null) {
1725 editor = (PropertyEditor) editorClass.newInstance();
1726 }
1727 else {
1728 editor = PropertyEditorManager.findEditor(fieldType);
1729 }
1730
1731 if (editor != null) {
1732 final Method reader = descriptor.getReadMethod();
1733 editor.setValue(reader.invoke(this));
1734 editor.setAsText(propValue);
1735 writer.invoke(this, editor.getValue());
1736 }
1737 else if (fieldType.isEnum()) {
1738 final Class<? extends Enum> c = (Class<? extends Enum>) fieldType;
1739 writer.invoke(this, Enum.valueOf(c, propValue));
1740 }
1741 else {
1742 throw new IllegalArgumentException("The '" + propName
1743 + "' parameter could not be set " + "to the value '"
1744 + propValue + "'. No editor available.");
1745
1746 }
1747 }
1748 catch (final InstantiationException e) {
1749 throw new IllegalArgumentException(e);
1750 }
1751 catch (final IllegalAccessException e) {
1752 throw new IllegalArgumentException(e);
1753 }
1754 catch (final InvocationTargetException e) {
1755 throw new IllegalArgumentException(e);
1756 }
1757 }
1758 }