/// <summary> /// Recieves the version without a given configuration. The configuration will be loaded from /// the version service /// </summary> private void RecieveVersionWithoutConfig(MonitoredClient client) { Logger.Log(LogLevel.Debug, "Trying to get service configuration for service '{0}'", client.ServiceName); client.ServiceConfiguration = VersionService.GetServiceConfiguration(client.ServiceName); if (client.ServiceConfiguration == null) { HandleConnectionFailure(client, "Empty answer."); return; } // Finally reached the server Logger.Log(LogLevel.Debug, "Got service configuration for service '{0}'", client.ServiceName); client.ClientInfo.ServerVersion = client.ServiceConfiguration.ServerVersion; client.ClientInfo.MinClientVersion = client.ServiceConfiguration.MinClientVersion; client.ClientInfo.Uri = client.ServiceConfiguration.ServiceUrl; var versionMatch = VersionService.Match(client.ClientInfo, client.ServiceConfiguration) ? InternalConnectionState.VersionMatch : InternalConnectionState.VersionMissmatch; HandleConnectionState(client, versionMatch); }
/// <summary> /// Recievies the version of the client from the versionservice by a given config /// </summary> private void RecievieVersionWithConfig(MonitoredClient client) { if (string.IsNullOrEmpty(client.Config.MinServerVersion)) { // Don't do any version check. HandleConnectionState(client, InternalConnectionState.VersionMatch); } else { Logger.Log(LogLevel.Debug, "Trying to get version info for service '{0}'", client.ServiceName); var version = VersionService.GetServerVersion(client.Config.Endpoint); if (string.IsNullOrEmpty(version)) { HandleConnectionFailure(client, "Empty answer."); return; } // Finally reached the server Logger.Log(LogLevel.Debug, "Got version '{0}' info for service '{1}'", version, client.ServiceName); client.ClientInfo.ServerVersion = version; var versionMatch = VersionService.Match(client.Config, version) ? InternalConnectionState.VersionMatch : InternalConnectionState.VersionMissmatch; HandleConnectionState(client, versionMatch); } }
/// <summary> /// Instantiate the client object. /// </summary> private ICommunicationObject InstantiateClientObject(MonitoredClient monitoredClient) { var clientParams = new List <object>(); var serviceConfiguration = monitoredClient.Endpoint; var monitoredClientConfig = monitoredClient.Config; var requiresAuthentication = (serviceConfiguration is { RequiresAuthentication : true }) ||
/// <summary> /// Handles a connection failure and throws a failed try. /// </summary> private void HandleConnectionFailure(MonitoredClient client, string message) { Logger.Log(LogLevel.Debug, "Can't get version info for service '{0}': {1}", client.ServiceName, message); client.ClientInfo.ServerVersion = WcfClientInfo.Unknown; client.ClientInfo.Uri = WcfClientInfo.Unknown; HandleConnectionState(client, InternalConnectionState.FailedTry); }
private void HandleDisconnect(MonitoredClient client, DisconnectReason reason) { if (client == null) { Logger.Log(LogLevel.Warning, "Connection of unknown client/callback {0}", reason); return; } Logger.Log(LogLevel.Warning, "Connection/Callback to '{0}' {1}", client.ServiceName, reason); // State of the client. If the client was destroyed, the internal connection state will be set to closed. // If the client was not destroryed, the connection was lost. var clientState = client.Destroy ? InternalConnectionState.Closed : InternalConnectionState.ConnectionLost; HandleConnectionState(client, clientState); }
/// <summary> /// Matches the version of the given client and writes it to the client info. /// </summary> private void ReceiveVersion(MonitoredClient client) { try { if (client.Config != null) { ReceiveVersionWithConfig(client); } else { ReceiveVersionWithoutConfig(client); } } catch (Exception e) { HandleConnectionFailure(client, e.Message); } }
/// <summary> /// Receives the version of the client from the version service by a given config /// </summary> private void ReceiveVersionWithConfig(MonitoredClient client) { if (!client.Config.CheckVersion) { // Don't do any version check. HandleConnectionState(client, InternalConnectionState.VersionMatch); } else { Logger.Log(LogLevel.Debug, "Trying to get version info for service '{0}'", client.ServiceName); // Get all endpoints for this service var endpoints = VersionService.ServiceEndpoints(client.ServiceName); if (endpoints == null) { HandleConnectionFailure(client, "Endpoint service not available"); return; } // Select endpoint by path for config var endpoint = endpoints.FirstOrDefault(e => e.Path == client.Config.Endpoint); if (endpoint == null) { HandleConnectionFailure(client, "Endpoint not found on server"); return; } // Finally reached the server Logger.Log(LogLevel.Debug, "Got version '{0}' for service '{1}'", endpoint.Version, client.ServiceName); var clientVersion = Version.Parse(client.Config.ClientVersion); var serverVersion = Version.Parse(endpoint.Version); client.ClientInfo.ServerVersion = endpoint.Version; // Compare version if (endpoint.Binding == client.Config.BindingType && VersionCompare.ClientMatch(serverVersion, clientVersion)) { HandleConnectionState(client, InternalConnectionState.VersionMatch); } else { HandleConnectionState(client, InternalConnectionState.VersionMissmatch); } } }
/// <summary> /// Checks if the client is currently connected /// </summary> private static bool CheckIsClientOffline(MonitoredClient client) { if (client.Destroy) { return(false); } switch (client.State) { case InternalConnectionState.New: case InternalConnectionState.FailedTry: case InternalConnectionState.ConnectionLost: case InternalConnectionState.Closed: case InternalConnectionState.VersionMissmatch: case InternalConnectionState.VersionMatch: return(true); default: return(false); } }
/// <summary> /// Invokes the destroy of the client. /// Will kill the instance and close the connection /// </summary> private void InvokeDestroyClient(MonitoredClient client) { // Invoke callback with closing state to enable safe communication shutdown HandleConnectionState(client, InternalConnectionState.Closing); lock (client) { if (client.Instance == null) { return; } try { client.Instance.Close(); } catch { client.Instance.Abort(); } } }
/// <summary> /// Invokes the creation of the client. Will instantiate the client object and raises the /// success event. /// </summary> private void InvokeCreateClient(MonitoredClient client) { try { if (client.State == InternalConnectionState.VersionMatch) { client.Instance = InstantiateClientObject(client); client.State = InternalConnectionState.Success; SetClientInfoState(client.ClientInfo, client.State); client.ConnectionCallback(client.ClientInfo.State, client.Instance); RaiseClientConnected(client.ServiceName); } else { client.ConnectionCallback(client.ClientInfo.State, client.Instance); } return; } catch (CommunicationException e) { Logger.LogException(LogLevel.Error, e, "Can't open connection to {0}", client.ServiceName); } catch (TimeoutException e) { Logger.LogException(LogLevel.Error, e, "Can't open connection to {0}", client.ServiceName); } catch (Exception e) { Logger.LogException(LogLevel.Error, e, "Caught unexpected exception"); } client.State = InternalConnectionState.FailedTry; SetClientInfoState(client.ClientInfo, client.State); }
/// <summary> /// Receives the version without a given configuration. The configuration will be loaded from /// the version service /// </summary> private void ReceiveVersionWithoutConfig(MonitoredClient client) { Logger.Log(LogLevel.Debug, "Trying to get service configuration for service '{0}'", client.ServiceName); // Get all endpoints for this service var endpoints = VersionService.ServiceEndpoints(client.ServiceName); if (endpoints == null || endpoints.Length == 0) { HandleConnectionFailure(client, "Endpoint service or endpoint not available"); return; } // Select endpoint by matching version var clientVersion = Version.Parse(client.ClientInfo.ClientVersion); var endpoint = endpoints .Where(e => { var serverVersion = Version.Parse(e.Version); // The client factory can not connect WebHttp return(e.Binding > ServiceBindingType.WebHttp && VersionCompare.ClientMatch(serverVersion, clientVersion)); }).OrderByDescending(e => e.Version).FirstOrDefault(); if (endpoint == null) { HandleConnectionState(client, InternalConnectionState.VersionMissmatch); return; } // Finally reached the server client.Endpoint = endpoint; Logger.Log(LogLevel.Debug, "Got service configuration for service '{0}'", client.ServiceName); client.ClientInfo.ServerVersion = client.Endpoint.Version; client.ClientInfo.Uri = endpoint.Address; HandleConnectionState(client, InternalConnectionState.VersionMatch); }
/// <summary> /// Handles the state of the connection. /// </summary> private void HandleConnectionState(MonitoredClient client, InternalConnectionState newState) { var oldState = client.State; client.State = newState; SetClientInfoState(client.ClientInfo, newState); // Only report state changes if the state has changed if (newState == oldState) { return; } if ((oldState == InternalConnectionState.New && newState == InternalConnectionState.FailedTry) || (newState == InternalConnectionState.ConnectionLost) || (newState == InternalConnectionState.Closed)) { RaiseClientDisconnected(client.ServiceName); } if (!client.Destroy) { _threadContext.Invoke(() => InvokeCreateClient(client)); } }
/// <summary> /// Instantiate the client object. /// </summary> private ICommunicationObject InstantiateClientObject(MonitoredClient monitoredClient) { var clientParams = new List <object>(); var serviceConfiguration = monitoredClient.Endpoint; var monitoredClientConfig = monitoredClient.Config; var requiresAuthentication = (serviceConfiguration != null && serviceConfiguration.RequiresAuthentication) || (monitoredClientConfig != null && monitoredClientConfig.RequiresAuthentification); // Add callback context if the service is an net.tcp service var bindingType = serviceConfiguration?.Binding ?? monitoredClientConfig?.BindingType ?? ServiceBindingType.BasicHttp; if (bindingType == ServiceBindingType.NetTcp && monitoredClient.CallbackService != null) { if (monitoredClient.ClientType.GetConstructor(new[] { typeof(InstanceContext), typeof(Binding), typeof(EndpointAddress) }) == null) { throw new NotSupportedException("Generated client does not support custom callback. Use base class instead?"); } monitoredClient.CallbackContext = new InstanceContext(monitoredClient.CallbackService); clientParams.Add(monitoredClient.CallbackContext); // Called if the callback context was closed monitoredClient.CallbackContext.Closed += delegate(object sender, EventArgs args) { HandleContextEvent(sender, DisconnectReason.Closed); }; // Called if the callback context was faulted monitoredClient.CallbackContext.Faulted += delegate(object sender, EventArgs args) { HandleContextEvent(sender, DisconnectReason.Faulted); }; } var binding = monitoredClient.Binding ?? BindingFactory.CreateDefault(bindingType, requiresAuthentication, _proxyConfig); var endpointAddress = CreateEndpointAddress(serviceConfiguration, monitoredClientConfig); clientParams.Add(binding); clientParams.Add(endpointAddress); var clientObj = Activator.CreateInstance(monitoredClient.ClientType, clientParams.ToArray()) as ICommunicationObject; // Add behaviors monitoredClient.AddEndpointBehavior(clientObj, new CultureBehavior()); // Set credentials if (monitoredClientConfig != null) { var credentials = monitoredClient.GetClientCredentials(clientObj); credentials.UserName.UserName = monitoredClientConfig.UserName; credentials.UserName.Password = monitoredClientConfig.Password; } // ReSharper disable once PossibleNullReferenceException clientObj.Open(); // Register to communication faulted event for reconnect clientObj.Faulted += delegate(object sender, EventArgs args) { HandleClientEvent(sender, DisconnectReason.Faulted); }; // Register to communication closed event for reconnect clientObj.Closed += delegate(object sender, EventArgs args) { HandleClientEvent(sender, DisconnectReason.Closed); }; monitoredClient.InnerChannel = monitoredClient.GetInnerChannel(clientObj); return(clientObj); }
/// <summary> /// Adds a new client to the monitoring queue. /// </summary> private long AddClientToMonitor <T, TK>(string clientVersion, object callbackService, Action <ConnectionState, T> callback, Binding binding, ClientConfig config = null) where T : ClientBase <TK> where TK : class { // ReSharper disable UseObjectOrCollectionInitializer // ReSharper disable ConvertToLambdaExpression StartOnDemand(); var serviceName = typeof(TK).Name; var clientType = typeof(T); // Add client to collection var request = new MonitoredClient { Binding = binding, ClientType = clientType, ServiceName = serviceName, CallbackService = callbackService, ClientInfo = new WcfClientInfo(CreateClientId(), serviceName, clientVersion), }; //Wraps the client callback in safe delegate to avoid invoke exceptions request.ConnectionCallback = delegate(ConnectionState state, ICommunicationObject client) { try { callback(state, (T)client); } catch (Exception e) { Logger.LogException(LogLevel.Error, e, "Factory callback threw exception"); } }; //Set delegate to get the inner channel of the wcf client request.GetInnerChannel = client => ((T)client).InnerChannel; //Set delegate to get the ClientCredentials of the wcf client request.GetClientCredentials = client => ((T)client).ClientCredentials; //Set delegate to add additional endpoint behaviors request.AddEndpointBehavior = (client, behavior) => ((T)client).Endpoint.Behaviors.Add(behavior); Logger.Log(LogLevel.Info, "Creating WCF client {0} for '{1}', client version {2}, min. service version {3}", request.ClientInfo.Id, serviceName, clientVersion, request.ClientInfo.MinServerVersion); if (config != null) { request.Config = config; request.ClientInfo.Uri = CreateEndpointAddress(request.Endpoint, request.Config).Uri.ToString(); } lock (_monitoredClients) _monitoredClients.Add(request); _clientInfos.Add(request.ClientInfo); RaiseClientInfoChanged(request.ClientInfo); return(request.ClientInfo.Id); }