1 module glued.application.di.initializers; 2 3 import std.algorithm; 4 import std.meta; 5 import std.traits; 6 7 import glued.annotations; 8 import glued.logging; 9 10 import glued.application.di.annotations; 11 import glued.application.di.resolveCall; 12 13 import dejector; 14 15 private template queryForField(T, string name) 16 if (is(T == class)) 17 { 18 alias annotation = getAnnotation!(__traits(getMember, T, name), Autowire); 19 static if (is(annotation.Query == DefaultQuery)) 20 { 21 alias queryForField = typeof(__traits(getMember, T, name)); 22 } 23 else 24 { 25 alias queryForField = annotation.Query; 26 } 27 } 28 29 private template queryForProperty(T, string name, size_t overloadIdx) 30 if (is(T == class)) 31 { 32 alias annotation = getAnnotation!(__traits(getOverloads, T, name)[overloadIdx], Autowire); 33 static if (is(annotation.Query == DefaultQuery)) 34 { 35 alias queryForProperty = Parameters!(__traits(getOverloads, T, name)[overloadIdx])[0]; 36 } 37 else 38 { 39 alias queryForProperty = annotation.Query; 40 } 41 } 42 43 /** 44 * Injects dependencies to existing instance. 45 * 46 * Finds every public field or property setter (single-param method with @property 47 * attribute that returns void) that is annotated with Autowire. 48 */ 49 class InstanceInitializer(T, bool checkNulls): Initializer 50 if (is(T == class)) 51 { 52 mixin CreateLogger; 53 private Dejector injector; 54 private Logger log; 55 56 this(Dejector injector) 57 { 58 this.injector = injector; 59 log = Logger(injector.get!(LogSink)); 60 } 61 62 override void initialize(Object o) 63 { 64 log.debug_.emit("Instance initialization for ", &o, " of type ", fullyQualifiedName!T); 65 T t = (cast(T)o); 66 log.debug_.emit("Field injection for ", &o, " of type ", fullyQualifiedName!T); 67 fieldInjection(t); 68 propertyInjection(t); 69 //postConstructInjection(t); 70 log.debug_.emit("Instance initialization for ", &o, " of type ", fullyQualifiedName!T, " finished"); 71 } 72 73 private void fieldInjection(T t) 74 { 75 enum isPublic(string name) = __traits(getProtection, __traits(getMember, T, name)) == "public"; 76 enum isAutowired(string name) = hasOneAnnotation!(__traits(getMember, T, name), Autowire); 77 auto hasNullValue(string name)() 78 { 79 return __traits(getMember, t, name) is null; 80 } 81 82 static foreach (i, name; FieldNameTuple!T) 83 { 84 static if (isPublic!name && isAutowired!name) 85 { 86 mixin("import "~moduleName!(queryForField!(T, name))~";"); 87 static if (checkNulls) 88 { 89 if (hasNullValue!name()) 90 { 91 log.debug_.emit("Injecting field ", name, " with query ", fullyQualifiedName!(queryForField!(T, name)), " after null check"); 92 __traits(getMember, t, name) = this.injector.get!(queryForField!(T, name)); 93 log.debug_.emit("Injected field ", name, " after null check"); 94 } 95 else 96 { 97 log.debug_.emit("Field ", name, " didn't pass null check (value=", __traits(getMember, t, name), ")"); 98 } 99 } 100 else 101 { 102 log.debug_.emit("Injecting field ", name, " with query ", fullyQualifiedName!(queryForField!(T, name)), " with no null check"); 103 __traits(getMember, t, name) = this.injector.get!(queryForField!(T, name)); 104 log.debug_.emit("Injected field ", name, " with no null check"); 105 } 106 } 107 } 108 } 109 110 private void propertyInjection(T t){ 111 enum isPublic(string name, size_t overloadIdx) = __traits(getProtection, __traits(getOverloads, T, name)[overloadIdx]) == "public"; 112 enum isAutowired(string name, size_t overloadIdx) = hasOneAnnotation!(__traits(getOverloads, T, name)[overloadIdx], Autowire); 113 enum isMarkedAsProperty(string name, size_t overloadIdx) = hasFunctionAttributes!(__traits(getOverloads, T, name)[overloadIdx], "@property"); 114 enum isVoidMethod(string name, size_t overloadIdx) = is(ReturnType!(__traits(getOverloads, T, name)[overloadIdx]) == void); 115 enum hasOneParam(string name, size_t overloadIdx) = Parameters!(__traits(getOverloads, T, name)[overloadIdx]).length == 1; 116 117 static foreach (i, name; __traits(allMembers, T)) 118 { 119 static foreach (j, _ignored; __traits(getOverloads, T, name)) 120 { 121 static if (isPublic!(name, j) && 122 isAutowired!(name, j) && 123 isMarkedAsProperty!(name, j) && 124 isVoidMethod!(name, j) && 125 hasOneParam!(name, j)) 126 { 127 log.debug_.emit("Injecting property ", name, " with query ", fullyQualifiedName!(queryForProperty!(T, name, j))); 128 __traits(getOverloads, t, name)[j](injector.get!(queryForProperty!(T, name, j))); 129 log.debug_.emit("Injecting property ", name); 130 } 131 } 132 } 133 } 134 } 135 136 /** 137 * Initializes freshly created instance (for which constructor wasn't called yet). 138 * Besides standard injection methods performs constructor injection. 139 * 140 * If aggregate has many constructors, up to one of them can be annotated 141 * with Constructor - that one will be called. 142 * If there is only one constructor it will be called by default. To turn this 143 * behaviour off annotate that constructor with DontInject. 144 */ 145 class ComponentSeedInitializer(T): InstanceInitializer!(T, false) 146 if (is(T == class)) 147 { 148 mixin CreateLogger; 149 private Dejector injector; 150 private Logger log; 151 152 this(Dejector injector) 153 { 154 super(injector); 155 this.injector = injector; 156 log = Logger(injector.get!(LogSink)); 157 } 158 159 override void initialize(Object o) 160 { 161 log.debug_.emit("Seed initialization for ", &o, " of type ", fullyQualifiedName!T); 162 T t = (cast(T)o); 163 log.debug_.emit("Constructor injection for ", &o, " of type ", fullyQualifiedName!T); 164 constructorInjection(t); 165 super.initialize(o); 166 log.debug_.emit("Seed initialization for ", &o, " of type ", fullyQualifiedName!T, " finished"); 167 } 168 169 private void constructorInjection(T t) 170 { 171 enum isPublic(alias ctor) = __traits(getProtection, ctor) == "public"; 172 enum isAnnotatedForInjection(alias ctor) = hasOneAnnotation!(ctor, Constructor); 173 enum isAnnotatedForIgnoring(alias ctor) = hasOneAnnotation!(ctor, DontInject); 174 175 static if (hasMember!(T, "__ctor")) 176 { 177 static if (__traits(getOverloads, T, "__ctor").length == 1 && 178 isPublic!(__traits(getOverloads, T, "__ctor")[0])) 179 { 180 static if(isAnnotatedForIgnoring!(__traits(getOverloads, t, "__ctor")[0])) 181 { 182 log.debug_.emit("Sole constructor for seed ", &t, " of type ", fullyQualifiedName!T, " is ignored"); 183 } 184 else 185 { 186 log.debug_.emit("Calling default constructor for seed ", &t, " of type ", fullyQualifiedName!T); 187 resolveCall(this.injector, &__traits(getOverloads, t, "__ctor")[0]); 188 log.debug_.emit("Called default constructor for instance", &t); 189 } 190 } 191 else 192 { 193 bool ctorCalled = false; 194 static foreach (i, ctor; __traits(getOverloads, T, "__ctor")) 195 { 196 static if (isPublic!ctor && isAnnotatedForInjection!ctor) 197 { 198 assert(!ctorCalled); 199 log.debug_.emit("Calling constructor for seed ", &t, " of type ", fullyQualifiedName!T); 200 resolveCall!(__traits(getOverloads, T, "__ctor")[i])(this.injector, &__traits(getOverloads, t, "__ctor")[i]); 201 log.debug_.emit("Called constructor for instance", &t); 202 ctorCalled = true; 203 } 204 } 205 if (!ctorCalled) 206 log.debug_.emit("Couldn't find any constructor for seed ", &t, " of type ", fullyQualifiedName!T); 207 } 208 } 209 else 210 { 211 log.debug_.emit("Couldn't find any constructor for seed ", &t, " of type ", fullyQualifiedName!T); 212 } 213 } 214 }