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 }