Ejemplo n.º 1
0
        public void OnGrandOutputEventInfo(ReceivedData receivedData)
        {
            var version = Convert.ToInt32(receivedData.ServerClientSession.ClientData["LogEntryVersion"]);

            _memoryStream.SetLength(0);
            _memoryStream.Write(receivedData.Data.ToArray(), 0, receivedData.Data.Count);
            _memoryStream.Seek(0, SeekOrigin.Begin);

            var logEntry = LogEntry.Read(_binaryReader, version, out _) as IMulticastLogEntry;

            receivedData.ServerClientSession.ClientData.TryGetValue("AppName", out var appName);
            var alertEntry = new AlertEntry(logEntry, appName);

            List <IAlertModel> faulty = null;

            foreach (var alert in _alerts)
            {
                try
                {
                    if (!alert.Condition(alertEntry))
                    {
                        continue;
                    }

                    _activityMonitor.Info("An alert has been sent");
                    foreach (var sender in alert.Senders)
                    {
                        sender.Send(alertEntry);
                    }
                }
                catch (Exception exception)
                {
                    const string message = "Alert crashed.";
                    _activityMonitor.Fatal(message, exception);
                    if (faulty == null)
                    {
                        faulty = new List <IAlertModel>();
                    }
                    faulty.Add(alert);
                }
            }

            if (faulty == null)
            {
                return;
            }

            foreach (var faultyAlert in faulty)
            {
                _alerts.Remove(faultyAlert);
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Deletes a file or folder. It must be physically accessible
        /// (<see cref="IFileInfo.PhysicalPath"/> must not be null): if inside a <see cref="GitFolder"/>, it must
        /// be a in the current head (ie. corresponds to a file in the current working directory).
        /// </summary>
        /// <param name="m">The monitor.</param>
        /// <param name="subPath">The item path to delete.</param>
        /// <returns>True on success, false on error (error is logged into the monitor).</returns>
        public bool Delete(IActivityMonitor m, NormalizedPath subPath)
        {
            IFileInfo info = GetFileInfo(subPath);

            if (info.Exists)
            {
                if (info.PhysicalPath == null)
                {
                    m.Error($"'{subPath}' to delete is not physically available.");
                    return(false);
                }
                try
                {
                    if (info.IsDirectory)
                    {
                        m.Info($"Deleting folder '{subPath}'.");
                        FileHelper.RawDeleteLocalDirectory(m, info.PhysicalPath);
                    }
                    else
                    {
                        m.Info($"Deleting file '{subPath}'.");
                        File.Delete(info.PhysicalPath);
                    }
                }
                catch (Exception ex)
                {
                    m.Fatal(ex);
                    return(false);
                }
            }
            return(true);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Copy a content to a <paramref name="destination"/> path in this
        /// file system.
        /// The destination must not be an existing folder and must be physically accessible
        /// (<see cref="IFileInfo.PhysicalPath"/> must not be null): if inside a <see cref="GitFolder"/>, it must
        /// be a in the current head (ie. corresponds to a file in the current working directory).
        /// </summary>
        /// <param name="m">The activity monitor.</param>
        /// <param name="content">The content source.</param>
        /// <param name="destination">The target path in this file system.</param>
        /// <returns>True on success, false on error.</returns>
        public bool CopyTo(IActivityMonitor m, Stream content, NormalizedPath destination)
        {
            IFileInfo fDest = GetWritableDestination(m, ref destination);

            if (fDest == null)
            {
                return(false);
            }
            using (m.OpenInfo($"{(fDest.Exists ? "Replacing" : "Creating")} {destination}."))
            {
                try
                {
                    using (var d = new FileStream(fDest.PhysicalPath, FileMode.Create, FileAccess.Write, FileShare.Read))
                    {
                        content.CopyTo(d);
                    }
                    return(true);
                }
                catch (Exception ex)
                {
                    m.Fatal(ex);
                    return(false);
                }
            }
        }
Ejemplo n.º 4
0
 public bool FetchBranches(IActivityMonitor m, bool originOnly = true)
 {
     using (m.OpenInfo($"Fetching {(originOnly ? "origin" : "all remotes")} in repository '{SubPath}'."))
     {
         try
         {
             foreach (Remote remote in Git.Network.Remotes.Where(r => !originOnly || r.Name == "origin"))
             {
                 m.Info($"Fetching remote '{remote.Name}'.");
                 IEnumerable <string> refSpecs = remote.FetchRefSpecs.Select(x => x.Specification).ToArray();
                 Commands.Fetch(Git, remote.Name, refSpecs, new FetchOptions()
                 {
                     CredentialsProvider = (url, user, cred) => PATCredentialsHandler(m),
                     TagFetchMode        = TagFetchMode.All
                 }, $"Fetching remote '{remote.Name}'.");
             }
             return(true);
         }
         catch (Exception ex)
         {
             using (m.OpenFatal("This error need manual fix : "))
             {
                 m.Fatal(ex);
             }
             return(false);
         }
     }
 }
Ejemplo n.º 5
0
        PocoType?CreatePocoType(IActivityMonitor monitor, Type t)
        {
            if (!_typeFilter(monitor, t))
            {
                monitor.Info($"Poco interface '{t.AssemblyQualifiedName}' is excluded.");
                return(null);
            }
            PocoType?theOnlyRoot = null;

            foreach (Type b in t.GetInterfaces())
            {
                if (b == typeof(IPoco) || b == typeof(IClosedPoco))
                {
                    continue;
                }
                // Base interface must be a IPoco. This is a security to avoid "normal" interfaces to appear
                // by mistake in a Poco.
                if (!typeof(IPoco).IsAssignableFrom(b))
                {
                    monitor.Fatal($"Poco interface '{t.AssemblyQualifiedName}' extends '{b.Name}'. '{b.Name}' must be marked with CK.Core.IPoco interface.");
                    return(null);
                }
                // Attempts to register the base if and only if it is not a "definer".
                if (_actualPocoPredicate(monitor, b))
                {
                    var baseType = DoRegisterInterface(monitor, b);
                    if (baseType == null)
                    {
                        return(null);
                    }
                    // Detect multiple root Poco.
                    if (theOnlyRoot != null)
                    {
                        if (theOnlyRoot != baseType.Root)
                        {
                            monitor.Fatal($"Poco interface '{t.AssemblyQualifiedName}' extends both '{theOnlyRoot.Type.Name}' and '{baseType.Root.Type.Name}' (via '{baseType.Type.Name}').");
                            return(null);
                        }
                    }
                    else
                    {
                        theOnlyRoot = baseType.Root;
                    }
                }
            }
            return(new PocoType(t, theOnlyRoot));
        }
 void DemoLogs( IActivityMonitor m, FileInfo f, Exception ex )
 {
     m.Trace().Send( "Data from '{0}' processed.", f.Name );
     m.Info().Send( ex, "An error occurred while processing '{0}'. Process will be retried later.", f.Name );
     m.Warn().Send( "File '{0}' is too big ({1} Kb). It must be less than 50Kb.", f.Name, f.Length / 1024 );
     m.Error().Send( ex, "File '{0}' can not be processed.", f.Name );
     m.Fatal().Send( ex, "This will cancel the whole operation." );
 }
Ejemplo n.º 7
0
 void DemoLogs(IActivityMonitor m, FileInfo f, Exception ex)
 {
     m.Trace().Send("Data from '{0}' processed.", f.Name);
     m.Info().Send(ex, "An error occurred while processing '{0}'. Process will be retried later.", f.Name);
     m.Warn().Send("File '{0}' is too big ({1} Kb). It must be less than 50Kb.", f.Name, f.Length / 1024);
     m.Error().Send(ex, "File '{0}' can not be processed.", f.Name);
     m.Fatal().Send(ex, "This will cancel the whole operation.");
 }
Ejemplo n.º 8
0
        private void DoCallStObjConstruct(IActivityMonitor monitor, BuildValueCollector valueCollector, IStObjValueResolver?valueResolver, MethodInfo stobjConstruct, IReadOnlyList <MutableParameter> mutableParameters)
        {
            object?[] parameters = new object[mutableParameters.Count];
            int       i          = 0;

            foreach (MutableParameter t in mutableParameters)
            {
                // We inject our "setup monitor" for IActivityMonitor parameter type.
                if (t.IsSetupLogger)
                {
                    t.SetParameterValue(monitor);
                    t.BuilderValueIndex = Int32.MaxValue;
                }
                else
                {
                    MutableItem?resolved = null;
                    if (t.Value == System.Type.Missing)
                    {
                        // Parameter reference have already been resolved as dependencies for graph construction since
                        // no Value has been explicitly set for the parameter.
                        resolved = t.CachedResolvedStObj;
                        if (resolved != null)
                        {
                            Debug.Assert(resolved.InitialObject != System.Type.Missing);
                            t.SetParameterValue(resolved.InitialObject);
                        }
                    }
                    if (valueResolver != null)
                    {
                        valueResolver.ResolveParameterValue(monitor, t);
                    }
                    if (t.Value == System.Type.Missing && !t.IsRealParameterOptional)
                    {
                        if (!t.IsOptional)
                        {
                            // By throwing an exception here, we stop the process and avoid the construction
                            // of an invalid object graph...
                            // This behavior (FailFastOnFailureToResolve) may be an option once. For the moment: log the error.
                            monitor.Fatal($"{t}: Unable to resolve non optional. Attempting to use a default value to continue the setup process in order to detect other errors.");
                        }
                        Debug.Assert(t.Type != null);
                        t.SetParameterValue(t.Type.IsValueType ? Activator.CreateInstance(t.Type) : null);
                    }
                    if (resolved != null && t.Value == resolved.InitialObject)
                    {
                        t.BuilderValueIndex = -(resolved.IndexOrdered + 1);
                    }
                    else
                    {
                        t.BuilderValueIndex = valueCollector.RegisterValue(t.Value);
                    }
                }
                parameters[i++] = t.Value;
            }
            stobjConstruct.Invoke(_leafData.StructuredObject, parameters);
        }
 public void LogErrorAndWarnings(IActivityMonitor monitor)
 {
     Throw.CheckNotNullArgument(monitor);
     using (monitor.OpenTrace($"Collector summary:"))
     {
         if (PocoSupport == null)
         {
             monitor.Fatal($"Poco support failed!");
         }
         RealObjects.LogErrorAndWarnings(monitor);
         AutoServices.LogErrorAndWarnings(monitor);
     }
 }
Ejemplo n.º 10
0
        /// <summary>
        /// Gets the simple git version <see cref="RepositoryInfo"/> from a branch.
        /// Returns null if an error occurred or if RepositoryInfo.xml has not been successfully read.
        /// </summary>
        /// <param name="m">The monitor to use.</param>
        /// <param name="branchName">Defaults to <see cref="CurrentBranchName"/>.</param>
        /// <returns>The RepositoryInfo or null if it it cannot be obtained.</returns>
        public RepositoryInfo ReadRepositoryVersionInfo(IActivityMonitor m, string branchName = null)
        {
            if (branchName == null)
            {
                branchName = CurrentBranchName;
            }
            try
            {
                Branch b = Git.Branches[branchName];
                if (b == null)
                {
                    m.Error($"Unknown branch {branchName}.");
                    return(null);
                }
                var pathOpt = b.IsRemote
                                ? SubPath.AppendPart("remotes").Combine(b.FriendlyName)
                                : SubPath.AppendPart("branches").AppendPart(branchName);

                pathOpt = pathOpt.AppendPart("RepositoryInfo.xml");
                var fOpt = FileSystem.GetFileInfo(pathOpt);
                if (!fOpt.Exists)
                {
                    m.Error($"Missing required {pathOpt} file.");
                    return(null);
                }
                var opt = RepositoryInfoOptions.Read(fOpt.ReadAsXDocument().Root);
                opt.StartingBranchName = branchName;
                var result = new RepositoryInfo(Git, opt);
                if (result.RepositoryError != null)
                {
                    m.Error($"Unable to read RepositoryInfo. RepositoryError: {result.RepositoryError}.");
                    return(null);
                }
                if (result.Error != null)
                {
                    m.Error(result.ReleaseTagError);
                    return(null);
                }
                return(result);
            }
            catch (Exception ex)
            {
                m.Fatal($"While reading version info for branch '{branchName}'.", ex);
                return(null);
            }
        }
Ejemplo n.º 11
0
 void LogDemo(IActivityMonitor m)
 {
     m.Info("This is an info.");
     using (m.OpenInfo($"This is an info group."))
     {
         m.Fatal($"Ouch! a faaaaatal.");
         m.OpenTrace($"A trace");
         var group = m.OpenInfo($"This is another group (trace).");
         {
             try
             {
                 throw new Exception();
             }
             catch (Exception ex)
             {
                 m.Error("An error occurred.", ex);
             }
         }
         m.CloseGroup("This is a close group.");
         group.Dispose();
     }
 }
Ejemplo n.º 12
0
 /// <summary>
 /// Builds and returns a <see cref="StObjCollectorResult"/> if no error occurred during type registration.
 /// On error, <see cref="StObjCollectorResult.HasFatalError"/> is true and this result should be discarded.
 /// If <see cref="RegisteringFatalOrErrorCount"/> is not equal to 0, this throws a <see cref="InvalidOperationException"/>.
 /// </summary>
 /// <returns>The result.</returns>
 public StObjCollectorResult GetResult()
 {
     if (_registerFatalOrErrorCount > 0)
     {
         throw new InvalidOperationException($"There are {_registerFatalOrErrorCount} registration errors.");
     }
     if (_computedResult)
     {
         throw new InvalidOperationException("Must be called once and only once.");
     }
     _computedResult = true;
     try
     {
         var(typeResult, orderedItems, buildValueCollector) = CreateTypeAndObjectResults();
         if (orderedItems != null)
         {
             // This is far from elegant but simplifies the engine object model:
             // We set the final ordered results on the crappy mutable EngineMap (that should
             // not exist and be replaced with intermediate - functional-like - value results).
             // But this would be a massive refactoring and this internal mutable state is, to be honest,
             // quite convenient!
             typeResult.SetFinalOrderedResults(orderedItems);
             if (!RegisterServices(typeResult))
             {
                 // Setting the valueCollector to null indicates the error to the StObjCollectorResult.
                 buildValueCollector = null;
             }
         }
         return(new StObjCollectorResult(typeResult, _tempAssembly, buildValueCollector));
     }
     catch (Exception ex)
     {
         _monitor.Fatal(ex);
         throw;
     }
 }
Ejemplo n.º 13
0
 StObjEngineResult DoRun(IStObjCollectorResultResolver?resolver)
 {
     Throw.CheckState("Run can be called only once.", !_hasRun);
     _hasRun = true;
     if (!_config.CheckAndValidate(_monitor))
     {
         return(new StObjEngineResult(false, _config));
     }
     if (_ckSetupConfig != null)
     {
         _config.ApplyCKSetupConfiguration(_monitor, _ckSetupConfig);
     }
     if (!_config.Initialize(_monitor, out bool canSkipRun))
     {
         return(new StObjEngineResult(false, _config));
     }
     // If canSkipRun is true here it means that regarding the 2 core generated artifacts, there is
     // nothing to do.
     using var _   = _monitor.OpenInfo("Running StObjEngine setup.");
     _status       = new Status(_monitor);
     _startContext = new StObjEngineConfigureContext(_monitor, _config, _status, canSkipRun);
     try
     {
         // Creating and configuring the aspects.
         _startContext.CreateAndConfigureAspects(() => _status.Success = false);
         if (_status.Success && _startContext.CanSkipRun)
         {
             _monitor.Info("Skipping run.");
             _status.Success |= UpdateGeneratedArtifacts(_config.Groups);
             _startContext.OnSkippedRun(() => _status.Success = false);
             return(new StObjEngineResult(_status.Success, _config));
         }
         if (_status.Success)
         {
             StObjEngineRunContext runCtx = new StObjEngineRunContext(_monitor, _startContext);
             // Creates the StObjCollectorResult for each group of compatible BinPaths
             // and instantiates a StObjEngineRunContext.GenPath that exposes the engine map and the dynamic assembly
             // for each of them through IGeneratedBinPath, ICodeGenerationContext and ICSCodeGenerationContext.
             foreach (var g in _config.Groups)
             {
                 using (_monitor.OpenInfo(g.IsUnifiedPure
                                             ? $"Analyzing types from Unified Working directory '{g.Configuration.Path}'."
                                             : $"Analyzing types from BinPaths '{g.SimilarConfigurations.Select( b => b.Path.Path ).Concatenate( "', '" )}'."))
                 {
                     StObjCollectorResult?r = resolver?.GetResult(g) ?? SafeBuildStObj(g);
                     if (r == null)
                     {
                         _status.Success = false;
                         break;
                     }
                     runCtx.AddResult(g, r);
                 }
             }
             // This is where all aspects runs before Code generation: this is where CK.Setupable.Engine.SetupableAspect.Run():
             // - Builds the ISetupItem items (that support 3 steps setup) associated to each StObj (relies on EngineMap.StObjs.OrderedStObjs).
             // - Projects the StObj topological order on the ISetupItem items graph.
             // - Calls the DynamicItemInitialize methods that can create new Setup items (typically as child of existing containers like SqlProcedure on SqlTable)
             // - The ISetupItems are then sorted topologically (this is the second graph).
             // - The Init/Install/Settle steps are executed.
             if (_status.Success)
             {
                 runCtx.RunAspects(() => _status.Success = false, postCode: false);
             }
             // Code Generation.
             if (_status.Success)
             {
                 using (_monitor.OpenInfo("Code Generation."))
                 {
                     foreach (var g in runCtx.AllBinPaths)
                     {
                         if (!g.Result.GenerateSourceCode(_monitor,
                                                          g,
                                                          _config.Configuration.InformationalVersion,
                                                          runCtx.Aspects.OfType <ICSCodeGenerator>()))
                         {
                             _status.Success = false;
                             break;
                         }
                     }
                 }
             }
             // Handling generated artifacts.
             _status.Success &= UpdateGeneratedArtifacts(runCtx.AllBinPaths.Select(g => g.ConfigurationGroup));
             // Run the aspects Post Code Generation.
             if (_status.Success)
             {
                 runCtx.RunAspects(() => _status.Success = false, postCode: true);
             }
             // Secure errors (ensure error log and logs error path).
             if (!_status.Success)
             {
                 // Emit the last error log path as an error and ensure that at least one error
                 // has been logged on failure.
                 var errorPath = _status.LastErrorPath;
                 if (errorPath == null || errorPath.Count == 0)
                 {
                     _monitor.Fatal("Success status is false but no error has been logged.");
                 }
                 else
                 {
                     _monitor.Error(errorPath.ToStringPath());
                 }
             }
             // Always runs the aspects Termination.
             var termCtx = new StObjEngineTerminateContext(_monitor, runCtx);
             termCtx.TerminateAspects(() => _status.Success = false);
         }
         return(new StObjEngineResult(_status.Success, _config));
     }
     finally
     {
         DisposeDisposableAspects();
         _status.Dispose();
     }
 }
        void OnTime(object state)
        {
            // This is a simple lock that is non reentrant.
            // One may perfectly have used a standard .Net lock object here (Monitor.Enter/Exit) but
            // this is even lighter than standard lock.
            if (Interlocked.CompareExchange(ref _reentrancyFlag, 1, 0) == 0)
            {
                HandleDirtyOption();
                using (_monitor.OpenInfo($"Work n°{++_workCount}."))
                {
                    switch (_workCount % 15)
                    {
                    case 0: _monitor.Debug("A debug line (most verbose level)."); break;

                    case 1: _monitor.Trace("A trace line."); break;

                    case 2: _monitor.Info("An info line."); break;

                    case 3: _monitor.Warn("A warning line."); break;

                    case 4: _monitor.Error("An error line."); break;

                    case 5: _monitor.Fatal("A fatal line (most severe level)."); break;

                    case 6:
                    {
                        _monitor.Info(@"throw new Exception( ""Unhandled exception, directly on the thread pool, during the timer handling."" );

My first idea was that such an exception directly on the timer thread would have been trapped by AppDomain.CurrentDomain.UnhandledException. This was not the case:
see the (still opened) issue: https://github.com/dotnet/extensions/issues/1836

""
The application exits because entire process is crashing. The host isn't gracefully shutting down, the process is just dying.
This happens when using timers or the thread pool directly without a try catch.
""
");
                        break;
                    }

                    case 7:
                    {
                        _monitor.Trace(@"Calling: Task.Run( () => throw new Exception( ""Unhandled exception on the default Task scheduler."" ) );

This 'lost' exception will be hooked by a TaskScheduler.UnobservedTaskException an logged... but at the next GC time!
");
                        Task.Run(() => throw new Exception("Unhandled exception on the default Task scheduler."));
                        break;
                    }

                    case 8:
                    {
                        if (_dotNetLogger == null)
                        {
                            _monitor.Info("Creating the '.Net Standard Demo' logger (this case n°8 emits a .Net LogTrace below).");
                            _dotNetLogger = _dotNetLoggerFactory.CreateLogger(".Net Standard Demo");
                        }
                        _dotNetLogger.LogTrace($".Net LogTrace (most verbose .Net log level).");
                        break;
                    }

                    case 9: _dotNetLogger.LogDebug($".Net LogDebug (Debug is less verbose than Trace fo .Net logs)."); break;

                    case 10: _dotNetLogger.LogInformation($".Net LogInformation."); break;

                    case 11: _dotNetLogger.LogWarning($".Net LogWarning."); break;

                    case 12: _dotNetLogger.LogError($".Net LogError."); break;

                    case 13: _dotNetLogger.LogCritical($".Net LogCritical (most severe .Net level)."); break;

                    default:
                        using (_monitor.OpenInfo("Calling the Garbage collector: this will release the Task.UnobservedExceptions."))
                        {
                            GC.Collect();
                            _monitor.Trace("Waiting for completion.");
                            GC.WaitForFullGCComplete();
                        }
                        break;
                    }
                }
                HandleDirtyOption();
                Interlocked.Exchange(ref _reentrancyFlag, 0);
            }
        }
Ejemplo n.º 15
0
        void OnTime(object state)
        {
            // This is a simple lock that is non reentrant.
            // One may perfectly have used a standard .Net lock object here (Monitor.Enter/Exit) but
            // this is lighter than standard lock (since we don't want to wait to enter the lock, we just want to
            // skip the run).
            if (Interlocked.CompareExchange(ref _reentrancyFlag, 1, 0) == 0)
            {
                HandleDirtyOption();
                using (_monitor.OpenInfo($"Work n°{++_workCount}."))
                {
                    switch (_workCount % 16)
                    {
                    case 0: _monitor.Debug("A debug line (most verbose level)."); break;

                    case 1: _monitor.Trace("A trace line."); break;

                    case 2: _monitor.Info("An info line."); break;

                    case 3: _monitor.Warn("A warning line."); break;

                    case 4: _monitor.Error("An error line."); break;

                    case 5: _monitor.Fatal("A fatal line (most severe level)."); break;

                    case 6:
                    {
                        _monitor.Info(@"This would crash the entire process: throw new Exception( ""Unhandled exception, directly on the thread pool, during the timer handling."" ); ");
                        _monitor.Debug(@"
My first idea was that such an exception directly on the timer thread would have been trapped by AppDomain.CurrentDomain.UnhandledException. This is not the case:
see the (closed) issue: https://github.com/dotnet/extensions/issues/1836

""
The application exits because entire process is crashing. The host isn't gracefully shutting down, the process is just dying.
This happens when using timers or the thread pool directly without a try catch.
""
");
                        break;
                    }

                    case 7:
                    {
                        if (_options.CurrentValue.ThrowTaskSchedulerUnobservedTaskException)
                        {
                            _monitor.Trace(@"Calling: Task.Run( () => throw new Exception( ""Unhandled exception on the default Task scheduler."" ) );

This 'lost' exception will be hooked by a TaskScheduler.UnobservedTaskException an logged... but at the next GC time!
");
                            Task.Run(() => throw new Exception("Unhandled exception on the default Task scheduler."));
                        }
                        else
                        {
                            _monitor.Trace(@"Throwing unhandled exception has been skipped since ThrowTaskSchedulerUnobservedTaskException is false.");
                        }
                        break;
                    }

                    case 8:
                    {
                        if (_dotNetLogger == null)
                        {
                            _monitor.Info("Creating the '.Net Standard Demo' logger (this case n°8 emits a .Net LogTrace below).");
                            _dotNetLogger = _dotNetLoggerFactory.CreateLogger(".Net Standard Demo");
                        }
                        _dotNetLogger.LogTrace($".Net LogTrace (most verbose .Net log level).");
                        break;
                    }

                    case 9: _dotNetLogger.LogDebug($".Net LogDebug (Debug is less verbose than Trace fo .Net logs)."); break;

                    case 10: _dotNetLogger.LogInformation($".Net LogInformation."); break;

                    case 11: _dotNetLogger.LogWarning($".Net LogWarning."); break;

                    case 12: _dotNetLogger.LogError($".Net LogError."); break;

                    case 13: _dotNetLogger.LogCritical($".Net LogCritical (most severe .Net level)."); break;

                    case 14:
                    {
                        CheckSafeFailFast(() =>
                            {
                                Debug.Assert(false, "Debug.Assert failure results in a CK.Monitoring.MonitoringFailFastException and not a fail fast of the application.");
                            });

                        CheckSafeFailFast(() =>
                            {
                                Trace.Assert(false, "Trace.Assert failure results in a CK.Monitoring.MonitoringFailFastException and not a fail fast of the application.");
                            });

                        void CheckSafeFailFast(Action a)
                        {
                            try
                            {
                                a();
                            }
                            catch (CK.Monitoring.MonitoringFailFastException ex)
                            {
                                _monitor.Info($"Received a MonitoringFailFastException with message: {ex.Message}");
                            }
                        }

                        break;
                    }

                    default:
                        using (_monitor.OpenInfo("Calling the Garbage collector: this will release the TaskScheduler.UnobservedTaskException."))
                        {
                            GC.Collect();
                            _monitor.Trace("Waiting for completion.");
                            GC.WaitForFullGCComplete();
                        }
                        break;
                    }
                }
                HandleDirtyOption();
                Interlocked.Exchange(ref _reentrancyFlag, 0);
            }
        }
Ejemplo n.º 16
0
        /// <inheritdoc/>
        public async Task <ConnectResult> ConnectAsync(IActivityMonitor?m, MqttClientCredentials?credentials = null, OutgoingLastWill?lastWill = null)
        {
            if (IsConnected)
            {
                throw new InvalidOperationException("This client is already connected.");
            }
            using (m?.OpenTrace("Connecting..."))
            {
                try
                {
                    (IOutgoingPacketStore store, IIncomingPacketStore packetIdStore) = await _config.StoreFactory.CreateAsync(m, _pConfig, _config, _config.ConnectionString, credentials?.CleanSession ?? true);

                    IMqttChannel channel = await _config.ChannelFactory.CreateAsync(m, _config.ConnectionString);

                    ConnectAckReflex     connectAckReflex = new();
                    Task <ConnectResult> connectedTask    = connectAckReflex.Task;
                    var             output = new OutputPump(this, _pConfig);
                    OutputProcessor outputProcessor;
                    var             input = new InputPump(this, channel.DuplexPipe.Input, connectAckReflex.ProcessIncomingPacket);
                    OpenPumps(m, new ClientState(input, output, channel, packetIdStore, store));
                    ReflexMiddlewareBuilder builder = new ReflexMiddlewareBuilder()
                                                      .UseMiddleware(new PublishReflex(_config, packetIdStore, OnMessage, output))
                                                      .UseMiddleware(new PublishLifecycleReflex(packetIdStore, store, output))
                                                      .UseMiddleware(new SubackReflex(store))
                                                      .UseMiddleware(new UnsubackReflex(store));
                    if (_config.KeepAliveSeconds == 0)
                    {
                        outputProcessor = new OutputProcessor(output, channel.DuplexPipe.Output, store);
                    }
                    else
                    {
                        OutputProcessorWithKeepAlive withKeepAlive = new(_config, output, channel.DuplexPipe.Output, store);;
                        outputProcessor = withKeepAlive;
                        _ = builder.UseMiddleware(withKeepAlive);
                    }
                    output.StartPumping(outputProcessor);
                    connectAckReflex.Reflex = builder.Build(InvalidPacket);
                    OutgoingConnect             outgoingConnect    = new(_pConfig, _config, credentials, lastWill);
                    CancellationTokenSource     cts                = new(_config.WaitTimeoutMilliseconds);
                    IOutgoingPacket.WriteResult writeConnectResult = await outgoingConnect.WriteAsync(_pConfig.ProtocolLevel, channel.DuplexPipe.Output, cts.Token);

                    if (writeConnectResult != IOutgoingPacket.WriteResult.Written)
                    {
                        _ = await CloseAsync(DisconnectedReason.None);

                        return(new ConnectResult(ConnectError.Timeout));
                    }
                    Task timeout = _config.DelayHandler.Delay(_config.WaitTimeoutMilliseconds, CloseToken);
                    _ = await Task.WhenAny(connectedTask, timeout);

                    // This following code wouldn't be better with a sort of ... switch/pattern matching ?
                    if (connectedTask.Exception is not null)
                    {
                        m?.Fatal(connectedTask.Exception);
                        _ = await CloseAsync(DisconnectedReason.None);

                        return(new ConnectResult(ConnectError.InternalException));
                    }
                    if (CloseToken.IsCancellationRequested)
                    {
                        _ = await CloseAsync(DisconnectedReason.None);

                        return(new ConnectResult(ConnectError.RemoteDisconnected));
                    }
                    if (!connectedTask.IsCompleted)
                    {
                        _ = await CloseAsync(DisconnectedReason.None);

                        return(new ConnectResult(ConnectError.Timeout));
                    }
                    ConnectResult res = await connectedTask;
                    if (res.ConnectError != ConnectError.Ok)
                    {
                        _ = await CloseAsync(DisconnectedReason.None);

                        return(new ConnectResult(res.ConnectError));
                    }
                    bool askedCleanSession = credentials?.CleanSession ?? true;
                    if (askedCleanSession && res.SessionState != SessionState.CleanSession)
                    {
                        _ = await CloseAsync(DisconnectedReason.None);

                        return(new ConnectResult(ConnectError.ProtocolError_SessionNotFlushed));
                    }
                    if (res.SessionState == SessionState.CleanSession)
                    {
                        ValueTask task = packetIdStore.ResetAsync();
                        await store.ResetAsync();

                        await task;
                    }
                    else
                    {
                        throw new NotImplementedException();
                    }
                    return(res);
                }
                catch (Exception e)
                {
                    m?.Error("Error while connecting, closing client.", e);
                    _ = await CloseAsync(DisconnectedReason.None);

                    return(new ConnectResult(ConnectError.InternalException));
                }
            }
        }