/// <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);
            }
        }
Esempio n. 3
0
        /// <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);
        }