Example #1
0
        private ActinInstantiator getOrCreateExternalInstantiator(Type type)
        {
            if (!this.Running)
            {
                throw new AccessViolationException("The director has not yet started running.");
            }
            var success = false;
            ActinInstantiator inst;

            lock (lockInstantiators) {
                this.instantiators.TryGetValue(type, out inst);
            }
            if (!success)
            {
                inst = new ActinInstantiator(type);
                lock (lockInstantiators) {
                    //It's better to lock for the whole creation process, as this ensures all internal instantiator
                    //state is also locked. This only happens when a new type is discovered, so it shouldn't
                    //cause too much delay in exchange for that safety.
                    var skippedBecauseConcreteLineageRequired = !inst.Build(t => {
                        if (!this.instantiators.TryGetValue(t, out var dependencyInstantiator))
                        {
                            dependencyInstantiator = new ActinInstantiator(t);
                            this.instantiators[t]  = dependencyInstantiator;
                        }
                        return(dependencyInstantiator);
                    });
                    if (skippedBecauseConcreteLineageRequired)
                    {
                        throw new ApplicationException($"{type.Name} must remove the [Parent] or [Sibling] attribute. Top level external objects may not have Parents or Siblings injected.");
                    }
                    instantiators[type] = inst;
                }
            }
            return(inst);
        }
Example #2
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.
                }
            }
        }
        internal void ResolveDependencies(object instance, DependencyType dependencyType, object parent, ActinInstantiator parentInstantiator, Director director)
        {
            if (director is null)
            {
                throw new NullReferenceException("Director may not be null.");
            }
            var asActor = instance as Actor_SansType;

            if (asActor != null)
            {
                asActor.Instantiator = this; //Used for automatically disposing child dependencies.
                asActor.Util         = new ActorUtil(asActor, director.Clock);
            }
            Func <AccessorInstantiatorPair, bool> notSet = (x) => x.Accessor.GetVal(instance) == null;

            //Set and Resolve Singletons:
            foreach (var dep in SingletonDependencies.Where(notSet))
            {
                dep.Accessor.SetVal(instance, dep.Instantiator.GetSingletonInstance(director));
            }
            //Set Child Instances:
            var unresolvedInstanceDependencies = new List <AccessorInstantiatorPairWithInstance>();

            foreach (var dep in InstanceDependencies.Where(notSet))
            {
                var childInstance = dep.Instantiator.CreateNew();
                unresolvedInstanceDependencies.Add(new AccessorInstantiatorPairWithInstance {
                    Pair     = dep,
                    Instance = childInstance,
                });
                dep.Accessor.SetVal(instance, childInstance);
            }


            if (instance != null)   //It should never be null, but it doesn't hurt to be safe.
            //Note that it's important that we called this before we resolved child dependencies.
            //The director assumes that the order it receives dependencies in is the order in
            //which they should be initialized.
            {
                director.RegisterInjectedDependency(instance);
            }
            //Resolve Child Instances:
            foreach (var dep in unresolvedInstanceDependencies)
            {
                dep.Pair.Instantiator.ResolveDependencies(dep.Instance, DependencyType.Instance, instance, this, director);
            }

            if (dependencyType == DependencyType.Instance && parent != null)
            {
                //Means we can have parents and siblings:
                foreach (var dep in ParentDependencies.Where(notSet))
                {
                    try {
                        dep.Accessor.SetVal(instance, parent);
                    }
                    catch when(dep.Accessor.Markers.Contains(nameof(FlexibleParentAttribute)))
                    {
                        //Swallow the exception, as FlexibleParents may be different types.
                    }