1 /** 2 Copyright 2018 Mark Fisher 3 4 Permission is hereby granted, free of charge, to any person obtaining a copy of 5 this software and associated documentation files (the "Software"), to deal in 6 the Software without restriction, including without limitation the rights to 7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 of the Software, and to permit persons to whom the Software is furnished to do 9 so, subject to the following conditions: 10 11 The above copyright notice and this permission notice shall be included in all 12 copies or substantial portions of the Software. 13 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 SOFTWARE. 21 **/ 22 module dxx.util.injector; 23 24 //private import std.algorithm : each; 25 //private import std.variant; 26 private import std.experimental.logger; 27 private import std.stdio; 28 private import std.process : environment; 29 private import std.typecons; 30 31 private import aermicioi.aedi; 32 private import aermicioi.aedi_property_reader; 33 34 private import dxx.sys.constants; 35 private import dxx.util.ini; 36 //private import dxx.util.storage; 37 private import dxx.util.config; 38 39 40 alias component = aermicioi.aedi.component; 41 42 //static Variant[string] readInjectorProperties(File* f) { 43 // Variant[string] res; 44 // return res; 45 //} 46 // 47 //static void registerInjectorProperties(Variant[string] properties) { 48 //InjectionContainer._DEFAULT_CONTAINER.each!(c=>{ 49 // with(c.configure) { 50 // properties.each!((k,v)=>{ 51 // if(v.type == typeid(string)) { 52 // } 53 // }); 54 // } 55 //}); 56 //properties.keys.each!((k)=>{ 57 // if(v.type == typeid(string)) { 58 // InjectionContainer.register!string(k); 59 // } 60 // }); 61 //} 62 63 static auto resolveInjector(alias T,Arg...)(Arg arg,InjectionContainer i=InjectionContainer.getInstance) { 64 return i.resolve!T(arg); 65 } 66 67 static T getInjectorProperty(T)(string k,InjectionContainer i=InjectionContainer.getInstance) { 68 return i.resolve!T(k); 69 } 70 71 static void setInjectorProperty(T)(string k,T t,InjectionContainer i=InjectionContainer.getInstance) { 72 i.register!T(t,k); 73 } 74 75 static auto newInjector(alias T,V...)(AggregateContainer c = null) { 76 if(InjectionContainer.INSTANCE is null) { 77 new ContextInjector!(T,V)(c); 78 } 79 return InjectionContainer.getInstance; 80 } 81 82 static void terminateInjector() { 83 synchronized(InjectionContainer.classinfo) { 84 if(InjectionContainer.INSTANCE) { 85 InjectionContainer.INSTANCE.terminate; 86 } 87 } 88 } 89 90 //interface InjectionComponent { 91 // void registerComponent(InjectionContainer injector); 92 // 93 //} 94 95 abstract class InjectionContainer { 96 97 private static __gshared InjectionContainer INSTANCE; 98 static bool instantiated = false; 99 100 static auto ref getInstance() { 101 //assert(INSTANCE !is null); 102 return INSTANCE; 103 } 104 105 @property 106 AggregateContainer _container; 107 108 void services(T)(T parent) { 109 auto s = singleton; 110 auto p = prototype; 111 112 configureSingleton(s); 113 scanPrototype(p); 114 115 parent.set(s,"singleton"); 116 parent.set(p,"prototype"); 117 118 } 119 abstract void scanPrototype(PrototypeContainer); 120 abstract void configureSingleton(SingletonContainer); 121 //abstract void configureGlobals(AggregateContainer _container); 122 123 this(AggregateContainer _c) { 124 synchronized(InjectionContainer.classinfo) { 125 if(!instantiated) { 126 if(INSTANCE is null) { 127 services(_c); 128 _container = _c; 129 INSTANCE = this; 130 } 131 instantiated = true; 132 } 133 } 134 } 135 136 auto resolve(T,Arg ...)(Arg arg) { 137 return _container.locate!T(arg); 138 } 139 140 void register(T...)(const(string) arg) { 141 with(_container.configure("prototype")) { 142 register!T(arg); 143 } 144 } 145 void register(T...)() { 146 with(_container.configure("prototype")) { 147 register!T(); 148 } 149 } 150 void register(T)(ref T t,const(string) arg) { 151 with(_container.configure("singleton")) { 152 register!T(t,arg); 153 } 154 } 155 void setParam(T)(string k,T v) { 156 with(container.configure("parameters")) { 157 register!T(v,k); 158 } 159 } 160 T getParam(T)(string k) { 161 return _container.locate!T(k); 162 } 163 void terminate() { 164 debug(Injector) { 165 sharedLog.info("terminating"); 166 } 167 _container.terminate(); 168 } 169 auto instantiate() { 170 debug(Injector) { 171 sharedLog.info("instantiating"); 172 } 173 return _container.instantiate; 174 } 175 abstract void load(T : DocumentContainer!X, X...)(T container); 176 } 177 178 179 final class ContextInjector(C...) : InjectionContainer { 180 this(AggregateContainer c = null) { 181 if(c is null) c = aggregate(config, "parameters"); 182 super(c); 183 } 184 override void scanPrototype(PrototypeContainer p) { 185 debug(Injector) { 186 sharedLog.info("scanPrototype"); 187 } 188 static foreach(c;C) { 189 debug(Injector) { 190 import std.conv; 191 sharedLog.info("Scanning prototype: " ~ typeid(c).to!string); 192 } 193 static if(isTuple!c) { 194 } else { 195 debug(Injector) { 196 pragma(msg,"scanning prototype: "); 197 pragma(msg,c); 198 } 199 p.scan!c; 200 } 201 } 202 } 203 override void configureSingleton(SingletonContainer) { 204 } 205 206 auto config() { 207 debug(Injector) { 208 sharedLog.info("Injector config"); 209 } 210 auto cont = container( 211 argument, 212 env, 213 json("./dxx.json"), 214 json(RTConstants.constants.appDir ~ "/dxx.json") 215 //json("/etc/aedi-example/config.json"), 216 //configFiles 217 ); 218 foreach (c; cont) { 219 load(c); 220 } 221 return cont; 222 } 223 void load(T : DocumentContainer!X, X...)(T container) { 224 with (container.configure) { 225 static foreach(c;C) { 226 static if(isTuple!c) { 227 debug(Injector) { 228 pragma(msg,"scanning parameters: "); 229 pragma(msg,c); 230 } 231 register!string; 232 //register!uint; 233 //register!int; 234 register!long; 235 236 static foreach (fieldName ; c.fieldNames) { 237 { 238 mixin("alias f = c." ~ fieldName~";"); 239 alias fieldType = typeof(f); 240 debug(Injector) { 241 import std.conv; 242 sharedLog.info("field: " ~ typeid(fieldType).to!string ~ " " ~ fieldName); 243 } 244 register!fieldType(fieldName); 245 } 246 } 247 } 248 // Scan properties from the .ini file. 249 // Make them all strings. 250 iterateValuesF!(DXXConfig.keys)( (string fqn,string k,string v) { 251 //properties[k]=v; 252 register!string(k); 253 } ); 254 } 255 } 256 } 257 } 258 259 unittest { 260 class MyClass { 261 } 262 263 @component 264 class MyModule { 265 @component 266 public MyClass getMyClass() { 267 return new MyClass; 268 } 269 } 270 debug { 271 sharedLog.info("Starting component unittest."); 272 } 273 auto injector = newInjector!MyModule; 274 assert(injector !is null); 275 auto my = injector.resolve!MyClass; 276 assert(my !is null); 277 } 278 279 unittest { 280 alias param = Tuple!( 281 string,"name", 282 long,"age" 283 ); 284 debug { 285 sharedLog.info("Starting injector parameters unittest."); 286 } 287 auto injector = newInjector!param; 288 assert(injector !is null); 289 auto name = injector.resolve!string("name"); 290 auto age = injector.resolve!long("age"); 291 }