1 /**
2  * Contains the implementation of the dependency container.
3  *
4  * Part of the Poodinis Dependency Injection framework.
5  *
6  * Authors:
7  *  Mike Bierlee, m.bierlee@lostmoment.com
8  * Copyright: 2014-2022 Mike Bierlee
9  * License:
10  *  This software is licensed under the terms of the MIT license.
11  *  The full terms of the license can be found in the LICENSE file.
12  */
13 
14 module poodinis.container;
15 
16 import poodinis.registration : Registration, singleInstance,
17     toConcreteTypeListString, initializeFactoryType;
18 import poodinis.autowire : AutowiredRegistration, AutowireInstantiationContext;
19 import poodinis.factory : ConstructorInjectingInstanceFactory;
20 import poodinis.valueinjection : ValueInjectionException;
21 import poodinis.altphobos : isFunction;
22 import poodinis.imports : createImportsString;
23 
24 import std.string : format;
25 import std.algorithm : canFind;
26 import std.traits : fullyQualifiedName, hasUDA, BaseTypeTuple;
27 import std.meta : AliasSeq;
28 
29 debug
30 {
31     import std.stdio;
32 }
33 
34 /**
35  * Exception thrown when errors occur while resolving a type in a dependency container.
36  */
37 class ResolveException : Exception
38 {
39     this(string message, TypeInfo resolveType)
40     {
41         super(format("Exception while resolving type %s: %s", resolveType.toString(), message));
42     }
43 
44     this(Throwable cause, TypeInfo resolveType)
45     {
46         super(format("Exception while resolving type %s", resolveType.toString()), cause);
47     }
48 }
49 
50 /**
51  * Exception thrown when errors occur while registering a type in a dependency container.
52  */
53 class RegistrationException : Exception
54 {
55     this(string message, TypeInfo registrationType)
56     {
57         super(format("Exception while registering type %s: %s",
58                 registrationType.toString(), message));
59     }
60 }
61 
62 /**
63  * Options which influence the process of registering dependencies
64  */
65 public enum RegistrationOption
66 {
67     none = 0,
68     /**
69      * Prevent a concrete type being registered on itself. With this option you will always need
70      * to use the supertype as the type of the dependency.
71      */
72     doNotAddConcreteTypeRegistration = 1 << 0,
73 }
74 
75 /**
76  * Options which influence the process of resolving dependencies
77  */
78 public enum ResolveOption
79 {
80     none = 0,
81     /**
82      * Registers the type you're trying to resolve before returning it.
83      * This essentially makes registration optional for resolving by concerete types.
84      * Resolinvg will still fail when trying to resolve a dependency by supertype.
85      */
86     registerBeforeResolving = 1 << 0,
87 
88     /**
89      * Does not throw a resolve exception when a type is not registered but will
90      * return null instead. If the type is an array, an empty array is returned instead.
91      */
92     noResolveException = 1 << 1
93 }
94 
95 /**
96  * Methods marked with this UDA within dependencies are called after that dependency
97  * is constructed by the dependency container.
98  *
99  * Multiple methods can be marked and will all be called after construction. The order in which
100  * methods are called is undetermined. Methods should have the signature void(void).
101  */
102 struct PostConstruct
103 {
104 }
105 
106 /**
107  * Methods marked with this UDA within dependencies are called before the container
108  * loses the dependency's registration.
109  *
110  * This method is called when removeRegistration or clearAllRegistrations is called.
111  * It will also be called when the container's destructor is called.
112  */
113 struct PreDestroy
114 {
115 }
116 
117 /**
118  * The dependency container maintains all dependencies registered with it.
119  *
120  * Dependencies registered by a container can be resolved as long as they are still registered with the container.
121  * Upon resolving a dependency, an instance is fetched according to a specific scope which dictates how instances of
122  * dependencies are created. Resolved dependencies will be autowired before being returned.
123  *
124  * In most cases you want to use a global singleton dependency container provided by getInstance() to manage all dependencies.
125  * You can still create new instances of this class for exceptional situations.
126  */
127 synchronized class DependencyContainer
128 {
129     private Registration[][TypeInfo] registrations;
130 
131     private Registration[] autowireStack;
132 
133     private RegistrationOption persistentRegistrationOptions;
134     private ResolveOption persistentResolveOptions;
135 
136     ~this()
137     {
138         clearAllRegistrations();
139     }
140 
141     /**
142      * Register a dependency by concrete class type.
143      *
144      * A dependency registered by concrete class type can only be resolved by concrete class type.
145      * No qualifiers can be used when resolving dependencies which are registered by concrete type.
146      *
147      * The default registration scope is "single instance" scope.
148      *
149      * Returns:
150      * A registration is returned which can be used to change the registration scope.
151      *
152      * Examples:
153      * Register and resolve a class by concrete type:
154      * ---
155      * class Cat : Animal { ... }
156      * container.register!Cat;
157      * ---
158      *
159      * See_Also: singleInstance, newInstance, existingInstance
160      */
161     public Registration register(ConcreteType)(RegistrationOption options = RegistrationOption.none)
162     {
163         return register!(ConcreteType, ConcreteType)(options);
164     }
165 
166     /**
167      * Register a dependency by super type.
168      *
169      * A dependency registered by super type can only be resolved by super type. A qualifier is typically
170      * used to resolve dependencies registered by super type.
171      *
172      * The default registration scope is "single instance" scope.
173      *
174      * Examples:
175      * Register and resolve by super type
176      * ---
177      * class Cat : Animal { ... }
178      * container.register!(Animal, Cat);
179      * ---
180      *
181      * See_Also: singleInstance, newInstance, existingInstance, RegistrationOption
182      */
183     public Registration register(SuperType, ConcreteType:
184         SuperType)(RegistrationOption options = RegistrationOption.none)
185             if (!is(ConcreteType == struct))
186     {
187         TypeInfo registeredType = typeid(SuperType);
188         TypeInfo_Class concreteType = typeid(ConcreteType);
189 
190         debug (poodinisVerbose)
191         {
192             writeln(format("DEBUG: Register type %s (as %s)",
193                     concreteType.toString(), registeredType.toString()));
194         }
195 
196         auto existingRegistration = getExistingRegistration(registeredType, concreteType);
197         if (existingRegistration)
198         {
199             return existingRegistration;
200         }
201 
202         auto instanceFactory = new ConstructorInjectingInstanceFactory!ConcreteType(this);
203         auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType,
204             instanceFactory, this);
205         newRegistration.initializeFactoryType().singleInstance();
206 
207         static if (!is(SuperType == ConcreteType))
208         {
209             if (!hasOption(options, persistentRegistrationOptions,
210                     RegistrationOption.doNotAddConcreteTypeRegistration))
211             {
212                 auto concreteTypeRegistration = register!ConcreteType;
213                 concreteTypeRegistration.linkTo(newRegistration);
214             }
215         }
216 
217         registrations[registeredType] ~= cast(shared(Registration)) newRegistration;
218         return newRegistration;
219     }
220 
221     private bool hasOption(OptionType)(OptionType options,
222         OptionType persistentOptions, OptionType option)
223     {
224         return ((options | persistentOptions) & option) != 0;
225     }
226 
227     private OptionType buildFlags(OptionType)(OptionType[] options)
228     {
229         OptionType flags;
230         foreach (option; options)
231         {
232             flags |= option;
233         }
234         return flags;
235     }
236 
237     private Registration getExistingRegistration(TypeInfo registrationType, TypeInfo qualifierType)
238     {
239         auto existingCandidates = registrationType in registrations;
240         if (existingCandidates)
241         {
242             return getRegistration(cast(Registration[])*existingCandidates, qualifierType);
243         }
244 
245         return null;
246     }
247 
248     private Registration getRegistration(Registration[] candidates, TypeInfo concreteType)
249     {
250         foreach (existingRegistration; candidates)
251         {
252             if (existingRegistration.instanceType == concreteType)
253             {
254                 return existingRegistration;
255             }
256         }
257 
258         return null;
259     }
260 
261     /**
262      * Resolve dependencies.
263      *
264      * Dependencies can only resolved using this method if they are registered by concrete type or the only
265      * concrete type registered by super type.
266      *
267      * Resolved dependencies are automatically autowired before being returned.
268      *
269      * Returns:
270      * An instance is returned which is created according to the registration scope with which they are registered.
271      *
272      * Throws:
273      * ResolveException when type is not registered.
274      *
275      * Examples:
276      * Resolve dependencies registered by super type and concrete type:
277      * ---
278      * class Cat : Animal { ... }
279      * class Dog : Animal { ... }
280      *
281      * container.register!(Animal, Cat);
282      * container.register!Dog;
283      *
284      * container.resolve!Animal;
285      * container.resolve!Dog;
286      * ---
287      * You cannot resolve a dependency when it is registered by multiple super types:
288      * ---
289      * class Cat : Animal { ... }
290      * class Dog : Animal { ... }
291      *
292      * container.register!(Animal, Cat);
293      * container.register!(Animal, Dog);
294      *
295      * container.resolve!Animal; // Error: multiple candidates for type "Animal"
296      * container.resolve!Dog; // Error: No type is registered by concrete type "Dog", only by super type "Animal"
297      * ---
298      * You need to use the resolve method which allows you to specify a qualifier.
299      */
300     public RegistrationType resolve(RegistrationType)(
301         ResolveOption resolveOptions = ResolveOption.none)
302             if (!is(RegistrationType == struct))
303     {
304         return resolve!(RegistrationType, RegistrationType)(resolveOptions);
305     }
306 
307     /**
308      * Resolve dependencies using a qualifier.
309      *
310      * Dependencies can only resolved using this method if they are registered by super type.
311      *
312      * Resolved dependencies are automatically autowired before being returned.
313      *
314      * Returns:
315      * An instance is returned which is created according to the registration scope with which they are registered.
316      *
317      * Throws:
318      * ResolveException when type is not registered or there are multiple candidates available for type.
319      *
320      * Examples:
321      * Resolve dependencies registered by super type:
322      * ---
323      * class Cat : Animal { ... }
324      * class Dog : Animal { ... }
325      *
326      * container.register!(Animal, Cat);
327      * container.register!(Animal, Dog);
328      *
329      * container.resolve!(Animal, Cat);
330      * container.resolve!(Animal, Dog);
331      * ---
332      */
333     public QualifierType resolve(RegistrationType, QualifierType:
334         RegistrationType)(ResolveOption resolveOptions = ResolveOption.none)
335             if (!is(QualifierType == struct))
336     {
337         TypeInfo resolveType = typeid(RegistrationType);
338         TypeInfo qualifierType = typeid(QualifierType);
339 
340         debug (poodinisVerbose)
341         {
342             writeln("DEBUG: Resolving type " ~ resolveType.toString() ~ " with qualifier " ~ qualifierType.toString());
343         }
344 
345         auto candidates = resolveType in registrations;
346         if (!candidates)
347         {
348             static if (is(typeof(typeid(QualifierType)) == TypeInfo_Class) && !__traits(isAbstractClass, QualifierType))
349             {
350                 if (hasOption(resolveOptions, persistentResolveOptions, ResolveOption
351                         .registerBeforeResolving))
352                 {
353                     register!(RegistrationType, QualifierType)();
354                     return resolve!(RegistrationType, QualifierType)(resolveOptions);
355                 }
356             }
357 
358             if (hasOption(resolveOptions, persistentResolveOptions,
359                     ResolveOption.noResolveException))
360             {
361                 return null;
362             }
363 
364             throw new ResolveException("Type not registered.", resolveType);
365         }
366 
367         Registration registration = getQualifiedRegistration(resolveType,
368             qualifierType, cast(Registration[])*candidates);
369 
370         try
371         {
372             QualifierType newInstance = resolveAutowiredInstance!QualifierType(registration);
373             callPostConstructors(newInstance);
374             return newInstance;
375         }
376         catch (ValueInjectionException e)
377         {
378             throw new ResolveException(e, resolveType);
379         }
380     }
381 
382     bool isRegistered(RegistrationType)()
383     {
384         TypeInfo typeInfo = typeid(RegistrationType);
385         auto candidates = typeInfo in registrations;
386         return candidates !is null;
387     }
388 
389     private QualifierType resolveAutowiredInstance(QualifierType)(Registration registration)
390     {
391         QualifierType instance;
392         if (!(cast(Registration[]) autowireStack).canFind(registration))
393         {
394             autowireStack ~= cast(shared(Registration)) registration;
395             instance = cast(QualifierType) registration.getInstance(
396                 new AutowireInstantiationContext());
397             autowireStack = autowireStack[0 .. $ - 1];
398         }
399         else
400         {
401             auto autowireContext = new AutowireInstantiationContext();
402             autowireContext.autowireInstance = false;
403             instance = cast(QualifierType) registration.getInstance(autowireContext);
404         }
405         return instance;
406     }
407 
408     /**
409      * Resolve all dependencies registered to a super type.
410      *
411      * Returns:
412      * An array of autowired instances is returned. The order is undetermined.
413      *
414      * Examples:
415      * ---
416      * class Cat : Animal { ... }
417      * class Dog : Animal { ... }
418      *
419      * container.register!(Animal, Cat);
420      * container.register!(Animal, Dog);
421      *
422      * Animal[] animals = container.resolveAll!Animal;
423      * ---
424      */
425     public RegistrationType[] resolveAll(RegistrationType)(
426         ResolveOption resolveOptions = ResolveOption.none)
427     {
428         RegistrationType[] instances;
429         TypeInfo resolveType = typeid(RegistrationType);
430 
431         auto qualifiedRegistrations = resolveType in registrations;
432         if (!qualifiedRegistrations)
433         {
434             if (hasOption(resolveOptions, persistentResolveOptions,
435                     ResolveOption.noResolveException))
436             {
437                 return [];
438             }
439 
440             throw new ResolveException("Type not registered.", resolveType);
441         }
442 
443         foreach (registration; cast(Registration[])*qualifiedRegistrations)
444         {
445             instances ~= resolveAutowiredInstance!RegistrationType(registration);
446         }
447 
448         return instances;
449     }
450 
451     private Registration getQualifiedRegistration(TypeInfo resolveType,
452         TypeInfo qualifierType, Registration[] candidates)
453     {
454         if (resolveType == qualifierType)
455         {
456             if (candidates.length > 1)
457             {
458                 string candidateList = candidates.toConcreteTypeListString();
459                 throw new ResolveException(
460                     "Multiple qualified candidates available: " ~ candidateList ~ ". Please use a qualifier.",
461                     resolveType);
462             }
463 
464             return candidates[0];
465         }
466 
467         return getRegistration(candidates, qualifierType);
468     }
469 
470     private void callPostConstructors(Type)(Type instance)
471     {
472         foreach (memberName; __traits(allMembers, Type))
473         {
474             mixin(createImportsString!Type);
475             enum QualifiedName = fullyQualifiedName!Type ~ `.` ~ memberName;
476             static if (__traits(compiles, __traits(getProtection, __traits(getMember, instance, memberName)))
477                 && __traits(getProtection, __traits(getMember, instance, memberName)) == "public"
478                 && isFunction!(mixin(QualifiedName))
479                 && hasUDA!(__traits(getMember, instance, memberName), PostConstruct))
480             {
481                 __traits(getMember, instance, memberName)();
482             }
483         }
484     }
485 
486     /**
487      * Clears all dependency registrations managed by this container.
488      */
489     public void clearAllRegistrations()
490     {
491         foreach (registrationsOfType; registrations)
492         {
493             callPreDestructorsOfRegistrations(registrationsOfType);
494         }
495         registrations.destroy();
496     }
497 
498     /**
499      * Removes a registered dependency by type.
500      *
501      * A dependency can be removed either by super type or concrete type, depending on how they are registered.
502      *
503      * Examples:
504      * ---
505      * container.removeRegistration!Animal;
506      * ---
507      */
508     public void removeRegistration(RegistrationType)()
509     {
510         auto registrationsOfType = *(typeid(RegistrationType) in registrations);
511         callPreDestructorsOfRegistrations(registrationsOfType);
512         registrations.remove(typeid(RegistrationType));
513     }
514 
515     private void callPreDestructorsOfRegistrations(shared(Registration[]) registrations)
516     {
517         foreach (registration; registrations)
518         {
519             Registration unsharedRegistration = cast(Registration) registration;
520             if (unsharedRegistration.preDestructor !is null)
521             {
522                 unsharedRegistration.preDestructor()();
523             }
524         }
525     }
526 
527     /**
528      * Apply persistent registration options which will be used everytime register() is called.
529      */
530     public void setPersistentRegistrationOptions(RegistrationOption options)
531     {
532         persistentRegistrationOptions = options;
533     }
534 
535     /**
536      * Unsets all applied persistent registration options
537      */
538     public void unsetPersistentRegistrationOptions()
539     {
540         persistentRegistrationOptions = RegistrationOption.none;
541     }
542 
543     /**
544      * Apply persistent resolve options which will be used everytime resolve() is called.
545      */
546     public void setPersistentResolveOptions(ResolveOption options)
547     {
548         persistentResolveOptions = options;
549     }
550 
551     /**
552      * Unsets all applied persistent resolve options
553      */
554     public void unsetPersistentResolveOptions()
555     {
556         persistentResolveOptions = ResolveOption.none;
557     }
558 
559 }