1 /** 2 License: 3 Boost Software License - Version 1.0 - August 17th, 2003 4 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 27 Authors: 28 aermicioi 29 **/ 30 31 module aermicioi.aedi.container.factory; 32 33 import aermicioi.aedi.factory.factory; 34 import aermicioi.aedi.factory.decorating_factory; 35 import aermicioi.aedi.exception; 36 import aermicioi.aedi.storage.locator; 37 import aermicioi.aedi.storage.allocator_aware; 38 import aermicioi.aedi.storage.decorator : MutableDecoratorMixin; 39 40 /** 41 A decorating factory that detects circular reference insantiations. 42 **/ 43 @safe class InProcessObjectFactoryDecorator : ObjectFactory, ObjectFactoryDecorator { 44 45 mixin AllocatorAwareMixin!(typeof(this)); 46 mixin DestructDecoratorMixin!(typeof(this)); 47 48 private { 49 bool inProcess; 50 } 51 52 public { 53 mixin MutableDecoratorMixin!(ObjectFactory); 54 55 /** 56 * Default constructor for InProcessObjectFactoryDecorator 57 **/ 58 this() { 59 60 } 61 62 /** 63 * Constructor for InProcessObjectFactoryDecorator 64 * 65 * Params: 66 * decorated = factory that is decorated with InProcessObjectFactoryDecorator 67 **/ 68 this(ObjectFactory decorated) { 69 this.decorated = decorated; 70 } 71 72 @property { 73 /** 74 * Get type of created object 75 * 76 * Returns: 77 * TypeInfo 78 **/ 79 TypeInfo type() const @safe { 80 return this.decorated.type(); 81 } 82 83 /** 84 * Set locator 85 * 86 * Params: 87 * locator = ${param-description} 88 * Throws: 89 * Returns: 90 * typeof(this) 91 **/ 92 InProcessObjectFactoryDecorator locator(Locator!() locator) { 93 this.decorated.locator = locator; 94 95 return this; 96 } 97 } 98 99 /** 100 Factory an object. 101 102 Returns: 103 Object created object. 104 **/ 105 Object factory() @safe { 106 if (inProcess) { 107 throw new InProgressException("ObjectFactory is already instantiating component of ${type} type", null, type); 108 } 109 110 inProcess = true; 111 scope(exit) inProcess = false; 112 113 Object obj = this.decorated.factory(); 114 115 return obj; 116 } 117 } 118 } 119 120 /** 121 A decorating factory that catches any thrown exceptions by decorated factory, 122 and rethrows them with additional information. 123 124 A decorating factory that catches any thrown exceptions by decorated factory, 125 and chains it in a new exception that adds additonal debugging information 126 such as registered identity, and cause of exception. It is useful for chaining 127 component instantiation pipeline and printing it for debugging purposes. 128 **/ 129 @safe class ExceptionChainingObjectFactory : ObjectFactory, ObjectFactoryDecorator { 130 131 mixin AllocatorAwareMixin!(typeof(this)); 132 mixin DestructDecoratorMixin!(typeof(this)); 133 134 private { 135 string id_; 136 } 137 138 public { 139 mixin MutableDecoratorMixin!(ObjectFactory); 140 141 /** 142 * Default constructor for ExceptionChainingObjectFactory 143 **/ 144 this() { 145 146 } 147 148 /** 149 * Constructor for ExceptionChainingObjectFactory 150 * 151 * Params: 152 * decorated = factory to be decorated 153 * id = identity of created object, used for exception message. 154 **/ 155 this(ObjectFactory decorated, string id) { 156 this.decorated = decorated; 157 this.id = id; 158 } 159 160 @property { 161 /** 162 * Set id 163 * 164 * Params: 165 * id = identity of created object, used for exception message. 166 * Returns: 167 * typeof(this) 168 **/ 169 ExceptionChainingObjectFactory id(string id) @safe nothrow { 170 this.id_ = id; 171 172 return this; 173 } 174 175 /** 176 * Get id 177 * 178 * Returns: 179 * string 180 **/ 181 string id() @safe nothrow { 182 return this.id_; 183 } 184 185 /** 186 * Get type of created object 187 * 188 * Returns: 189 * TypeInfo 190 **/ 191 TypeInfo type() const @safe { 192 return this.decorated.type(); 193 } 194 195 /** 196 * Set locator 197 * 198 * Params: 199 * locator = the locator used to locate dependencies for created object. 200 * Returns: 201 * typeof(this) 202 **/ 203 ExceptionChainingObjectFactory locator(Locator!() locator) @safe { 204 this.decorated.locator = locator; 205 206 return this; 207 } 208 } 209 210 /** 211 Factory an object, and catch any exception wrapping it in a library exception to be rethrown further. 212 213 Returns: 214 Object created object. 215 **/ 216 Object factory() @safe { 217 218 try { 219 Object obj = this.decorated.factory(); 220 221 return obj; 222 } catch (NotFoundException e) { 223 224 throw new NotFoundException("A dependency for ${identity} could not be found", this.id, e).processThrowChain; 225 } catch (InProgressException e) { 226 e.identity = this.id; 227 228 throw new CircularReferenceException("Circular reference detected during construction of ${identity} with chain of dependencies ${chain}", this.id, e); 229 } catch(CircularReferenceException e) { 230 e.chain ~= this.id; 231 232 throw e.processThrowChain; 233 } catch(PropertyConfigurerException e) { 234 e.identity = this.id; 235 236 throw e.processThrowChain; 237 } catch(InstanceFactoryException e) { 238 e.identity = this.id; 239 240 throw e.processThrowChain; 241 } catch (AediException e) { 242 243 throw new AediException("A library exception occurred during construction of ${identity}", this.id, e).processThrowChain; 244 } catch (Exception e) { 245 import std.conv : text; 246 throw new Exception(text("A general exception occurred during construction of ", this.id), e); 247 } 248 } 249 } 250 } 251 252 private { 253 T processThrowChain(T : AediException)(T e) { 254 import aermicioi.aedi.util.range : exceptions, filterByInterface; 255 foreach (CircularReferenceException exception; e.exceptions.filterByInterface!CircularReferenceException) { 256 exception.chain ~= e.identity; 257 } 258 259 return e; 260 } 261 }