All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mongodb.MongoClientURI Maven / Gradle / Ivy

Go to download

The MongoDB Java Driver uber-artifact, containing mongodb-driver, mongodb-driver-core, and bson

There is a newer version: 3.1.0
Show newest version
/*
 * Copyright (c) 2008-2014 MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mongodb;

import javax.net.ssl.SSLSocketFactory;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;


/**
 * Represents a URI
 * which can be used to create a MongoClient instance. The URI describes the hosts to
 * be used and options.
 * 

The format of the URI is: *

 *   mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database[.collection]][?options]]
 * 
*
    *
  • {@code mongodb://} is a required prefix to identify that this is a string in the standard connection format.
  • *
  • {@code username:password@} are optional. If given, the driver will attempt to login to a database after * connecting to a database server. For some authentication mechanisms, only the username is specified and the password is not, * in which case the ":" after the username is left off as well.
  • *
  • {@code host1} is the only required part of the URI. It identifies a server address to connect to.
  • *
  • {@code :portX} is optional and defaults to :27017 if not provided.
  • *
  • {@code /database} is the name of the database to login to and thus is only relevant if the * {@code username:password@} syntax is used. If not specified the "admin" database will be used by default.
  • *
  • {@code ?options} are connection options. Note that if {@code database} is absent there is still a {@code /} * required between the last host and the {@code ?} introducing the options. Options are name=value pairs and the pairs * are separated by "&". For backwards compatibility, ";" is accepted as a separator in addition to "&", * but should be considered as deprecated.
  • *
*

The following options are supported (case insensitive):

* *

Replica set configuration:

*
    *
  • {@code replicaSet=name}: Implies that the hosts given are a seed list, and the driver will attempt to find * all members of the set.
  • *
* *

Connection Configuration:

*
    *
  • {@code ssl=true|false}: Whether to connect using SSL.
  • *
  • {@code connectTimeoutMS=ms}: How long a connection can take to be opened before timing out.
  • *
  • {@code socketTimeoutMS=ms}: How long a send or receive on a socket can take before timing out.
  • *
  • {@code maxIdleTimeMS=ms}: Maximum idle time of a pooled connection. A connection that exceeds this limit will be closed
  • *
  • {@code maxLifeTimeMS=ms}: Maximum life time of a pooled connection. A connection that exceeds this limit will be closed
  • *
* *

Connection pool configuration:

*
    *
  • {@code maxPoolSize=n}: The maximum number of connections in the connection pool.
  • *
  • {@code minPoolSize=n}: The minimum number of connections in the connection pool.
  • *
  • {@code waitQueueMultiple=n} : this multiplier, multiplied with the maxPoolSize setting, gives the maximum number of * threads that may be waiting for a connection to become available from the pool. All further threads will get an * exception right away.
  • *
  • {@code waitQueueTimeoutMS=ms}: The maximum wait time in milliseconds that a thread may wait for a connection to * become available.
  • *
*

Write concern configuration:

*
    *
  • {@code safe=true|false} *
      *
    • {@code true}: the driver sends a getLastError command after every update to ensure that the update succeeded * (see also {@code w} and {@code wtimeoutMS}).
    • *
    • {@code false}: the driver does not send a getLastError command after every update.
    • *
    *
  • *
  • {@code w=wValue} *
      *
    • The driver adds { w : wValue } to the getLastError command. Implies {@code safe=true}.
    • *
    • wValue is typically a number, but can be any string in order to allow for specifications like * {@code "majority"}
    • *
    *
  • *
  • {@code wtimeoutMS=ms} *
      *
    • The driver adds { wtimeout : ms } to the getlasterror command. Implies {@code safe=true}.
    • *
    • Used in combination with {@code w}
    • *
    *
  • *
* *

Read preference configuration:

*
    *
  • {@code slaveOk=true|false}: Whether a driver connected to a replica set will send reads to slaves/secondaries.
  • *
  • {@code readPreference=enum}: The read preference for this connection. If set, it overrides any slaveOk value. *
      *
    • Enumerated values: *
        *
      • {@code primary}
      • *
      • {@code primaryPreferred}
      • *
      • {@code secondary}
      • *
      • {@code secondaryPreferred}
      • *
      • {@code nearest}
      • *
      *
    • *
    *
  • *
  • {@code readPreferenceTags=string}. A representation of a tag set as a comma-separated list of colon-separated * key-value pairs, e.g. {@code "dc:ny,rack:1}". Spaces are stripped from beginning and end of all keys and values. * To specify a list of tag sets, using multiple readPreferenceTags, * e.g. {@code readPreferenceTags=dc:ny,rack:1;readPreferenceTags=dc:ny;readPreferenceTags=} *
      *
    • Note the empty value for the last one, which means match any secondary as a last resort.
    • *
    • Order matters when using multiple readPreferenceTags.
    • *
    *
  • *
*

Authentication configuration:

*
    *
  • {@code authMechanism=MONGO-CR|GSSAPI|PLAIN|MONGODB-X509}: The authentication mechanism to use if a credential was supplied. * The default is unspecified, in which case the client will pick the most secure mechanism available based on the sever version. For the * GSSAPI and MONGODB-X509 mechanisms, no password is accepted, only the username. *
  • *
  • {@code authSource=string}: The source of the authentication credentials. This is typically the database that * the credentials have been created. The value defaults to the database specified in the path portion of the URI. * If the database is specified in neither place, the default value is "admin". For GSSAPI, it's not necessary to specify * a source. *
  • *
  • {@code gssapiServiceName=string}: This option only applies to the GSSAPI mechanism and is used to alter the service name.. *
  • *
* *

Note: This class is a replacement for {@code MongoURI}, to be used with {@code MongoClient}. The main difference in * behavior is that the default write concern is {@code WriteConcern.ACKNOWLEDGED}.

* * @mongodb.driver.manual reference/connection-string Connection String URI Format * @see MongoClientOptions for the default values for all options * @since 2.10.0 */ public class MongoClientURI { private static final String PREFIX = "mongodb://"; private static final String UTF_8 = "UTF-8"; /** * Creates a MongoURI from the given string. * * @param uri the URI * @dochub connections */ public MongoClientURI(final String uri) { this(uri, new MongoClientOptions.Builder()); } /** * Creates a MongoURI from the given URI string, and MongoClientOptions.Builder. The builder can be configured with default options, * which may be overridden by options specified in the URI string. * * @param uri the URI * @param builder a Builder * @see com.mongodb.MongoClientURI#getOptions() * @since 2.11.0 */ public MongoClientURI(String uri, MongoClientOptions.Builder builder) { try { this.uri = uri; if (!uri.startsWith(PREFIX)) throw new IllegalArgumentException("uri needs to start with " + PREFIX); uri = uri.substring(PREFIX.length()); String serverPart; String nsPart; String optionsPart; String userName = null; char[] password = null; { int idx = uri.lastIndexOf("/"); if (idx < 0) { if (uri.contains("?")) { throw new IllegalArgumentException("URI contains options without trailing slash"); } serverPart = uri; nsPart = null; optionsPart = ""; } else { serverPart = uri.substring(0, idx); nsPart = uri.substring(idx + 1); idx = nsPart.indexOf("?"); if (idx >= 0) { optionsPart = nsPart.substring(idx + 1); nsPart = nsPart.substring(0, idx); } else { optionsPart = ""; } } } { // userName,password,hosts List all = new LinkedList(); int idx = serverPart.indexOf("@"); if (idx > 0) { String authPart = serverPart.substring(0, idx); serverPart = serverPart.substring(idx + 1); idx = authPart.indexOf(":"); if (idx == -1) { userName = URLDecoder.decode(authPart, UTF_8); } else { userName = URLDecoder.decode(authPart.substring(0, idx), UTF_8); password = URLDecoder.decode(authPart.substring(idx + 1), UTF_8).toCharArray(); } } Collections.addAll(all, serverPart.split(",")); Collections.sort(all); hosts = Collections.unmodifiableList(all); } if (nsPart != null && nsPart.length() != 0) { // database,_collection int idx = nsPart.indexOf("."); if (idx < 0) { database = nsPart; collection = null; } else { database = nsPart.substring(0, idx); collection = nsPart.substring(idx + 1); } } else { database = null; collection = null; } Map> optionsMap = parseOptions(optionsPart); options = createOptions(optionsMap, builder); credentials = createCredentials(optionsMap, userName, password, database); warnOnUnsupportedOptions(optionsMap); } catch (UnsupportedEncodingException e) { throw new MongoInternalException("This should not happen", e); } } static Set generalOptionsKeys = new HashSet(); static Set authKeys = new HashSet(); static Set readPreferenceKeys = new HashSet(); static Set writeConcernKeys = new HashSet(); static Set allKeys = new HashSet(); static { generalOptionsKeys.add("minpoolsize"); generalOptionsKeys.add("maxpoolsize"); generalOptionsKeys.add("waitqueuemultiple"); generalOptionsKeys.add("waitqueuetimeoutms"); generalOptionsKeys.add("connecttimeoutms"); generalOptionsKeys.add("maxidletimems"); generalOptionsKeys.add("maxlifetimems"); generalOptionsKeys.add("sockettimeoutms"); generalOptionsKeys.add("sockettimeoutms"); generalOptionsKeys.add("autoconnectretry"); generalOptionsKeys.add("ssl"); generalOptionsKeys.add("replicaset"); readPreferenceKeys.add("slaveok"); readPreferenceKeys.add("readpreference"); readPreferenceKeys.add("readpreferencetags"); writeConcernKeys.add("safe"); writeConcernKeys.add("w"); writeConcernKeys.add("wtimeoutms"); writeConcernKeys.add("fsync"); writeConcernKeys.add("j"); authKeys.add("authmechanism"); authKeys.add("authsource"); authKeys.add("gssapiservicename"); allKeys.addAll(generalOptionsKeys); allKeys.addAll(authKeys); allKeys.addAll(readPreferenceKeys); allKeys.addAll(writeConcernKeys); } private void warnOnUnsupportedOptions(Map> optionsMap) { for (String key : optionsMap.keySet()) { if (!allKeys.contains(key)) { LOGGER.warning("Unknown or Unsupported Option '" + key + "'"); } } } private MongoClientOptions createOptions(Map> optionsMap, MongoClientOptions.Builder builder) { for (String key : generalOptionsKeys) { String value = getLastValue(optionsMap, key); if (value == null) { continue; } if (key.equals("maxpoolsize")) { builder.connectionsPerHost(Integer.parseInt(value)); } else if (key.equals("minpoolsize")) { builder.minConnectionsPerHost(Integer.parseInt(value)); } else if (key.equals("maxidletimems")) { builder.maxConnectionIdleTime(Integer.parseInt(value)); } else if (key.equals("maxlifetimems")) { builder.maxConnectionLifeTime(Integer.parseInt(value)); } else if (key.equals("waitqueuemultiple")) { builder.threadsAllowedToBlockForConnectionMultiplier(Integer.parseInt(value)); } else if (key.equals("waitqueuetimeoutms")) { builder.maxWaitTime(Integer.parseInt(value)); } else if (key.equals("connecttimeoutms")) { builder.connectTimeout(Integer.parseInt(value)); } else if (key.equals("sockettimeoutms")) { builder.socketTimeout(Integer.parseInt(value)); } else if (key.equals("autoconnectretry")) { builder.autoConnectRetry(_parseBoolean(value)); } else if (key.equals("replicaset")) { builder.requiredReplicaSetName(value); } else if (key.equals("ssl")) { if (_parseBoolean(value)) { builder.socketFactory(SSLSocketFactory.getDefault()); } } } WriteConcern writeConcern = createWriteConcern(optionsMap); ReadPreference readPreference = createReadPreference(optionsMap); if (writeConcern != null) { builder.writeConcern(writeConcern); } if (readPreference != null) { builder.readPreference(readPreference); } return builder.build(); } private WriteConcern createWriteConcern(final Map> optionsMap) { Boolean safe = null; String w = null; int wTimeout = 0; boolean fsync = false; boolean journal = false; for (String key : writeConcernKeys) { String value = getLastValue(optionsMap, key); if (value == null) { continue; } if (key.equals("safe")) { safe = _parseBoolean(value); } else if (key.equals("w")) { w = value; } else if (key.equals("wtimeoutms")) { wTimeout = Integer.parseInt(value); } else if (key.equals("fsync")) { fsync = _parseBoolean(value); } else if (key.equals("j")) { journal = _parseBoolean(value); } } return buildWriteConcern(safe, w, wTimeout, fsync, journal); } private ReadPreference createReadPreference(final Map> optionsMap) { Boolean slaveOk = null; String readPreferenceType = null; DBObject firstTagSet = null; List remainingTagSets = new ArrayList(); for (String key : readPreferenceKeys) { String value = getLastValue(optionsMap, key); if (value == null) { continue; } if (key.equals("slaveok")) { slaveOk = _parseBoolean(value); } else if (key.equals("readpreference")) { readPreferenceType = value; } else if (key.equals("readpreferencetags")) { for (String cur : optionsMap.get(key)) { DBObject tagSet = getTagSet(cur.trim()); if (firstTagSet == null) { firstTagSet = tagSet; } else { remainingTagSets.add(tagSet); } } } } return buildReadPreference(readPreferenceType, firstTagSet, remainingTagSets, slaveOk); } private MongoCredential createCredentials(Map> optionsMap, final String userName, final char[] password, String database) { if (userName == null) { return null; } if (database == null) { database = "admin"; } String mechanism = null; String authSource = database; String gssapiServiceName = null; for (String key : authKeys) { String value = getLastValue(optionsMap, key); if (value == null) { continue; } if (key.equals("authmechanism")) { mechanism = value; } else if (key.equals("authsource")) { authSource = value; } else if (key.equals("gssapiservicename")) { gssapiServiceName = value; } } if (MongoCredential.GSSAPI_MECHANISM.equals(mechanism)) { MongoCredential gssapiCredential = MongoCredential.createGSSAPICredential(userName); if (gssapiServiceName != null) { gssapiCredential = gssapiCredential.withMechanismProperty("SERVICE_NAME", gssapiServiceName); } return gssapiCredential; } else if (MongoCredential.PLAIN_MECHANISM.equals(mechanism)) { return MongoCredential.createPlainCredential(userName, authSource, password); } else if (MongoCredential.MONGODB_CR_MECHANISM.equals(mechanism)) { return MongoCredential.createMongoCRCredential(userName, authSource, password); } else if (MongoCredential.MONGODB_X509_MECHANISM.equals(mechanism)) { return MongoCredential.createMongoX509Credential(userName); } else if (MongoCredential.SCRAM_SHA_1_MECHANISM.equals(mechanism)) { return MongoCredential.createScramSha1Credential(userName, authSource, password); } else if (mechanism == null) { return MongoCredential.createCredential(userName, authSource, password); } else { throw new IllegalArgumentException("Unsupported authMechanism: " + mechanism); } } private String getLastValue(final Map> optionsMap, final String key) { List valueList = optionsMap.get(key); if (valueList == null) { return null; } return valueList.get(valueList.size() - 1); } private Map> parseOptions(String optionsPart) { Map> optionsMap = new HashMap>(); for (String _part : optionsPart.split("&|;")) { int idx = _part.indexOf("="); if (idx >= 0) { String key = _part.substring(0, idx).toLowerCase(); String value = _part.substring(idx + 1); List valueList = optionsMap.get(key); if (valueList == null) { valueList = new ArrayList(1); } valueList.add(value); optionsMap.put(key, valueList); } } // JAVA-943 handle legacy wtimeout settings if (optionsMap.containsKey("wtimeout") && !optionsMap.containsKey("wtimeoutms")) { optionsMap.put("wtimeoutms", optionsMap.remove("wtimeout")); } return optionsMap; } private ReadPreference buildReadPreference(final String readPreferenceType, final DBObject firstTagSet, final List 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; } private WriteConcern buildWriteConcern(final Boolean safe, final String w, final int wTimeout, final boolean fsync, final boolean journal) { if (w != null || wTimeout != 0 || fsync || journal) { if (w == null) { return new WriteConcern(1, wTimeout, fsync, journal); } else { try { return new WriteConcern(Integer.parseInt(w), wTimeout, fsync, journal); } catch (NumberFormatException e) { return new WriteConcern(w, wTimeout, fsync, journal); } } } else if (safe != null) { if (safe) { return WriteConcern.ACKNOWLEDGED; } else { return WriteConcern.UNACKNOWLEDGED; } } return null; } private DBObject getTagSet(String tagSetString) { DBObject tagSet = new BasicDBObject(); if (tagSetString.length() > 0) { for (String tag : tagSetString.split(",")) { String[] tagKeyValuePair = tag.split(":"); if (tagKeyValuePair.length != 2) { throw new IllegalArgumentException("Bad read preference tags: " + tagSetString); } tagSet.put(tagKeyValuePair[0].trim(), tagKeyValuePair[1].trim()); } } return tagSet; } boolean _parseBoolean(String _in) { String in = _in.trim(); return in != null && in.length() > 0 && (in.equals("1") || in.toLowerCase().equals("true") || in.toLowerCase() .equals("yes")); } // --------------------------------- /** * Gets the username * * @return the username */ public String getUsername() { return credentials != null ? credentials.getUserName() : null; } /** * Gets the password * * @return the password */ public char[] getPassword() { return credentials != null ? credentials.getPassword() : null; } /** * Gets the list of hosts * * @return the host list */ public List getHosts() { return hosts; } /** * Gets the database name * * @return the database name */ public String getDatabase() { return database; } /** * Gets the collection name * * @return the collection name */ public String getCollection() { return collection; } /** * Get the unparsed URI. * * @return the URI */ public String getURI() { return uri; } /** * Gets the credentials. * * @return the credentials */ public MongoCredential getCredentials() { return credentials; } /** * Gets the options * * @return the MongoClientOptions based on this URI. */ public MongoClientOptions getOptions() { return options; } // --------------------------------- private final MongoClientOptions options; private final MongoCredential credentials; private final List hosts; private final String database; private final String collection; private final String uri; static final Logger LOGGER = Logger.getLogger("com.mongodb.MongoURI"); @Override public String toString() { return uri; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MongoClientURI that = (MongoClientURI) o; if (!hosts.equals(that.hosts)) { return false; } if (database != null ? !database.equals(that.database) : that.database != null) { return false; } if (collection != null ? !collection.equals(that.collection) : that.collection != null) { return false; } if (credentials != null ? !credentials.equals(that.credentials) : that.credentials != null) { return false; } if (!options.equals(that.options)) { return false; } return true; } @Override public int hashCode() { int result = options.hashCode(); result = 31 * result + (credentials != null ? credentials.hashCode() : 0); result = 31 * result + hosts.hashCode(); result = 31 * result + (database != null ? database.hashCode() : 0); result = 31 * result + (collection != null ? collection.hashCode() : 0); return result; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy