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 }