1 /**
2  * Contains functionality for autowiring dependencies using a dependency container.
3  *
4  * This module is used in a dependency container for autowiring dependencies when resolving them.
5  * You typically only need this module if you want inject dependencies into a class instance not
6  * managed by a dependency container.
7  *
8  * Part of the Poodinis Dependency Injection framework.
9  *
10  * Authors:
11  *  Mike Bierlee, m.bierlee@lostmoment.com
12  * Copyright: 2014-2023 Mike Bierlee
13  * License:
14  *  This software is licensed under the terms of the MIT license.
15  *  The full terms of the license can be found in the LICENSE file.
16  */
17 
18 module poodinis.autowire;
19 
20 import poodinis.container : DependencyContainer, PreDestroy, ResolveException, ResolveOption;
21 import poodinis.registration : Registration, InstantiationContext;
22 import poodinis.factory : InstanceFactory, InstanceFactoryParameters, CreatesSingleton;
23 import poodinis.valueinjection : ValueInjector, ValueInjectionException,
24     ValueNotAvailableException, Value, MandatoryValue;
25 import poodinis.altphobos : isFunction;
26 import poodinis.imports : createImportsString;
27 
28 import std.exception : enforce;
29 import std.string : format;
30 import std.traits : BaseClassesTuple, FieldNameTuple, fullyQualifiedName, hasUDA, isDynamicArray;
31 import std.range : ElementType;
32 
33 debug {
34     import std.stdio : writeln;
35 }
36 
37 private struct UseMemberType {
38 }
39 
40 /**
41  * UDA for annotating class members as candidates for autowiring.
42  *
43  * Optionally a template parameter can be supplied to specify the type of a qualified class. The qualified type
44  * of a concrete class is used to autowire members declared by supertype. If no qualifier is supplied, the type
45  * of the member is used as qualifier.
46  *
47  * Note: @Autowire is considered legacy, but not deprecated. Using @Inject is preferred.
48  *
49  * Examples:
50  * Annotate member of class to be autowired:
51  * ---
52  * class Car {
53  *    @Autowire
54  *    public Engine engine;
55  * }
56  * ---
57  *
58  * Annotate member of class with qualifier:
59  * ---
60  * class FuelEngine : Engine { ... }
61  * class ElectricEngine : Engine { ... }
62  *
63  * class HybridCar {
64  *    @Autowire!FuelEngine
65  *    public Engine fuelEngine;
66  *
67  *    @Autowire!ElectricEngine
68  *    public Engine electricEngine;
69  * }
70  * ---
71  * The members of an instance of "HybridCar" will now be autowired properly, because the autowire mechanism will
72  * autowire member "fuelEngine" as if it's of type "FuelEngine". This means that the members of instance "fuelEngine"
73  * will also be autowired because the autowire mechanism knows that member "fuelEngine" is an instance of "FuelEngine"
74  *
75  * See_Also: Inject
76  */
77 struct Autowire(QualifierType) {
78     QualifierType qualifier;
79 }
80 
81 /**
82  * UDA for marking autowired dependencies optional.
83  * Optional dependencies will not lead to a resolveException when there is no type registered for them.
84  * The member will remain null.
85  */
86 struct OptionalDependency {
87 }
88 
89 /**
90  * UDA for annotating class members to be autowired with a new instance regardless of their registration scope.
91  *
92  * Examples:
93  *---
94  * class Car {
95  *     @Autowire
96  *     @AssignNewInstance
97  *     public Antenna antenna;
98  * }
99  *---
100  * antenna will always be assigned a new instance of class Antenna.
101  */
102 struct AssignNewInstance {
103 }
104 
105 private void printDebugAutowiredInstance(TypeInfo instanceType, void* instanceAddress) {
106     debug {
107         writeln(format("DEBUG: Autowiring members of [%s@%s]", instanceType, instanceAddress));
108     }
109 }
110 
111 /**
112  * Autowires members of a given instance using dependencies registered in the given container.
113  *
114  * All members of the given instance, which are annotated using the "Autowire" UDA, are autowired.
115  * Members can have any visibility (public, private, etc). All members are resolved using the given
116  * container. Qualifiers are used to determine the type of class to resolve for any member of instance.
117  *
118  * See_Also: Autowire
119  */
120 public void autowire(Type)(shared(DependencyContainer) container, Type instance) {
121     debug (poodinisVerbose) {
122         printDebugAutowiredInstance(typeid(Type), &instance);
123     }
124 
125     // Recurse into base class if there are more between Type and Object in the hierarchy
126     static if (BaseClassesTuple!Type.length > 1) {
127         autowire!(BaseClassesTuple!Type[0])(container, instance);
128     }
129 
130     foreach (index, name; FieldNameTuple!Type) {
131         autowireMember!(name, index, Type)(container, instance);
132     }
133 }
134 
135 private void printDebugAutowiringCandidate(TypeInfo candidateInstanceType,
136     void* candidateInstanceAddress, TypeInfo instanceType, void* instanceAddress, string member) {
137     debug {
138         writeln(format("DEBUG: Autowired instance [%s@%s] to [%s@%s].%s", candidateInstanceType,
139                 candidateInstanceAddress, instanceType, instanceAddress, member));
140     }
141 }
142 
143 private void printDebugAutowiringArray(TypeInfo superTypeInfo,
144     TypeInfo instanceType, void* instanceAddress, string member) {
145     debug {
146         writeln(format("DEBUG: Autowired all registered instances of super type %s to [%s@%s].%s",
147                 superTypeInfo, instanceType, instanceAddress, member));
148     }
149 }
150 
151 private void autowireMember(string member, size_t memberIndex, Type)(
152     shared(DependencyContainer) container, Type instance) {
153     foreach (attribute; __traits(getAttributes, Type.tupleof[memberIndex])) {
154         static if (is(attribute == Autowire!T, T)) {
155             injectInstance!(member, memberIndex, typeof(attribute.qualifier))(container, instance);
156         } else static if (__traits(isSame, attribute, Autowire)) {
157             injectInstance!(member, memberIndex, UseMemberType)(container, instance);
158         } else static if (is(typeof(attribute) == Value)) {
159             enum key = attribute.key;
160             injectValue!(member, memberIndex, key, false)(container, instance);
161         } else static if (is(typeof(attribute) == MandatoryValue)) {
162             enum key = attribute.key;
163             injectValue!(member, memberIndex, key, true)(container, instance);
164         }
165     }
166 }
167 
168 private void injectInstance(string member, size_t memberIndex, QualifierType, Type)(
169     shared(DependencyContainer) container, Type instance) {
170     if (instance.tupleof[memberIndex] is null) {
171         alias MemberType = typeof(Type.tupleof[memberIndex]);
172         enum isOptional = hasUDA!(Type.tupleof[memberIndex], OptionalDependency);
173 
174         static if (isDynamicArray!MemberType) {
175             injectMultipleInstances!(member, memberIndex, isOptional, MemberType)(container,
176                 instance);
177         } else {
178             injectSingleInstance!(member, memberIndex, isOptional, MemberType, QualifierType)(container,
179                 instance);
180         }
181     }
182 }
183 
184 private void injectMultipleInstances(string member, size_t memberIndex,
185     bool isOptional, MemberType, Type)(shared(DependencyContainer) container, Type instance) {
186     alias MemberElementType = ElementType!MemberType;
187     static if (isOptional) {
188         auto instances = container.resolveAll!MemberElementType(ResolveOption.noResolveException);
189     } else {
190         auto instances = container.resolveAll!MemberElementType;
191     }
192 
193     instance.tupleof[memberIndex] = instances;
194     debug (poodinisVerbose) {
195         printDebugAutowiringArray(typeid(MemberElementType), typeid(Type), &instance, member);
196     }
197 }
198 
199 private void injectSingleInstance(string member, size_t memberIndex,
200     bool isOptional, MemberType, QualifierType, Type)(
201     shared(DependencyContainer) container, Type instance) {
202     debug (poodinisVerbose) {
203         TypeInfo qualifiedInstanceType = typeid(MemberType);
204     }
205 
206     enum assignNewInstance = hasUDA!(Type.tupleof[memberIndex], AssignNewInstance);
207 
208     MemberType qualifiedInstance;
209     static if (!is(QualifierType == UseMemberType)) {
210         qualifiedInstance = createOrResolveInstance!(MemberType, QualifierType,
211             assignNewInstance, isOptional)(container);
212         debug (poodinisVerbose) {
213             qualifiedInstanceType = typeid(QualifierType);
214         }
215     } else {
216         qualifiedInstance = createOrResolveInstance!(MemberType, MemberType,
217             assignNewInstance, isOptional)(container);
218     }
219 
220     instance.tupleof[memberIndex] = qualifiedInstance;
221 
222     debug (poodinisVerbose) {
223         printDebugAutowiringCandidate(qualifiedInstanceType,
224             &qualifiedInstance, typeid(Type), &instance, member);
225     }
226 }
227 
228 private QualifierType createOrResolveInstance(MemberType, QualifierType,
229     bool createNew, bool isOptional)(shared(DependencyContainer) container) {
230     static if (createNew) {
231         auto instanceFactory = new InstanceFactory();
232         instanceFactory.factoryParameters = InstanceFactoryParameters(typeid(MemberType),
233             CreatesSingleton.no);
234         return cast(MemberType) instanceFactory.getInstance();
235     } else {
236         static if (isOptional) {
237             return container.resolve!(MemberType, QualifierType)(ResolveOption.noResolveException);
238         } else {
239             return container.resolve!(MemberType, QualifierType);
240         }
241     }
242 }
243 
244 private void injectValue(string member, size_t memberIndex, string key, bool mandatory, Type)(
245     shared(DependencyContainer) container, Type instance) {
246     alias MemberType = typeof(Type.tupleof[memberIndex]);
247     try {
248         auto injector = container.resolve!(ValueInjector!MemberType);
249         instance.tupleof[memberIndex] = injector.get(key);
250         debug (poodinisVerbose) {
251             printDebugValueInjection(typeid(Type), &instance, member, typeid(MemberType), key);
252         }
253     } catch (ResolveException e) {
254         throw new ValueInjectionException(format(
255                 "Could not inject value of type %s into %s.%s: value injector is missing for this type.",
256                 typeid(MemberType), typeid(Type), member));
257     } catch (ValueNotAvailableException e) {
258         static if (mandatory) {
259             throw new ValueInjectionException(format("Could not inject value of type %s into %s.%s",
260                     typeid(MemberType), typeid(Type), member), e);
261         }
262     }
263 }
264 
265 private void printDebugValueInjection(TypeInfo instanceType,
266     void* instanceAddress, string member, TypeInfo valueType, string key) {
267     debug {
268         writeln(format("DEBUG: Injected value with key '%s' of type %s into [%s@%s].%s",
269                 key, valueType, instanceType, instanceAddress, member));
270     }
271 }
272 
273 /**
274  * Autowire the given instance using the globally available dependency container.
275  *
276  * See_Also: DependencyContainer
277  * Deprecated: Using the global container is undesired. See DependencyContainer.getInstance().
278  */
279 public deprecated void globalAutowire(Type)(Type instance) {
280     DependencyContainer.getInstance().autowire(instance);
281 }
282 
283 class AutowiredRegistration(RegistrationType : Object) : Registration {
284     private shared(DependencyContainer) container;
285 
286     public this(TypeInfo registeredType, InstanceFactory instanceFactory,
287         shared(DependencyContainer) originatingContainer) {
288         super(registeredType, typeid(RegistrationType), instanceFactory, originatingContainer);
289     }
290 
291     public override Object getInstance(
292         InstantiationContext context = new AutowireInstantiationContext()) {
293         enforce(!(originatingContainer is null),
294             "The registration's originating container is null. There is no way to resolve autowire dependencies.");
295 
296         RegistrationType instance = cast(RegistrationType) super.getInstance(context);
297 
298         AutowireInstantiationContext autowireContext = cast(AutowireInstantiationContext) context;
299         enforce(!(autowireContext is null), "Given instantiation context type could not be cast to an AutowireInstantiationContext. If you relied on using the default assigned context: make sure you're calling getInstance() on an instance of type AutowiredRegistration!");
300         if (autowireContext.autowireInstance) {
301             originatingContainer.autowire(instance);
302         }
303 
304         this.preDestructor = getPreDestructor(instance);
305 
306         return instance;
307     }
308 
309     private void delegate() getPreDestructor(RegistrationType instance) {
310         void delegate() preDestructor = null;
311         static foreach (memberName; __traits(allMembers, RegistrationType)) {
312             static foreach (overload; __traits(getOverloads, RegistrationType, memberName)) {
313                 static if (__traits(compiles, __traits(getProtection, overload))
314                     && __traits(getProtection, overload) == "public"
315                     && isFunction!overload
316                     && hasUDA!(overload, PreDestroy)) {
317                     preDestructor = &__traits(getMember, instance, memberName);
318                 }
319             }
320         }
321 
322         return preDestructor;
323     }
324 }
325 
326 class AutowireInstantiationContext : InstantiationContext {
327     public bool autowireInstance = true;
328 }