1 /** 2 Contains factories and primitives used for building proxy objects. 3 4 License: 5 Boost Software License - Version 1.0 - August 17th, 2003 6 7 Permission is hereby granted, free of charge, to any person or organization 8 obtaining a copy of the software and accompanying documentation covered by 9 this license (the "Software") to use, reproduce, display, distribute, 10 execute, and transmit the Software, and to prepare derivative works of the 11 Software, and to permit third-parties to whom the Software is furnished to 12 do so, all subject to the following: 13 14 The copyright notices in the Software and this entire statement, including 15 the above license grant, this restriction and the following disclaimer, 16 must be included in all copies of the Software, in whole or in part, and 17 all derivative works of the Software, unless such copies or derivative 18 works are solely in the form of machine-executable object code generated by 19 a source language processor. 20 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 24 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 25 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 26 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 DEALINGS IN THE SOFTWARE. 28 29 Authors: 30 aermicioi 31 **/ 32 module aermicioi.aedi.factory.proxy_factory; 33 34 import aermicioi.aedi.factory.factory; 35 import aermicioi.aedi.factory.generic_factory; 36 import aermicioi.aedi.storage.decorator; 37 import aermicioi.aedi.storage.locator; 38 import aermicioi.aedi.exception.di_exception; 39 import aermicioi.aedi.storage.locator_aware; 40 41 import aermicioi.aedi.util.traits; 42 43 import std.algorithm; 44 import std.meta; 45 import std.range; 46 import std.traits; 47 import std.typecons : AutoImplement; 48 49 /** 50 Creates a proxy to an object or interface of type T, 51 that is located in source locator by some identity. 52 **/ 53 @safe class ProxyFactory(T) : Factory!T 54 if ( 55 ( 56 is(T == class) && 57 !isFinalClass!T && 58 !isAbstractClass!T 59 ) || 60 (is(T == interface)) 61 ) { 62 63 import aermicioi.aedi.storage.allocator_aware : AllocatorAwareMixin, make, dispose, theAllocator; 64 mixin AllocatorAwareMixin!(typeof(this)); 65 66 private { 67 string identity_; 68 Locator!() source_; 69 } 70 71 public { 72 /** 73 Constructor for ProxyFactory!(T) 74 75 Params: 76 identity = identity of original object stored in a locator 77 original = locator that contains original object that a proxy should proxy. 78 **/ 79 this(string identity, Locator!() original) { 80 this.identity = identity; 81 this.source = original; 82 this.allocator = theAllocator; 83 } 84 85 /** 86 Instantiates component of type T. 87 88 Returns: 89 T instantiated component of type T. 90 **/ 91 Proxy!T factory() @trusted { 92 auto proxy = this.allocator.make!(Proxy!T); 93 proxy.__id__ = this.identity; 94 proxy.__locator__ = this.source; 95 96 return proxy; 97 } 98 99 /** 100 Destructs a component of type T. 101 102 Params: 103 component = component that is to ve destroyed. 104 105 Returns: 106 107 **/ 108 void destruct(ref T component) @trusted { 109 Proxy!T proxy = cast(Proxy!T) component; 110 111 if (proxy !is null) { 112 113 this.allocator.dispose(proxy); 114 return; 115 } 116 117 import aermicioi.aedi.exception.invalid_cast_exception : InvalidCastException; 118 throw new InvalidCastException( 119 "Cannot destruct component ${identity} because it is not a proxied object. Expected ${expected} while got ${actual}", 120 null, 121 typeid(Proxy!T), 122 typeid(T) 123 ); 124 } 125 126 @property { 127 /** 128 Set the identity of proxied object 129 130 Params: 131 identity = the identity of proxied object 132 133 Returns: 134 this 135 **/ 136 ProxyFactory!T identity(string identity) @safe nothrow { 137 this.identity_ = identity; 138 139 return this; 140 } 141 142 /** 143 Get the identity of proxied object. 144 145 Returns: 146 string identity 147 **/ 148 string identity() @safe nothrow { 149 return this.identity_; 150 } 151 152 /** 153 Set the source of proxied object. 154 155 Params: 156 source = source locator where proxied object resides. 157 158 Returns: 159 this 160 **/ 161 ProxyFactory!T source(Locator!() source) @safe nothrow { 162 this.source_ = source; 163 164 return this; 165 } 166 167 /** 168 Get the source of proxied object. 169 170 Returns: 171 Locator!() source 172 **/ 173 Locator!() source() @safe nothrow { 174 return this.source_; 175 } 176 177 /** 178 Set locator 179 180 Params: 181 locator = locator or source of original component proxied by proxy 182 Returns: 183 typeof(this) 184 **/ 185 LocatorAware!(Object, string) locator(Locator!(Object, string) locator) @safe nothrow { 186 this.source = locator; 187 188 return this; 189 } 190 191 /** 192 Get locator 193 194 Returns: 195 Locator!() 196 **/ 197 Locator!() locator() @safe nothrow { 198 return this.source; 199 } 200 201 /** 202 Get the type info of T that is created. 203 204 Returns: 205 TypeInfo object of created component. 206 **/ 207 TypeInfo type() @safe nothrow const { 208 return typeid(Proxy!T); 209 } 210 211 } 212 } 213 } 214 215 /** 216 Auto implements a proxy for object or interface of type T which 217 is not a final or abstract class. 218 219 Warning: 220 Current implmentation uses AutoImplement from phobos which 221 has some unfixed bugs. 222 **/ 223 template ProxyImpl(T) 224 if ( 225 ( 226 is(T == class) && 227 !isFinalClass!T && 228 !isAbstractClass!T 229 ) || 230 is(T == interface) 231 ) { 232 233 class ProxyImpl : T { 234 private { 235 236 Locator!() __locator_; 237 string __id_; 238 } 239 240 public { 241 242 this() { 243 super(); 244 } 245 } 246 247 @property public { 248 ProxyImpl!T __locator__(Locator!() locator) @safe nothrow @nogc { 249 this.__locator_ = locator; 250 251 return this; 252 } 253 254 Locator!() __locator__() @safe nothrow @nogc { 255 return this.__locator_; 256 } 257 258 ProxyImpl!T __id__(string id) @safe nothrow @nogc { 259 this.__id_ = id; 260 261 return this; 262 } 263 264 string __id__() @safe nothrow @nogc { 265 return this.__id_; 266 } 267 } 268 } 269 } 270 271 template how(T) { 272 static string how(C, alias fun)() { 273 static if (identifier!fun == "__ctor") { 274 pragma(msg, "here"); 275 return q{super(args)}; 276 } static if (identifier!fun == "__dtor") { 277 return q{}; 278 } else { 279 280 string stmt = q{ 281 import aermicioi.aedi.storage.locator; 282 import aermicioi.aedi.exception.di_exception; 283 import aermicioi.aedi.factory.proxy_factory; 284 } ~ fullyQualifiedName!T ~ " original; 285 try { 286 original = this.__locator__.locate!(" ~ fullyQualifiedName!T ~ ")(this.__id__); 287 } catch (Exception e) { 288 assert(false, \"Failed to fetch \" ~ __id__ ~ \" in proxy object.\"); 289 } 290 "; 291 292 static if (!is(ReturnType!fun == void)) { 293 stmt ~= " 294 return original." ~ __traits(identifier, fun) ~ "(args); 295 "; 296 } else { 297 stmt ~= " 298 original." ~ __traits(identifier, fun) ~ "(args); 299 "; 300 } 301 302 return stmt; 303 } 304 305 } 306 } 307 308 alias Proxy(T) = AutoImplement!(ProxyImpl!T, how!T, templateAnd!( 309 templateNot!isFinalFunction 310 )); 311 312 /** 313 A ProxyObjectFactory instantiates a proxy to some type of object located in source locator. 314 **/ 315 @safe interface ProxyObjectFactory : ObjectFactory { 316 317 @property { 318 319 /** 320 Get the identity of original object that proxy factory will intantiate proxy object. 321 322 Returns: 323 string the original object identity 324 **/ 325 string identity() @safe nothrow; 326 327 /** 328 Get the original locator that is used by proxy to fetch the proxied object. 329 330 Returns: 331 Locator!() original locator containing the proxied object. 332 **/ 333 Locator!() source() @safe nothrow; 334 } 335 } 336 337 /** 338 Proxy factory decorator, that conforms to requirements of a container, exposing as well the ability 339 to set proxied object's identity and locator. 340 **/ 341 @safe class ProxyObjectWrappingFactory(T) : ProxyObjectFactory, MutableDecorator!(ProxyFactory!T) 342 if (is(T : Object) && !isFinalClass!T) { 343 344 import aermicioi.aedi.storage.allocator_aware : AllocatorAwareMixin; 345 mixin AllocatorAwareMixin!(typeof(this)); 346 347 private { 348 ProxyFactory!T decorated_; 349 350 } 351 352 public { 353 354 /** 355 Constructor for ProxyObjectWrappingFactory!T 356 357 Params: 358 factory = proxy factory that is decorated 359 **/ 360 this(ProxyFactory!T factory) { 361 this.decorated = factory; 362 } 363 364 @property { 365 /** 366 Get the identity of original object that proxy factory will intantiate proxy object. 367 368 Returns: 369 string the original object identity 370 **/ 371 ProxyObjectWrappingFactory!T identity(string identity) @safe nothrow { 372 this.decorated.identity = identity; 373 374 return this; 375 } 376 377 /** 378 Get identity 379 380 Returns: 381 string 382 **/ 383 string identity() @safe nothrow { 384 return this.decorated.identity; 385 } 386 387 /** 388 Get the original locator that is used by proxy to fetch the proxied object. 389 390 Returns: 391 Locator!() original locator containing the proxied object. 392 **/ 393 ProxyObjectWrappingFactory!T source(Locator!() source) @safe nothrow { 394 this.decorated.source = source; 395 396 return this; 397 } 398 399 /** 400 Get source 401 402 Returns: 403 Locator!() 404 **/ 405 Locator!() source() @safe nothrow { 406 return this.decorated.source; 407 } 408 409 mixin MutableDecoratorMixin!(ProxyFactory!T); 410 411 /** 412 Set a locator to object. 413 414 Params: 415 locator = the locator that is set to oject. 416 417 Returns: 418 LocatorAware. 419 **/ 420 ProxyObjectWrappingFactory!T locator(Locator!() locator) @safe nothrow { 421 this.decorated.locator = locator; 422 423 return this; 424 } 425 426 /** 427 Get locator 428 429 Returns: 430 Locator!() 431 **/ 432 Locator!() locator() @safe nothrow { 433 return this.decorated.locator; 434 } 435 436 /** 437 Get the type info of T that is created. 438 439 Returns: 440 TypeInfo object of created component. 441 **/ 442 TypeInfo type() @safe nothrow const { 443 return this.decorated.type; 444 } 445 } 446 /** 447 Instantiates component of type Object. 448 449 Returns: 450 Object instantiated component. 451 **/ 452 Object factory() @safe { 453 return this.decorated.factory(); 454 } 455 456 /** 457 Destructs a component of type T. 458 459 Params: 460 component = component that is to ve destroyed. 461 462 Returns: 463 464 **/ 465 void destruct(ref Object component) @safe 466 in (component !is null, "Cannot destroy a null component, expected component of type " ~ typeid(T).toString) { 467 T proxy = cast(T) component; 468 469 if (proxy !is null) { 470 471 this.decorated.destruct(proxy); 472 return; 473 } 474 475 import aermicioi.aedi.exception.invalid_cast_exception : InvalidCastException; 476 throw new InvalidCastException( 477 "Cannot destruct component ${identity} because it is not managed by this proxy factory. Expected ${expected} while got ${actual}", 478 null, 479 typeid(Proxy!T), 480 component.classinfo 481 ); 482 } 483 } 484 }