@Test
  @SuppressWarnings("deprecation")
  public void testToClientOptions() throws UnknownHostException {
    MongoOptions options = new MongoOptions();
    options.description = "my client";
    options.fsync = true;
    options.readPreference = ReadPreference.secondary();
    options.requiredReplicaSetName = "test";
    options.cursorFinalizerEnabled = false;
    options.alwaysUseMBeans = true;
    options.connectTimeout = 100;
    options.maxWaitTime = 500;
    options.socketKeepAlive = true;
    options.threadsAllowedToBlockForConnectionMultiplier = 10;

    MongoClientOptions clientOptions = options.toClientOptions();

    assertEquals(options.requiredReplicaSetName, clientOptions.getRequiredReplicaSetName());
    assertEquals(options.description, clientOptions.getDescription());
    assertEquals(WriteConcern.ACKNOWLEDGED.withFsync(true), clientOptions.getWriteConcern());
    assertEquals(0, clientOptions.getMinConnectionsPerHost());
    assertEquals(10, clientOptions.getConnectionsPerHost());
    assertEquals(100, clientOptions.getConnectTimeout());
    assertEquals(500, clientOptions.getMaxWaitTime());
    assertEquals(ReadPreference.secondary(), clientOptions.getReadPreference());
    assertEquals(10, clientOptions.getThreadsAllowedToBlockForConnectionMultiplier());
    assertTrue(clientOptions.isSocketKeepAlive());
    assertFalse(clientOptions.isSslEnabled());
    assertEquals(options.dbDecoderFactory, clientOptions.getDbDecoderFactory());
    assertEquals(options.dbEncoderFactory, clientOptions.getDbEncoderFactory());
    assertEquals(15, clientOptions.getLocalThreshold());
    assertTrue(clientOptions.isAlwaysUseMBeans());
    assertFalse(clientOptions.isCursorFinalizerEnabled());
  }
示例#2
0
  /**
   * Determines the read preference that should be used for the given command.
   *
   * @param command the {@link DBObject} representing the command
   * @param requestedPreference the preference requested by the client.
   * @return the read preference to use for the given command. It will never return {@code null}.
   * @see com.mongodb.ReadPreference
   */
  ReadPreference getCommandReadPreference(DBObject command, ReadPreference requestedPreference) {
    String comString = command.keySet().iterator().next();

    if (comString.equals("getnonce") || comString.equals("authenticate")) {
      return ReadPreference.primaryPreferred();
    }

    boolean primaryRequired;

    // explicitly check mapreduce commands are inline
    if (comString.equals("mapreduce")) {
      Object out = command.get("out");
      if (out instanceof BSONObject) {
        BSONObject outMap = (BSONObject) out;
        primaryRequired = outMap.get("inline") == null;
      } else primaryRequired = true;
    } else {
      primaryRequired = !_obedientCommands.contains(comString.toLowerCase());
    }

    if (primaryRequired) {
      return ReadPreference.primary();
    } else if (requestedPreference == null) {
      return ReadPreference.primary();
    } else {
      return requestedPreference;
    }
  }
示例#3
0
 // Only append $readPreference meta-operator if connected to a mongos, read preference is not
 // primary
 // or secondary preferred,
 // and command is an instance of BasicDBObject.  The last condition is unfortunate, but necessary
 // in case
 // the encoder is not capable of encoding a BasicDBObject
 // Due to issues with compatibility between different versions of mongos, also wrap the command in
 // a
 // $query field, so that the $readPreference is not rejected
 private DBObject wrapCommand(DBObject cmd, final ReadPreference readPrefs) {
   if (getMongo().isMongosConnection()
       && !(ReadPreference.primary().equals(readPrefs)
           || ReadPreference.secondaryPreferred().equals(readPrefs))
       && cmd instanceof BasicDBObject) {
     cmd =
         new BasicDBObject("$query", cmd)
             .append(QueryOpBuilder.READ_PREFERENCE_META_OPERATOR, readPrefs.toDBObject());
   }
   return cmd;
 }
  @Test
  @SuppressWarnings("deprecation")
  public void testTaggedPreference() {
    ReadPreference readPreference =
        new ReadPreference.TaggedReadPreference(new BasicDBObject("dc", "ny"));
    for (int i = 0; i < 1000; i++) {
      nodeSet.add(readPreference.getNode(_set));
    }

    expectedNodeSet.addAll(Arrays.asList(_secondary1, _secondary2));
    assertEquals(expectedNodeSet, nodeSet);
  }
示例#5
0
  private OutMessage(
      final DBCollection collection,
      final Mongo m,
      OpCode opCode,
      final DBEncoder enc,
      final DBObject query,
      final int options,
      final ReadPreference readPref) {
    _collection = collection;
    _mongo = m;
    _encoder = enc;

    _buffer = _mongo._bufferPool.get();
    _buffer.reset();
    set(_buffer);

    _id = REQUEST_ID.getAndIncrement();
    _opCode = opCode;

    writeMessagePrologue(opCode);

    if (query == null) {
      _query = null;
      _queryOptions = 0;
    } else {
      _query = query;

      int allOptions = options;
      if (readPref != null && readPref.isSlaveOk()) {
        allOptions |= Bytes.QUERYOPTION_SLAVEOK;
      }

      _queryOptions = allOptions;
    }
  }
  @Test
  @SuppressWarnings("deprecation")
  public void testGetterSetters() throws Exception {

    MongoOptions options = new MongoOptions();

    options.setConnectionsPerHost(100);
    options.setThreadsAllowedToBlockForConnectionMultiplier(101);
    options.setMaxWaitTime(102);
    options.setConnectTimeout(103);
    options.setSocketTimeout(104);
    options.setSocketKeepAlive(true);
    options.setSafe(true);
    options.setW(106);
    options.setWtimeout(107);
    options.setFsync(true);
    options.setJ(false);
    options.setDbDecoderFactory(null);
    options.setDbEncoderFactory(null);
    options.setDescription("very cool");
    options.setReadPreference(ReadPreference.secondary());
    options.setSocketFactory(SSLSocketFactory.getDefault());
    options.setAlwaysUseMBeans(true);
    options.setCursorFinalizerEnabled(false);
    options.requiredReplicaSetName = "set1";

    assertEquals(options.getConnectionsPerHost(), 100);
    assertEquals(options.getThreadsAllowedToBlockForConnectionMultiplier(), 101);
    assertEquals(options.getMaxWaitTime(), 102);
    assertEquals(options.getConnectTimeout(), 103);
    assertEquals(options.getSocketTimeout(), 104);
    assertEquals(options.isSocketKeepAlive(), true);
    assertEquals(options.isSafe(), true);
    assertEquals(options.getW(), 106);
    assertEquals(options.getWtimeout(), 107);
    assertEquals(options.isFsync(), true);
    assertEquals(options.isJ(), false);
    assertEquals(options.getDbDecoderFactory(), null);
    assertEquals(options.getDbEncoderFactory(), null);
    assertEquals(options.getDescription(), "very cool");
    assertEquals(options.getReadPreference(), ReadPreference.secondary());
    assertEquals(options.isAlwaysUseMBeans(), true);
    assertEquals(options.getSocketFactory(), options.socketFactory);
    assertEquals(options.isCursorFinalizerEnabled(), false);
    assertEquals(options.getRequiredReplicaSetName(), "set1");
  }
  @Test
  public void testPrimaryPreferredWithNoPrimary() {
    for (int i = 0; i < 1000; i++) {
      nodeSet.add(ReadPreference.primaryPreferred().getNode(_setNoPrimary));
    }

    expectedNodeSet.addAll(Arrays.asList(_secondary1, _secondary2));
    assertEquals(expectedNodeSet, nodeSet);
  }
  @Test
  public void testNearest() {
    for (int i = 0; i < 1000; i++) {
      nodeSet.add(ReadPreference.nearest().getNode(_set));
    }

    expectedNodeSet.addAll(Arrays.asList(_primary, _secondary1));
    assertEquals(expectedNodeSet, nodeSet);
  }
示例#9
0
 static OutMessage query(
     DBCollection collection,
     int options,
     int numToSkip,
     int batchSize,
     DBObject query,
     DBObject fields) {
   return query(
       collection, options, numToSkip, batchSize, query, fields, ReadPreference.primary());
 }
  @Test
  public void testTaggedNearest() {
    final TaggableReadPreference taggedNearestReadPreference =
        ReadPreference.nearest(new BasicDBObject("dc", "ny"));
    for (int i = 0; i < 1000; i++) {
      nodeSet.add(taggedNearestReadPreference.getNode(_set));
    }

    expectedNodeSet.addAll(Arrays.asList(_primary, _secondary1));
    assertEquals(expectedNodeSet, nodeSet);
  }
  @Test
  public void testTaggedPrimaryPreferredWithNoPrimary() {
    final TaggableReadPreference readPreference =
        ReadPreference.primaryPreferred(new BasicDBObject("dc", "ny"));
    for (int i = 0; i < 1000; i++) {
      nodeSet.add(readPreference.getNode(_setNoPrimary));
    }

    expectedNodeSet.addAll(Arrays.asList(_secondary1, _secondary2));
    assertEquals(expectedNodeSet, nodeSet);
  }
示例#12
0
  @Override
  protected MongoClient createInstance() throws Exception {
    MongoClient mongo = initMongo();

    // 设定主从分离
    if (readSecondary) {
      mongo.setReadPreference(ReadPreference.secondaryPreferred());
    }
    // 设定写策略
    mongo.setWriteConcern(writeConcern);
    return mongo;
  }
 private ReadPreference buildReadPreference(
     final String readPreferenceType,
     final DBObject firstTagSet,
     final List<DBObject> remainingTagSets,
     final Boolean slaveOk) {
   if (readPreferenceType != null) {
     if (firstTagSet == null) {
       return ReadPreference.valueOf(readPreferenceType);
     } else {
       return ReadPreference.valueOf(
           readPreferenceType,
           firstTagSet,
           remainingTagSets.toArray(new DBObject[remainingTagSets.size()]));
     }
   } else if (slaveOk != null) {
     if (slaveOk.equals(Boolean.TRUE)) {
       return ReadPreference.secondaryPreferred();
     }
   }
   return null;
 }
  @Test
  public void testTaggedSecondaryPreferredWithNoSecondaryMatch() {
    final TaggableReadPreference nonMatchingReadPreference =
        ReadPreference.secondaryPreferred(new BasicDBObject("dc", "ca"));

    for (int i = 0; i < 1000; i++) {
      nodeSet.add(nonMatchingReadPreference.getNode(_set));
    }

    expectedNodeSet.addAll(Arrays.asList(_primary));
    assertEquals(expectedNodeSet, nodeSet);
  }
示例#15
0
  @SuppressWarnings("deprecation")
  @Test
  public void testApplyOptions() throws UnknownHostException {
    MongoOptions options = new MongoOptions();

    // test defaults
    Mongo m = new Mongo("localhost", options);
    assertEquals(ReadPreference.primary(), m.getReadPreference());
    assertEquals(WriteConcern.NORMAL, m.getWriteConcern());
    assertEquals(0, m.getOptions() & Bytes.QUERYOPTION_SLAVEOK);
    m.close();

    // test setting options
    options.setReadPreference(ReadPreference.nearest());
    options.slaveOk = true;
    options.safe = true;

    m = new Mongo("localhost", options);
    assertEquals(ReadPreference.nearest(), m.getReadPreference());
    assertEquals(WriteConcern.SAFE, m.getWriteConcern());
    assertEquals(Bytes.QUERYOPTION_SLAVEOK, m.getOptions() & Bytes.QUERYOPTION_SLAVEOK);
    m.close();
  }
  @Test
  @SuppressWarnings("deprecation")
  public void testCopy() throws Exception {

    MongoOptions options = new MongoOptions();

    options.connectionsPerHost = 100;
    options.threadsAllowedToBlockForConnectionMultiplier = 101;
    options.maxWaitTime = 102;
    options.connectTimeout = 103;
    options.socketTimeout = 104;
    options.socketKeepAlive = true;
    options.safe = true;
    options.w = 106;
    options.wtimeout = 107;
    options.fsync = true;
    options.j = false;
    options.dbDecoderFactory = null;
    options.dbEncoderFactory = null;
    options.description = "cool";
    options.readPreference = ReadPreference.secondary();
    options.cursorFinalizerEnabled = true;
    options.socketFactory = SSLSocketFactory.getDefault();
    options.alwaysUseMBeans = true;
    options.requiredReplicaSetName = "set1";

    MongoOptions copy = options.copy();
    assertEquals(options.connectionsPerHost, copy.connectionsPerHost);
    assertEquals(
        options.threadsAllowedToBlockForConnectionMultiplier,
        copy.threadsAllowedToBlockForConnectionMultiplier);
    assertEquals(options.maxWaitTime, copy.maxWaitTime);
    assertEquals(options.connectTimeout, copy.connectTimeout);
    assertEquals(options.socketTimeout, copy.socketTimeout);
    assertEquals(options.socketKeepAlive, copy.socketKeepAlive);
    assertEquals(options.safe, copy.safe);
    assertEquals(options.w, copy.w);
    assertEquals(options.wtimeout, copy.wtimeout);
    assertEquals(options.fsync, copy.fsync);
    assertEquals(options.j, copy.j);
    assertEquals(options.dbDecoderFactory, copy.dbDecoderFactory);
    assertEquals(options.dbEncoderFactory, copy.dbEncoderFactory);
    assertEquals(options.description, copy.description);
    assertEquals(options.readPreference, copy.readPreference);
    assertEquals(options.alwaysUseMBeans, copy.alwaysUseMBeans);
    assertEquals(options.socketFactory, copy.socketFactory);
    assertEquals(options.requiredReplicaSetName, copy.requiredReplicaSetName);
  }
示例#17
0
  @Test
  public void testGetCollectionNamesToSecondary() throws MongoException, UnknownHostException {
    if (!isReplicaSet(cleanupMongo)) {
      return;
    }

    Mongo mongo =
        new MongoClient(
            Arrays.asList(new ServerAddress("127.0.0.1"), new ServerAddress("127.0.0.1", 27018)));

    try {
      String secondary = getASecondaryAsString(mongo);
      mongo.close();
      mongo = new MongoClient(secondary);
      DB db = mongo.getDB("secondaryTest");
      db.setReadPreference(ReadPreference.secondary());
      db.getCollectionNames();
    } finally {
      mongo.close();
    }
  }
示例#18
0
/**
 * A database connection with internal connection pooling. For most applications, you should have
 * one Mongo instance for the entire JVM.
 *
 * <p>The following are equivalent, and all connect to the local database running on the default
 * port:
 *
 * <pre>
 * Mongo mongo1 = new Mongo();
 * Mongo mongo1 = new Mongo("localhost");
 * Mongo mongo2 = new Mongo("localhost", 27017);
 * Mongo mongo4 = new Mongo(new ServerAddress("localhost"));
 * </pre>
 *
 * <p>You can connect to a <a href="http://www.mongodb.org/display/DOCS/Replica+Sets">replica
 * set</a> using the Java driver by passing a ServerAddress list to the Mongo constructor. For
 * example:
 *
 * <pre>
 * Mongo mongo = new Mongo(Arrays.asList(
 *   new ServerAddress("localhost", 27017),
 *   new ServerAddress("localhost", 27018),
 *   new ServerAddress("localhost", 27019)));
 * </pre>
 *
 * You can connect to a sharded cluster using the same constructor. Mongo will auto-detect whether
 * the servers are a list of replica set members or a list of mongos servers.
 *
 * <p>By default, all read and write operations will be made on the primary, but it's possible to
 * read from secondaries by changing the read preference:
 *
 * <p>
 *
 * <pre>
 * mongo.setReadPreference(ReadPreference.secondary());
 * </pre>
 *
 * By default, write operations will not throw exceptions on failure, but that is easily changed
 * too:
 *
 * <p>
 *
 * <pre>
 * mongo.setWriteConcern(WriteConcern.SAFE);
 * </pre>
 *
 * Note: This class has been superseded by {@code MongoClient}, and may be deprecated in a future
 * release.
 *
 * @see MongoClient
 * @see ReadPreference
 * @see WriteConcern
 */
public class Mongo {

  static Logger logger = Logger.getLogger(Bytes.LOGGER.getName() + ".Mongo");

  // Make sure you don't change the format of these two static variables. A preprocessing regexp
  // is applied and updates the version based on configuration in build.properties.

  /** @deprecated Replaced by <code>Mongo.getMajorVersion()</code> */
  @Deprecated public static final int MAJOR_VERSION = 2;

  /** @deprecated Replaced by <code>Mongo.getMinorVersion()</code> */
  @Deprecated public static final int MINOR_VERSION = 13;

  private static final String FULL_VERSION = "2.13.0-SNAPSHOT";

  static int cleanerIntervalMS;

  private static final String ADMIN_DATABASE_NAME = "admin";

  static {
    cleanerIntervalMS =
        Integer.parseInt(System.getProperty("com.mongodb.cleanerIntervalMS", "1000"));
  }

  /**
   * Gets the major version of this library
   *
   * @return the major version, e.g. 2
   * @deprecated Please use {@link #getVersion()} instead.
   */
  @Deprecated
  public static int getMajorVersion() {
    return MAJOR_VERSION;
  }

  /**
   * Gets the minor version of this library
   *
   * @return the minor version, e.g. 8
   * @deprecated Please use {@link #getVersion()} instead.
   */
  @Deprecated
  public static int getMinorVersion() {
    return MINOR_VERSION;
  }

  /**
   * Connect to the MongoDB instance at the given address, select and return the {@code DB}
   * specified in the {@code DBAddress} parameter.
   *
   * @param addr The details of the server and database to connect to
   * @return the DB requested in the addr parameter.
   * @throws MongoException
   * @deprecated Please use {@link MongoClient#getDB(String)} instead.
   */
  @Deprecated
  public static DB connect(DBAddress addr) {
    return new Mongo(addr).getDB(addr.getDBName());
  }

  /**
   * Creates a Mongo instance based on a (single) mongodb node (localhost, default port)
   *
   * @throws UnknownHostException
   * @throws MongoException
   * @deprecated Replaced by {@link MongoClient#MongoClient()})
   */
  @Deprecated
  public Mongo() throws UnknownHostException {
    this(new ServerAddress());
  }

  /**
   * Creates a Mongo instance based on a (single) mongodb node (default port)
   *
   * @param host server to connect to
   * @throws UnknownHostException if the database host cannot be resolved
   * @throws MongoException
   * @deprecated Replaced by {@link MongoClient#MongoClient(String)}
   */
  @Deprecated
  public Mongo(String host) throws UnknownHostException {
    this(new ServerAddress(host));
  }

  /**
   * Creates a Mongo instance based on a (single) mongodb node (default port)
   *
   * @param host server to connect to
   * @param options default query options
   * @throws UnknownHostException if the database host cannot be resolved
   * @throws MongoException
   * @deprecated Replaced by {@link MongoClient#MongoClient(String, MongoClientOptions)}
   */
  @Deprecated
  public Mongo(String host, MongoOptions options) throws UnknownHostException {
    this(new ServerAddress(host), options);
  }

  /**
   * Creates a Mongo instance based on a (single) mongodb node
   *
   * @param host the database's host address
   * @param port the port on which the database is running
   * @throws UnknownHostException if the database host cannot be resolved
   * @throws MongoException
   * @deprecated Replaced by {@link MongoClient#MongoClient(String, int)}
   */
  @Deprecated
  public Mongo(String host, int port) throws UnknownHostException {
    this(new ServerAddress(host, port));
  }

  /**
   * Creates a Mongo instance based on a (single) mongodb node
   *
   * @see com.mongodb.ServerAddress
   * @param addr the database address
   * @throws MongoException
   * @deprecated Replaced by {@link MongoClient#MongoClient(ServerAddress)}
   */
  @Deprecated
  public Mongo(ServerAddress addr) {
    this(addr, new MongoOptions());
  }

  /**
   * Creates a Mongo instance based on a (single) mongo node using a given ServerAddress
   *
   * @see com.mongodb.ServerAddress
   * @param addr the database address
   * @param options default query options
   * @throws MongoException
   * @deprecated Replaced by {@link MongoClient#MongoClient(ServerAddress, MongoClientOptions)}
   */
  @Deprecated
  public Mongo(ServerAddress addr, MongoOptions options) {
    this(MongoAuthority.direct(addr), options);
  }

  /**
   * Creates a Mongo in paired mode. <br>
   * This will also work for a replica set and will find all members (the master will be used by
   * default).
   *
   * @see com.mongodb.ServerAddress
   * @param left left side of the pair
   * @param right right side of the pair
   * @throws MongoException
   */
  @Deprecated
  public Mongo(ServerAddress left, ServerAddress right) {
    this(left, right, new MongoOptions());
  }

  /**
   * Creates a Mongo connection in paired mode. <br>
   * This will also work for a replica set and will find all members (the master will be used by
   * default).
   *
   * @see com.mongodb.ServerAddress
   * @param left left side of the pair
   * @param right right side of the pair
   * @param options the optional settings for the Mongo instance
   * @throws MongoException
   * @deprecated Please use {@link MongoClient#MongoClient(java.util.List, MongoClientOptions)}
   *     instead.
   */
  @Deprecated
  public Mongo(ServerAddress left, ServerAddress right, MongoOptions options) {
    this(MongoAuthority.dynamicSet(Arrays.asList(left, right)), options);
  }

  /**
   * Creates a Mongo based on a list of replica set members or a list of mongos. It will find all
   * members (the master will be used by default). If you pass in a single server in the list, the
   * driver will still function as if it is a replica set. If you have a standalone server, use the
   * Mongo(ServerAddress) constructor.
   *
   * <p>If this is a list of mongos servers, it will pick the closest (lowest ping time) one to send
   * all requests to, and automatically fail over to the next server if the closest is down.
   *
   * @see com.mongodb.ServerAddress
   * @param seeds Put as many servers as you can in the list and the system will figure out the
   *     rest. This can either be a list of mongod servers in the same replica set or a list of
   *     mongos servers in the same sharded cluster.
   * @throws MongoException
   * @deprecated Replaced by {@link MongoClient#MongoClient(java.util.List)}
   */
  @Deprecated
  public Mongo(List<ServerAddress> seeds) {
    this(seeds, new MongoOptions());
  }

  /**
   * Creates a Mongo based on a list of replica set members or a list of mongos. It will find all
   * members (the master will be used by default). If you pass in a single server in the list, the
   * driver will still function as if it is a replica set. If you have a standalone server, use the
   * Mongo(ServerAddress) constructor.
   *
   * <p>If this is a list of mongos servers, it will pick the closest (lowest ping time) one to send
   * all requests to, and automatically fail over to the next server if the closest is down.
   *
   * @see com.mongodb.ServerAddress
   * @param seeds Put as many servers as you can in the list and the system will figure out the
   *     rest. This can either be a list of mongod servers in the same replica set or a list of
   *     mongos servers in the same sharded cluster.
   * @param options for configuring this Mongo instance
   * @throws MongoException
   * @deprecated Replaced by {@link MongoClient#MongoClient(java.util.List, MongoClientOptions)}
   */
  @Deprecated
  public Mongo(List<ServerAddress> seeds, MongoOptions options) {
    this(MongoAuthority.dynamicSet(seeds), options);
  }

  /**
   * Creates a Mongo described by a URI. If only one address is used it will only connect to that
   * node, otherwise it will discover all nodes. If the URI contains database credentials, the
   * database will be authenticated lazily on first use with those credentials.
   *
   * @param uri the URI to connect to.
   *     <p>examples:
   *     <ul>
   *       <li>mongodb://localhost
   *       <li>mongodb://fred:foobar@localhost/
   *     </ul>
   *
   * @throws MongoException
   * @throws UnknownHostException
   * @dochub connections
   * @deprecated Replaced by {@link MongoClient#MongoClient(MongoClientURI)}
   */
  @Deprecated
  public Mongo(MongoURI uri) throws UnknownHostException {
    this(getMongoAuthorityFromURI(uri), uri.getOptions());
  }

  /**
   * Creates a Mongo based on an authority and options.
   *
   * <p>Note: This constructor is provisional and is subject to change before the final release
   *
   * @param authority the authority
   * @param options the options
   */
  Mongo(MongoAuthority authority, MongoOptions options) {
    logger.info(
        "Creating Mongo instance (driver version "
            + getVersion()
            + ") with authority "
            + authority
            + " and options "
            + options);
    _authority = authority;
    _options = options;
    _applyMongoOptions();

    _connector = new DBTCPConnector(this);

    _connector.start();
    if (_options.cursorFinalizerEnabled) {
      _cleaner = new CursorCleanerThread();
      _cleaner.start();
    } else {
      _cleaner = null;
    }
  }

  /**
   * Gets a database object from this MongoDB instance.
   *
   * @param dbname the name of the database to retrieve
   * @return a DB representing the specified database
   */
  public DB getDB(String dbname) {

    DB db = _dbs.get(dbname);
    if (db != null) return db;

    db = new DBApiLayer(this, dbname, _connector);
    DB temp = _dbs.putIfAbsent(dbname, db);
    if (temp != null) return temp;
    return db;
  }

  /**
   * Returns the list of databases used by the driver since this Mongo instance was created. This
   * may include DBs that exist in the client but not yet on the server.
   *
   * @return a collection of database objects
   */
  public Collection<DB> getUsedDatabases() {
    return _dbs.values();
  }

  /**
   * Gets a list of the names of all databases on the connected server.
   *
   * @return list of database names
   * @throws MongoException
   */
  public List<String> getDatabaseNames() {

    BasicDBObject cmd = new BasicDBObject();
    cmd.put("listDatabases", 1);

    CommandResult res = getDB(ADMIN_DATABASE_NAME).command(cmd, getOptions());
    res.throwOnError();

    List l = (List) res.get("databases");

    List<String> list = new ArrayList<String>();

    for (Object o : l) {
      list.add(((BasicDBObject) o).getString("name"));
    }
    return list;
  }

  /**
   * Drops the database if it exists.
   *
   * @param dbName name of database to drop
   * @throws MongoException
   */
  public void dropDatabase(String dbName) {

    getDB(dbName).dropDatabase();
  }

  /**
   * gets this driver version
   *
   * @return the full version string of this driver, e.g. "2.8.0"
   */
  public String getVersion() {
    return FULL_VERSION;
  }

  /**
   * Get a String for debug purposes.
   *
   * @return a string representing the hosts used in this Mongo instance
   * @deprecated This method is NOT a part of public API and will be dropped in 3.x versions.
   */
  @Deprecated
  public String debugString() {
    return _connector.debugString();
  }

  /**
   * Gets a {@code String} representation of current connection point, i.e. master.
   *
   * @return server address in a host:port form
   */
  public String getConnectPoint() {
    return _connector.getConnectPoint();
  }

  /**
   * Gets the underlying TCP connector
   *
   * @return A DBTCPConnector representing the connection to MongoDB
   * @deprecated {@link DBTCPConnector} is NOT part of the public API. It will be dropped in 3.x
   *     releases.
   */
  @Deprecated
  public DBTCPConnector getConnector() {
    return _connector;
  }

  /**
   * Get the status of the replica set cluster.
   *
   * @return replica set status information
   */
  public ReplicaSetStatus getReplicaSetStatus() {
    return _connector.getReplicaSetStatus();
  }

  /**
   * Gets the address of the current master
   *
   * @return the address
   */
  public ServerAddress getAddress() {
    return _connector.getAddress();
  }

  /**
   * Gets a list of all server addresses used when this Mongo was created
   *
   * @return list of server addresses
   * @throws MongoException
   */
  public List<ServerAddress> getAllAddress() {
    List<ServerAddress> result = _connector.getAllAddress();
    if (result == null) {
      return Arrays.asList(getAddress());
    }
    return result;
  }

  /**
   * Gets the list of server addresses currently seen by this client. This includes addresses
   * auto-discovered from a replica set.
   *
   * @return list of server addresses
   * @throws MongoException
   */
  public List<ServerAddress> getServerAddressList() {
    return _connector.getServerAddressList();
  }

  /**
   * Closes the underlying connector, which in turn closes all open connections. Once called, this
   * Mongo instance can no longer be used.
   */
  public void close() {

    try {
      _connector.close();
    } catch (final Throwable t) {
      /* nada */
    }

    if (_cleaner != null) {
      _cleaner.interrupt();

      try {
        _cleaner.join();
      } catch (InterruptedException e) {
        // end early
      }
    }
  }

  /**
   * Sets the write concern for this database. Will be used as default for writes to any collection
   * in any database. See the documentation for {@link WriteConcern} for more information.
   *
   * @param concern write concern to use
   */
  public void setWriteConcern(WriteConcern concern) {
    _concern = concern;
  }

  /**
   * Gets the default write concern
   *
   * @return the default write concern
   */
  public WriteConcern getWriteConcern() {
    return _concern;
  }

  /**
   * Sets the read preference for this database. Will be used as default for reads from any
   * collection in any database. See the documentation for {@link ReadPreference} for more
   * information.
   *
   * @param preference Read Preference to use
   */
  public void setReadPreference(ReadPreference preference) {
    _readPref = preference;
  }

  /**
   * Gets the default read preference
   *
   * @return the default read preference
   */
  public ReadPreference getReadPreference() {
    return _readPref;
  }

  /**
   * makes it possible to run read queries on secondary nodes
   *
   * @deprecated Replaced with {@code ReadPreference.secondaryPreferred()}
   * @see ReadPreference#secondaryPreferred()
   */
  @Deprecated
  public void slaveOk() {
    addOption(Bytes.QUERYOPTION_SLAVEOK);
  }

  /**
   * Add a default query option keeping any previously added options.
   *
   * @param option value to be added to current options
   */
  public void addOption(int option) {
    _netOptions.add(option);
  }

  /**
   * Set the default query options. Overrides any existing options.
   *
   * @param options value to be set
   */
  public void setOptions(int options) {
    _netOptions.set(options);
  }

  /** Reset the default query options */
  public void resetOptions() {
    _netOptions.reset();
  }

  /**
   * Gets the default query options
   *
   * @return an int representing the options to be used by queries
   */
  public int getOptions() {
    return _netOptions.get();
  }

  /**
   * Helper method for setting up MongoOptions at instantiation so that any options which affect
   * this connection can be set.
   */
  @SuppressWarnings("deprecation")
  void _applyMongoOptions() {
    if (_options.slaveOk) {
      slaveOk();
    }
    if (_options.getReadPreference() != null) {
      setReadPreference(_options.getReadPreference());
    }
    setWriteConcern(_options.getWriteConcern());
  }

  /**
   * Returns the mongo options.
   *
   * @deprecated Please use {@link MongoClient} and corresponding {@link
   *     com.mongodb.MongoClient#getMongoClientOptions()}
   * @return A {@link com.mongodb.MongoOptions} containing the settings for this MongoDB instance.
   */
  @Deprecated
  public MongoOptions getMongoOptions() {
    return _options;
  }

  /**
   * Gets the maximum size for a BSON object supported by the current master server. Note that this
   * value may change over time depending on which server is master. If the size is not known yet, a
   * request may be sent to the master server
   *
   * @return the maximum size
   * @throws MongoException
   */
  public int getMaxBsonObjectSize() {
    return _connector.getMaxBsonObjectSize();
  }

  boolean isMongosConnection() {
    return _connector.isMongosConnection();
  }

  private static MongoAuthority getMongoAuthorityFromURI(final MongoURI uri)
      throws UnknownHostException {
    if (uri.getHosts().size() == 1) {
      return MongoAuthority.direct(new ServerAddress(uri.getHosts().get(0)), uri.getCredentials());
    } else {
      List<ServerAddress> replicaSetSeeds = new ArrayList<ServerAddress>(uri.getHosts().size());
      for (String host : uri.getHosts()) replicaSetSeeds.add(new ServerAddress(host));
      return MongoAuthority.dynamicSet(replicaSetSeeds, uri.getCredentials());
    }
  }

  final MongoOptions _options;
  final DBTCPConnector _connector;
  final ConcurrentMap<String, DB> _dbs = new ConcurrentHashMap<String, DB>();
  private WriteConcern _concern = WriteConcern.NORMAL;
  private ReadPreference _readPref = ReadPreference.primary();
  final Bytes.OptionHolder _netOptions = new Bytes.OptionHolder(null);
  final CursorCleanerThread _cleaner;
  final MongoAuthority _authority;

  org.bson.util.SimplePool<PoolOutputBuffer> _bufferPool =
      new org.bson.util.SimplePool<PoolOutputBuffer>(1000) {

        protected PoolOutputBuffer createNew() {
          return new PoolOutputBuffer();
        }
      };

  /**
   * Forces the master server to fsync the RAM data to disk This is done automatically by the server
   * at intervals, but can be forced for better reliability.
   *
   * @param async if true, the fsync will be done asynchronously on the server.
   * @return result of the command execution
   * @throws MongoException
   * @mongodb.driver.manual reference/command/fsync/ fsync command
   */
  public CommandResult fsync(boolean async) {
    DBObject cmd = new BasicDBObject("fsync", 1);
    if (async) {
      cmd.put("async", 1);
    }
    CommandResult result = getDB(ADMIN_DATABASE_NAME).command(cmd);
    result.throwOnError();
    return result;
  }

  /**
   * Forces the master server to fsync the RAM data to disk, then lock all writes. The database will
   * be read-only after this command returns.
   *
   * @return result of the command execution
   * @throws MongoException
   * @mongodb.driver.manual reference/command/fsync/ fsync command
   */
  public CommandResult fsyncAndLock() {
    DBObject cmd = new BasicDBObject("fsync", 1);
    cmd.put("lock", 1);
    CommandResult result = getDB(ADMIN_DATABASE_NAME).command(cmd);
    result.throwOnError();
    return result;
  }

  /**
   * Unlocks the database, allowing the write operations to go through. This command may be
   * asynchronous on the server, which means there may be a small delay before the database becomes
   * writable.
   *
   * @return {@code DBObject} in the following form {@code {"ok": 1,"info": "unlock completed"}}
   * @throws MongoException
   * @mongodb.driver.manual reference/command/fsync/ fsync command
   */
  public DBObject unlock() {
    DB db = getDB(ADMIN_DATABASE_NAME);
    DBCollection col = db.getCollection("$cmd.sys.unlock");
    return col.findOne();
  }

  /**
   * Returns true if the database is locked (read-only), false otherwise.
   *
   * @return result of the command execution
   * @throws MongoException
   * @mongodb.driver.manual reference/command/fsync/ fsync command
   */
  public boolean isLocked() {
    DB db = getDB(ADMIN_DATABASE_NAME);
    DBCollection col = db.getCollection("$cmd.sys.inprog");
    BasicDBObject res = (BasicDBObject) col.findOne();
    if (res.containsField("fsyncLock")) {
      return res.getInt("fsyncLock") == 1;
    }
    return false;
  }

  // -------

  /**
   * Mongo.Holder can be used as a static place to hold several instances of Mongo. Security is not
   * enforced at this level, and needs to be done on the application side.
   */
  public static class Holder {

    /**
     * Attempts to find an existing MongoClient instance matching that URI in the holder, and
     * returns it if exists. Otherwise creates a new Mongo instance based on this URI and adds it to
     * the holder.
     *
     * @param uri the Mongo URI
     * @return the client
     * @throws MongoException
     * @throws UnknownHostException
     * @deprecated Please use {@link #connect(MongoClientURI)} instead.
     */
    @Deprecated
    public Mongo connect(final MongoURI uri) throws UnknownHostException {
      return connect(uri.toClientURI());
    }

    /**
     * Attempts to find an existing MongoClient instance matching that URI in the holder, and
     * returns it if exists. Otherwise creates a new Mongo instance based on this URI and adds it to
     * the holder.
     *
     * @param uri the Mongo URI
     * @return the client
     * @throws MongoException
     * @throws UnknownHostException
     */
    public Mongo connect(final MongoClientURI uri) throws UnknownHostException {

      final String key = toKey(uri);

      Mongo client = _mongos.get(key);

      if (client == null) {
        final Mongo newbie = new MongoClient(uri);
        client = _mongos.putIfAbsent(key, newbie);
        if (client == null) {
          client = newbie;
        } else {
          newbie.close();
        }
      }

      return client;
    }

    private String toKey(final MongoClientURI uri) {
      return uri.toString();
    }

    public static Holder singleton() {
      return _default;
    }

    private static Holder _default = new Holder();
    private final ConcurrentMap<String, Mongo> _mongos = new ConcurrentHashMap<String, Mongo>();
  }

  class CursorCleanerThread extends Thread {

    CursorCleanerThread() {
      setDaemon(true);
      setName("MongoCleaner" + hashCode());
    }

    public void run() {
      while (_connector.isOpen()) {
        try {
          try {
            Thread.sleep(cleanerIntervalMS);
          } catch (InterruptedException e) {
            // caused by the Mongo instance being closed -- proceed with cleanup
          }
          for (DB db : _dbs.values()) {
            db.cleanCursors(true);
          }
        } catch (Throwable t) {
          // thread must never die
        }
      }
    }
  }

  @Override
  public String toString() {
    return "Mongo{" + "authority=" + _authority + ", options=" + _options + '}';
  }

  /**
   * Gets the authority, which includes the connection type, the server address(es), and the
   * credentials.
   *
   * @return the authority
   */
  MongoAuthority getAuthority() {
    return _authority;
  }
}
 @Test
 public void testSecondaryPreferredWithNoPrimaryOrSecondary() {
   assertNull(ReadPreference.secondaryPreferred().getNode(_emptySet));
 }
 @Test
 public void testPrimaryWithNoPrimary() {
   assertNull(ReadPreference.primary().getNode(_setNoPrimary));
 }
 @Test
 public void testTaggedSecondaryWithNoSecondary() {
   final TaggableReadPreference taggedSecondaryReadPreference =
       ReadPreference.secondary(new BasicDBObject("dc", "ny"));
   assertNull(taggedSecondaryReadPreference.getNode(_setNoSecondary));
 }
 @Test
 public void testSecondaryWithNoSecondary() {
   assertNull(ReadPreference.secondary().getNode(_setNoSecondary));
 }
 @Test
 public void testTaggedSecondaryPreferredWithNoPrimaryOrSecondary() {
   final TaggableReadPreference readPreference =
       ReadPreference.secondaryPreferred(new BasicDBObject("dc", "ny"));
   assertNull(readPreference.getNode(_emptySet));
 }