Esempio n. 1
0
        //Init ======================= Init:
        private async Task <ConfigureUtil> init(Action <ConfigureUtil> configure)
        {
            this.runtimeLog = new LogDispatcher(this.Clock);
            lock (lockRunning) {
                if (__running__)
                {
                    return(null);
                }
                __running__ = true;
            }
            this.AddSingletonDependency(this, typeof(ICreateInstanceActorForScene));

            //Get configuration from the user:
            var config = new ConfigureUtil();

            configure(config);
            config.Sanitize();

            //Add this director to the static list of running directors:
            lockDirectors.EnterWriteLock();
            try {
                if (directors.ContainsKey(config.DirectorName))
                {
                    throw new ApplicationException($"There is already a running director with the name '{config.DirectorName}'");
                }
                directors[config.DirectorName] = this;
            }
            finally {
                lockDirectors.ExitWriteLock();
            }

            //Get an instance of the start up log, or throw an exception:
            var   log = new LogDispatcherForActor(new LogDispatcher(this.Clock), "Before Start");
            Actor startUpLogAsActor;

            {
                var startUpLogInstantiator = new ActinInstantiator(config.StartUpLogType);
                if (!startUpLogInstantiator.Build((t) => {
                    throw new ApplicationException($"{config.StartUpLogType.Name} is being used as the 'StartUp' Log, and must not have any dependencies.");
                }))
                {
                    throw new ApplicationException($"{config.StartUpLogType.Name} is being used as the 'StartUp' Log, and must not have any dependencies.");
                }
                lock (lockInstantiators) {
                    instantiators[config.StartUpLogType] = startUpLogInstantiator;
                }
                var startUpLog = startUpLogInstantiator.GetSingletonInstance(this) as IActinLogger;
                if (startUpLog == null)
                {
                    throw new ApplicationException("The 'StartUp' Log must implement IActinLogger.");
                }
                startUpLogAsActor = startUpLog as Actor;
                log.AddDestination(startUpLog);

                if (startUpLog is ActinStandardLogger && !string.IsNullOrWhiteSpace(config.StandardLogOutputFolder))
                {
                    var standardLogger = startUpLog as ActinStandardLogger;
                    standardLogger.SetClock(this.Clock);
                    standardLogger.SetLogFolderPath(config.StandardLogOutputFolder);
                }
            }

            try {
                //Do manual user start up:
                log.Info("Director Initializing");
                await config.RunBeforeStart(new ActorUtil(null, this.Clock) {
                    Log = log,
                });

                //Do automated DI startup:
                foreach (var a in config.AssembliesToCheckForDI)
                {
                    try {
                        foreach (var t in a.GetTypes())
                        {
                            if (t.HasAttribute <SingletonAttribute>() || t.HasAttribute <InstanceAttribute>())
                            {
                                lock (lockInstantiators) {
                                    if (!instantiators.ContainsKey(t))
                                    {
                                        //If it's already contained, then it was manually added as a Singleton dependency.
                                        //We can't add it again, as when manually added, a singleton instance was provided.
                                        instantiators[t] = new ActinInstantiator(t);
                                    }
                                }
                            }
                        }
                    }
                    catch (Exception ex) {
                        var msg = $"Actin Failed in assembly {a.FullName}. Inner Exception: {ex.Message}";
                        log.Error(msg, ex);
                        await runStartUpLog();

                        throw new Exception(msg, ex);
                    }
                }

                lock (lockInstantiators) {
                    //At this point, we should only have manually added singletons, and attribute marked Singleton or Instance classes.
                    var rootableInstantiators = instantiators.Values.ToList();
                    rootableInstantiators = rootableInstantiators.Where(config.RootActorFilter).ToList();
                    var skipped = new List <ActinInstantiator>();
                    foreach (var instantiator in rootableInstantiators)
                    {
                        try {
                            var skippedBecauseConcreteLineageRequired = !instantiator.Build(t => {
                                if (!this.instantiators.TryGetValue(t, out var dependencyInstantiator))
                                {
                                    dependencyInstantiator = new ActinInstantiator(t);
                                    this.instantiators[t]  = dependencyInstantiator;
                                }
                                return(dependencyInstantiator);
                            });
                            if (skippedBecauseConcreteLineageRequired)
                            {
                                skipped.Add(instantiator);
                            }
                        }
                        catch (Exception ex) {
                            throw new ApplicationException($"Failed to build rootable type {instantiator.Type.Name}: {ex.Message}", ex);
                        }
                    }

                    var skippedAndNeverBuilt = skipped.Where(x => !x.WasBuilt).ToList();
                    if (skippedAndNeverBuilt.Any())
                    {
                        throw new AggregateException(skippedAndNeverBuilt.Select(
                                                         x => new ApplicationException($"{x.Type.Name} has a concrete [Parent] or [Sibling], but its parent was not found in the dependency chain."
                                                                                       + "Most likely you forgot to mark the parent class with a [Singleton] or [Instance] attribute."
                                                                                       + "If the Parent is a Scene, or not always available, then you must instead use [FlexibleParent] or [FlexibleSibling]."
                                                                                       + "Note that the flexible attributes do not do type checking on start-up.")));
                    }

                    foreach (var singletonInstantiator in rootableInstantiators.Where(x => x.IsRootSingleton))
                    {
                        var singleton = singletonInstantiator.GetSingletonInstance(this);
                    }
                }

                if (!TryGetSingleton(config.RuntimeLogType, out var rtLog))
                {
                    throw new ApplicationException($"Actin failed to get a singleton instance of the 'Runtime' log. Ensure you've marked {config.RuntimeLogType.Name} with the Singleton attribute, or manually added it to the Director as a singleton.");
                }
                var rtLogAsIActinLogger = rtLog as IActinLogger;
                if (rtLogAsIActinLogger == null)
                {
                    throw new ApplicationException($"{config.RuntimeLogType} must implement IActinLogger, as it is being used as the 'Runtime' Log.");
                }

                runtimeLog.AddDestination(rtLogAsIActinLogger);
                await config.RunAfterStart(new ActorUtil(null, this.Clock) {
                    Log = new LogDispatcherForActor(runtimeLog, "After Start"),
                });

                return(config);
            }
            catch (Exception ex) when(logFailedStartup(ex))
            {
                //Exception is always unhandled, this is a nicer way to ensure logging before the exception propagates.
                return(null);
            }

            bool logFailedStartup(Exception ex)
            {
                log.Log(new ActinLog {
                    Time        = Clock.Now,
                    Location    = "StartUp",
                    UserMessage = "Actin failed to start.",
                    Details     = ex?.ToString(),
                    Type        = LogType.Error,
                });
                runStartUpLog().Wait();
                return(false);
            }

            async Task runStartUpLog()
            {
                try {
                    if (startUpLogAsActor != null)
                    {
                        var disposeHandle = await startUpLogAsActor.Init(() => new DispatchData {
                            MainLog = new ConsoleLogger(),
                        });

                        if (disposeHandle != null)
                        {
                            lock (lockDisposeHandles) {
                                disposeHandles.Add(disposeHandle);
                            }
                        }
                        await startUpLogAsActor.Run(() => new DispatchData {
                            MainLog = new ConsoleLogger(),
                        });
                    }
                }
                catch {
                    //Nowhere to put this if the log is failing.
                }
            }
        }