/* Copyright 2010-present 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.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver.Core.Configuration;
namespace MongoDB.Driver
{
///
/// Represents an immutable URL style connection string. See also MongoUrlBuilder.
///
#if NET452
[Serializable]
#endif
public class MongoUrl : IEquatable
{
// private static fields
private static object __staticLock = new object();
private static Dictionary __cache = new Dictionary();
// private fields
private readonly string _applicationName;
private readonly string _authenticationMechanism;
private readonly IEnumerable> _authenticationMechanismProperties;
private readonly string _authenticationSource;
private readonly ConnectionMode _connectionMode;
private readonly TimeSpan _connectTimeout;
private readonly string _databaseName;
private readonly bool? _fsync;
private readonly GuidRepresentation _guidRepresentation;
private readonly TimeSpan _heartbeatInterval;
private readonly TimeSpan _heartbeatTimeout;
private readonly bool _ipv6;
private readonly bool? _journal;
private readonly TimeSpan _maxConnectionIdleTime;
private readonly TimeSpan _maxConnectionLifeTime;
private readonly int _maxConnectionPoolSize;
private readonly int _minConnectionPoolSize;
private readonly string _password;
private readonly ReadConcernLevel? _readConcernLevel;
private readonly ReadPreference _readPreference;
private readonly string _replicaSetName;
private readonly bool? _retryWrites;
private readonly TimeSpan _localThreshold;
private readonly ConnectionStringScheme _scheme;
private readonly IEnumerable _servers;
private readonly TimeSpan _serverSelectionTimeout;
private readonly TimeSpan _socketTimeout;
private readonly string _username;
private readonly bool _useSsl;
private readonly bool _verifySslCertificate;
private readonly WriteConcern.WValue _w;
private readonly double _waitQueueMultiple;
private readonly int _waitQueueSize;
private readonly TimeSpan _waitQueueTimeout;
private readonly TimeSpan? _wTimeout;
private readonly string _url;
private readonly string _originalUrl;
// constructors
///
/// Creates a new instance of MongoUrl.
///
/// The URL containing the settings.
public MongoUrl(string url)
{
_originalUrl = url;
var builder = new MongoUrlBuilder(url); // parses url
_applicationName = builder.ApplicationName;
_authenticationMechanism = builder.AuthenticationMechanism;
_authenticationMechanismProperties = builder.AuthenticationMechanismProperties;
_authenticationSource = builder.AuthenticationSource;
_connectionMode = builder.ConnectionMode;
_connectTimeout = builder.ConnectTimeout;
_databaseName = builder.DatabaseName;
_fsync = builder.FSync;
_guidRepresentation = builder.GuidRepresentation;
_heartbeatInterval = builder.HeartbeatInterval;
_heartbeatTimeout = builder.HeartbeatTimeout;
_ipv6 = builder.IPv6;
_journal = builder.Journal;
_localThreshold = builder.LocalThreshold;
_maxConnectionIdleTime = builder.MaxConnectionIdleTime;
_maxConnectionLifeTime = builder.MaxConnectionLifeTime;
_maxConnectionPoolSize = builder.MaxConnectionPoolSize;
_minConnectionPoolSize = builder.MinConnectionPoolSize;
_password = builder.Password;
_readConcernLevel = builder.ReadConcernLevel;
_readPreference = builder.ReadPreference;
_replicaSetName = builder.ReplicaSetName;
_retryWrites = builder.RetryWrites;
_scheme = builder.Scheme;
_servers = builder.Servers;
_serverSelectionTimeout = builder.ServerSelectionTimeout;
_socketTimeout = builder.SocketTimeout;
_username = builder.Username;
_useSsl = builder.UseSsl;
_verifySslCertificate = builder.VerifySslCertificate;
_w = builder.W;
_waitQueueMultiple = builder.WaitQueueMultiple;
_waitQueueSize = builder.WaitQueueSize;
_waitQueueTimeout = builder.WaitQueueTimeout;
_wTimeout = builder.WTimeout;
_url = builder.ToString(); // keep canonical form
}
// public properties
///
/// Gets the application name.
///
public string ApplicationName
{
get { return _applicationName; }
}
///
/// Gets the authentication mechanism.
///
public string AuthenticationMechanism
{
get { return _authenticationMechanism; }
}
///
/// Gets the authentication mechanism properties.
///
public IEnumerable> AuthenticationMechanismProperties
{
get { return _authenticationMechanismProperties; }
}
///
/// Gets the authentication source.
///
public string AuthenticationSource
{
get { return _authenticationSource; }
}
///
/// Gets the actual wait queue size (either WaitQueueSize or WaitQueueMultiple x MaxConnectionPoolSize).
///
public int ComputedWaitQueueSize
{
get
{
if (_waitQueueMultiple == 0.0)
{
return _waitQueueSize;
}
else
{
return (int)(_waitQueueMultiple * _maxConnectionPoolSize);
}
}
}
///
/// Gets the connection mode.
///
public ConnectionMode ConnectionMode
{
get { return _connectionMode; }
}
///
/// Gets the connect timeout.
///
public TimeSpan ConnectTimeout
{
get { return _connectTimeout; }
}
///
/// Gets the optional database name.
///
public string DatabaseName
{
get { return _databaseName; }
}
///
/// Gets the FSync component of the write concern.
///
public bool? FSync
{
get { return _fsync; }
}
///
/// Gets the representation to use for Guids.
///
public GuidRepresentation GuidRepresentation
{
get { return _guidRepresentation; }
}
///
/// Gets a value indicating whether this instance has authentication settings.
///
public bool HasAuthenticationSettings
{
get
{
return
_username != null ||
_password != null ||
_authenticationMechanism != null ||
_authenticationSource != null;
}
}
///
/// Gets the heartbeat interval.
///
public TimeSpan HeartbeatInterval
{
get { return _heartbeatInterval; }
}
///
/// Gets the heartbeat timeout.
///
public TimeSpan HeartbeatTimeout
{
get { return _heartbeatTimeout; }
}
///
/// Gets a value indicating whether to use IPv6.
///
public bool IPv6
{
get { return _ipv6; }
}
///
/// Gets the Journal component of the write concern.
///
public bool? Journal
{
get { return _journal; }
}
///
/// Gets the local threshold.
///
public TimeSpan LocalThreshold
{
get { return _localThreshold; }
}
///
/// Gets the max connection idle time.
///
public TimeSpan MaxConnectionIdleTime
{
get { return _maxConnectionIdleTime; }
}
///
/// Gets the max connection life time.
///
public TimeSpan MaxConnectionLifeTime
{
get { return _maxConnectionLifeTime; }
}
///
/// Gets the max connection pool size.
///
public int MaxConnectionPoolSize
{
get { return _maxConnectionPoolSize; }
}
///
/// Gets the min connection pool size.
///
public int MinConnectionPoolSize
{
get { return _minConnectionPoolSize; }
}
///
/// Gets the password.
///
public string Password
{
get { return _password; }
}
///
/// Gets the read concern level.
///
public ReadConcernLevel? ReadConcernLevel
{
get { return _readConcernLevel; }
}
///
/// Gets the read preference.
///
public ReadPreference ReadPreference
{
get { return _readPreference; }
}
///
/// Gets the name of the replica set.
///
public string ReplicaSetName
{
get { return _replicaSetName; }
}
///
/// Gets whether writes will be retried.
///
public bool? RetryWrites
{
get { return _retryWrites; }
}
///
/// Gets the scheme.
///
public ConnectionStringScheme Scheme
{
get { return _scheme; }
}
///
/// Gets the address of the server (see also Servers if using more than one address).
///
public MongoServerAddress Server
{
get { return (_servers == null) ? null : _servers.Single(); }
}
///
/// Gets the list of server addresses (see also Server if using only one address).
///
public IEnumerable Servers
{
get { return _servers; }
}
///
/// Gets the server selection timeout.
///
public TimeSpan ServerSelectionTimeout
{
get { return _serverSelectionTimeout; }
}
///
/// Gets the socket timeout.
///
public TimeSpan SocketTimeout
{
get { return _socketTimeout; }
}
///
/// Gets the URL (in canonical form).
///
public string Url
{
get { return _url; }
}
///
/// Gets the username.
///
public string Username
{
get { return _username; }
}
///
/// Gets a value indicating whether to use SSL.
///
public bool UseSsl
{
get { return _useSsl; }
}
///
/// Gets a value indicating whether to verify an SSL certificate.
///
public bool VerifySslCertificate
{
get { return _verifySslCertificate; }
}
///
/// Gets the W component of the write concern.
///
public WriteConcern.WValue W
{
get { return _w; }
}
///
/// Gets the wait queue multiple (the actual wait queue size will be WaitQueueMultiple x MaxConnectionPoolSize).
///
public double WaitQueueMultiple
{
get { return _waitQueueMultiple; }
}
///
/// Gets the wait queue size.
///
public int WaitQueueSize
{
get { return _waitQueueSize; }
}
///
/// Gets the wait queue timeout.
///
public TimeSpan WaitQueueTimeout
{
get { return _waitQueueTimeout; }
}
///
/// Gets the WTimeout component of the write concern.
///
public TimeSpan? WTimeout
{
get { return _wTimeout; }
}
// public operators
///
/// Compares two MongoUrls.
///
/// The first URL.
/// The other URL.
/// True if the two URLs are equal (or both null).
public static bool operator ==(MongoUrl lhs, MongoUrl rhs)
{
return object.Equals(lhs, rhs);
}
///
/// Compares two MongoUrls.
///
/// The first URL.
/// The other URL.
/// True if the two URLs are not equal (or one is null and the other is not).
public static bool operator !=(MongoUrl lhs, MongoUrl rhs)
{
return !(lhs == rhs);
}
// public static methods
///
/// Clears the URL cache. When a URL is parsed it is stored in the cache so that it doesn't have to be
/// parsed again. There is rarely a need to call this method.
///
public static void ClearCache()
{
__cache.Clear();
}
///
/// Creates an instance of MongoUrl (might be an existing existence if the same URL has been used before).
///
/// The URL containing the settings.
/// An instance of MongoUrl.
public static MongoUrl Create(string url)
{
// cache previously seen urls to avoid repeated parsing
lock (__staticLock)
{
MongoUrl mongoUrl;
if (!__cache.TryGetValue(url, out mongoUrl))
{
mongoUrl = new MongoUrl(url);
var canonicalUrl = mongoUrl.ToString();
if (canonicalUrl != url)
{
if (__cache.ContainsKey(canonicalUrl))
{
mongoUrl = __cache[canonicalUrl]; // use existing MongoUrl
}
else
{
__cache[canonicalUrl] = mongoUrl; // cache under canonicalUrl also
}
}
__cache[url] = mongoUrl;
}
return mongoUrl;
}
}
// public methods
///
/// Compares two MongoUrls.
///
/// The other URL.
/// True if the two URLs are equal.
public bool Equals(MongoUrl rhs)
{
if (object.ReferenceEquals(rhs, null) || GetType() != rhs.GetType()) { return false; }
return _url == rhs._url; // this works because URL is in canonical form
}
///
/// Compares two MongoUrls.
///
/// The other URL.
/// True if the two URLs are equal.
public override bool Equals(object obj)
{
return Equals(obj as MongoUrl); // works even if obj is null or of a different type
}
///
/// Gets the credential.
///
/// The credential (or null if the URL has not authentication settings).
public MongoCredential GetCredential()
{
if (HasAuthenticationSettings)
{
return MongoCredential.FromComponents(
_authenticationMechanism,
_authenticationSource ?? _databaseName,
_username,
_password);
}
else
{
return null;
}
}
///
/// Gets the hash code.
///
/// The hash code.
public override int GetHashCode()
{
return _url.GetHashCode(); // this works because URL is in canonical form
}
///
/// Returns a WriteConcern value based on this instance's settings and a default enabled value.
///
/// The default enabled value.
/// A WriteConcern.
public WriteConcern GetWriteConcern(bool enabledDefault)
{
if (_w == null && !_wTimeout.HasValue && !_fsync.HasValue && !_journal.HasValue)
{
return enabledDefault ? WriteConcern.Acknowledged : WriteConcern.Unacknowledged;
}
return new WriteConcern(_w, _wTimeout, _fsync, _journal);
}
///
/// Resolves a connection string. If the connection string indicates more information is available
/// in the DNS system, it will acquire that information as well.
///
/// A resolved MongoURL.
public MongoUrl Resolve()
{
if (_scheme == ConnectionStringScheme.MongoDB)
{
return this;
}
var connectionString = new ConnectionString(_originalUrl);
var resolved = connectionString.Resolve();
return new MongoUrl(resolved.ToString());
}
///
/// Resolves a connection string. If the connection string indicates more information is available
/// in the DNS system, it will acquire that information as well.
///
/// The cancellation token.
/// A resolved MongoURL.
public async Task ResolveAsync(CancellationToken cancellationToken = default(CancellationToken))
{
if (_scheme == ConnectionStringScheme.MongoDB)
{
return this;
}
var connectionString = new ConnectionString(_originalUrl);
var resolved = await connectionString.ResolveAsync(cancellationToken).ConfigureAwait(false);
return new MongoUrl(resolved.ToString());
}
///
/// Returns the canonical URL based on the settings in this MongoUrlBuilder.
///
/// The canonical URL.
public override string ToString()
{
return _url;
}
// private methods
private bool AnyWriteConcernSettingsAreSet()
{
return _fsync != null || _journal != null || _w != null || _wTimeout != null;
}
}
}