/* 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.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using MongoDB.Driver.Core.Authentication;
using MongoDB.Driver.Core.Clusters;
using MongoDB.Driver.Core.Clusters.ServerSelectors;
using MongoDB.Driver.Core.Configuration;
using MongoDB.Driver.Core.ConnectionPools;
using MongoDB.Driver.Core.Connections;
using MongoDB.Driver.Core.Events;
using MongoDB.Driver.Core.Events.Diagnostics;
using MongoDB.Driver.Core.Misc;
using MongoDB.Driver.Core.Servers;
namespace MongoDB.Driver
{
///
/// Represents a registry of already created clusters.
///
public class ClusterRegistry
{
#region static
// static fields
private static readonly ClusterRegistry __instance = new ClusterRegistry();
// static properties
///
/// Gets the default cluster registry.
///
///
/// The default cluster registry.
///
public static ClusterRegistry Instance
{
get { return __instance; }
}
#endregion
// fields
private readonly object _lock = new object();
private readonly Dictionary _registry = new Dictionary();
// methods
private ICluster CreateCluster(ClusterKey clusterKey)
{
var builder = new ClusterBuilder()
.ConfigureCluster(settings => ConfigureCluster(settings, clusterKey))
.ConfigureServer(settings => ConfigureServer(settings, clusterKey))
.ConfigureConnectionPool(settings => ConfigureConnectionPool(settings, clusterKey))
.ConfigureConnection(settings => ConfigureConnection(settings, clusterKey))
.ConfigureTcp(settings => ConfigureTcp(settings, clusterKey))
.ConfigureSdamLogging(settings => ConfigureSdamLogging(settings, clusterKey));
if (clusterKey.UseSsl)
{
builder.ConfigureSsl(settings => ConfigureSsl(settings, clusterKey));
}
if (clusterKey.ClusterConfigurator != null)
{
clusterKey.ClusterConfigurator(builder);
}
var cluster = builder.BuildCluster();
cluster.Initialize();
return cluster;
}
private ClusterSettings ConfigureCluster(ClusterSettings settings, ClusterKey clusterKey)
{
var endPoints = clusterKey.Servers.Select(s => EndPointHelper.Parse(s.ToString()));
return settings.With(
connectionMode: clusterKey.ConnectionMode.ToCore(),
endPoints: Optional.Enumerable(endPoints),
replicaSetName: clusterKey.ReplicaSetName,
maxServerSelectionWaitQueueSize: clusterKey.WaitQueueSize,
serverSelectionTimeout: clusterKey.ServerSelectionTimeout,
postServerSelector: new LatencyLimitingServerSelector(clusterKey.LocalThreshold));
}
private ConnectionPoolSettings ConfigureConnectionPool(ConnectionPoolSettings settings, ClusterKey clusterKey)
{
return settings.With(
// maintenanceInterval: TODO: should this be configurable?
maxConnections: clusterKey.MaxConnectionPoolSize,
minConnections: clusterKey.MinConnectionPoolSize,
waitQueueSize: clusterKey.WaitQueueSize,
waitQueueTimeout: clusterKey.WaitQueueTimeout);
}
private ConnectionSettings ConfigureConnection(ConnectionSettings settings, ClusterKey clusterKey)
{
var authenticators = clusterKey.Credentials.Select(c => c.ToAuthenticator());
return settings.With(
authenticators: Optional.Enumerable(authenticators),
maxIdleTime: clusterKey.MaxConnectionIdleTime,
maxLifeTime: clusterKey.MaxConnectionLifeTime,
applicationName: clusterKey.ApplicationName);
}
private SdamLoggingSettings ConfigureSdamLogging(SdamLoggingSettings settings, ClusterKey clusterKey)
{
return settings.With(logFilename: clusterKey.SdamLogFilename);
}
private ServerSettings ConfigureServer(ServerSettings settings, ClusterKey clusterKey)
{
return settings.With(
heartbeatInterval: clusterKey.HeartbeatInterval,
heartbeatTimeout: clusterKey.HeartbeatTimeout);
}
private SslStreamSettings ConfigureSsl(SslStreamSettings settings, ClusterKey clusterKey)
{
if (clusterKey.UseSsl)
{
var sslSettings = clusterKey.SslSettings ?? new SslSettings();
var validationCallback = sslSettings.ServerCertificateValidationCallback;
if (validationCallback == null && !clusterKey.VerifySslCertificate)
{
validationCallback = AcceptAnySslCertificate;
}
return settings.With(
clientCertificates: Optional.Enumerable(sslSettings.ClientCertificates ?? Enumerable.Empty()),
checkCertificateRevocation: sslSettings.CheckCertificateRevocation,
clientCertificateSelectionCallback: sslSettings.ClientCertificateSelectionCallback,
enabledProtocols: sslSettings.EnabledSslProtocols,
serverCertificateValidationCallback: validationCallback);
}
return settings;
}
private TcpStreamSettings ConfigureTcp(TcpStreamSettings settings, ClusterKey clusterKey)
{
if (clusterKey.IPv6)
{
settings = settings.With(addressFamily: AddressFamily.InterNetworkV6);
}
return settings.With(
connectTimeout: clusterKey.ConnectTimeout,
readTimeout: clusterKey.SocketTimeout,
receiveBufferSize: clusterKey.ReceiveBufferSize,
sendBufferSize: clusterKey.SendBufferSize,
writeTimeout: clusterKey.SocketTimeout);
}
internal ICluster GetOrCreateCluster(ClusterKey clusterKey)
{
lock (_lock)
{
ICluster cluster;
if (!_registry.TryGetValue(clusterKey, out cluster))
{
cluster = CreateCluster(clusterKey);
_registry.Add(clusterKey, cluster);
}
return cluster;
}
}
private static bool AcceptAnySslCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors
)
{
return true;
}
///
/// Unregisters and disposes the cluster.
///
/// The cluster.
public void UnregisterAndDisposeCluster(ICluster cluster)
{
Ensure.IsNotNull(cluster, nameof(cluster));
lock (_lock)
{
ClusterKey clusterKey = null;
foreach (var keyValuePair in _registry)
{
if (object.ReferenceEquals(keyValuePair.Value, cluster))
{
clusterKey = keyValuePair.Key;
break;
}
}
if (clusterKey != null)
{
_registry.Remove(clusterKey);
}
}
cluster.Dispose();
}
}
}