1 /**
2 
3 License:
4 	Boost Software License - Version 1.0 - August 17th, 2003
5 
6     Permission is hereby granted, free of charge, to any person or organization
7     obtaining a copy of the software and accompanying documentation covered by
8     this license (the "Software") to use, reproduce, display, distribute,
9     execute, and transmit the Software, and to prepare derivative works of the
10     Software, and to permit third-parties to whom the Software is furnished to
11     do so, all subject to the following:
12 
13     The copyright notices in the Software and this entire statement, including
14     the above license grant, this restriction and the following disclaimer,
15     must be included in all copies of the Software, in whole or in part, and
16     all derivative works of the Software, unless such copies or derivative
17     works are solely in the form of machine-executable object code generated by
18     a source language processor.
19 
20     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22     FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23     SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24     FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25     ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26     DEALINGS IN THE SOFTWARE.
27 
28 Authors:
29 	Alexandru Ermicioi
30 **/
31 module aermicioi.aedi.container.singleton_container;
32 
33 import aermicioi.aedi.container.container;
34 import aermicioi.aedi.util.typecons : Pair, pair;
35 import aermicioi.aedi.storage.object_storage;
36 import aermicioi.aedi.storage.locator_aware;
37 import aermicioi.aedi.storage.locator;
38 import aermicioi.aedi.factory.factory;
39 import aermicioi.aedi.exception;
40 import aermicioi.aedi.container.factory;
41 
42 import std.range.interfaces;
43 
44 /**
45  Singleton container.
46 
47  It creates objects from ObjectFactory implementations and sets them as long as it lives in application.
48 **/
49 @safe class SingletonContainer : ConfigurableContainer {
50 
51     private {
52 
53         ObjectStorage!() singletons;
54         ObjectStorage!(ObjectFactory, string) factories;
55     }
56 
57     public {
58 
59         /**
60          * Default constructor for SingletonContainer
61         **/
62         this() {
63             this.singletons = new ObjectStorage!();
64             this.factories = new ObjectStorage!(ObjectFactory, string);
65         }
66 
67         /**
68          * Set object factory
69          *
70          * Params:
71          * 	object = factory for a object that is to be managed by prototype container.
72          *  key = identity of factory
73          * Returns:
74          * 	typeof(this)
75         **/
76         SingletonContainer set(ObjectFactory object, string key) {
77             this.factories.set(new ExceptionChainingObjectFactory(new InProcessObjectFactoryDecorator(object), key), key);
78 
79             return this;
80         }
81 
82         /**
83          * Remove an object factory from container.
84          *
85          * Params:
86          * 	key = identity of factory to be removed
87          * Returns:
88          * 	typeof(this)
89         **/
90         SingletonContainer remove(string key) {
91 
92             if (this.singletons.has(key)) {
93 
94                 auto temporary = this.singletons.get(key);
95                 this.factories.get(key).destruct(temporary);
96             }
97 
98             this.factories.remove(key);
99             this.singletons.remove(key);
100 
101             return this;
102         }
103 
104         /**
105          * Get object created by a factory identified by key
106          *
107          * Params:
108          *  key = identity of factory
109          * Returns:
110          * 	Object
111         **/
112         Object get(string key) {
113 
114             if (!this.singletons.has(key)) {
115                 if (!this.factories.has(key)) {
116                     throw new NotFoundException("Component ${identity} not found.", key);
117                 }
118 
119                 this.singletons.set(
120                     this.factories.get(key).factory(),
121                     this.resolve(key),
122                 );
123             }
124 
125             return this.singletons.get(key);
126         }
127 
128         /**
129          * Check if an object factory for it exists in container.
130          *
131          * Params:
132          * 	key = identity of factory
133          * Returns:
134          * 	bool
135         **/
136         bool has(in string key) inout {
137             return this.factories.has(key);
138         }
139 
140         /**
141         Sets up the internal state of container.
142 
143         Sets up the internal state of container (Ex, for singleton container it will spawn all objects that locator contains).
144         **/
145         SingletonContainer instantiate() {
146             import std.algorithm : filter;
147             foreach (pair; this.factories.contents.byKeyValue.filter!((pair) => pair.key !in this.singletons.contents)) {
148                 this.singletons.set(
149                     pair.value.factory,
150                     pair.key,
151                 );
152             }
153 
154             return this;
155         }
156 
157         /**
158         Destruct all managed components.
159 
160         Destruct all managed components. The method denotes the end of container lifetime, and therefore destruction of all managed components
161         by it.
162         **/
163         Container terminate() {
164             foreach (pair; this.singletons.contents.byKeyValue) {
165                 this.factories.get(pair.key).destruct(pair.value);
166             }
167 
168             (() scope @trusted => this.singletons.contents.clear)();
169 
170             return this;
171         }
172 
173         /**
174         Alias a key to an alias_.
175 
176         Params:
177         	key = the originial identity which is to be aliased.
178         	alias_ = the alias of identity.
179 
180 		Returns:
181 			this
182         **/
183         SingletonContainer link(string key, string alias_) {
184             this.singletons.link(key, alias_);
185             this.factories.link(key, alias_);
186 
187             return this;
188         }
189 
190         /**
191         Removes alias.
192 
193         Params:
194         	alias_ = alias to remove.
195 
196         Returns:
197             this
198 
199         **/
200         SingletonContainer unlink(string alias_) {
201             this.singletons.unlink(alias_);
202             this.factories.unlink(alias_);
203 
204             return this;
205         }
206 
207         /**
208         Resolve an alias to original identity, if possible.
209 
210         Params:
211         	key = alias of original identity
212 
213         Returns:
214         	Type the last identity in alias chain.
215 
216         **/
217         const(string) resolve(in string key) const {
218             return this.factories.resolve(key);
219         }
220 
221         /**
222         Get factory for constructed component identified by identity.
223 
224         Get factory for constructed component identified by identity.
225         Params:
226         	identity = the identity of component that factory constructs.
227 
228         Throws:
229         	NotFoundException when factory for it is not found.
230 
231         Returns:
232         	ObjectFactory the factory for constructed component.
233         **/
234         ObjectFactory getFactory(string identity) {
235             return this.factories.get(identity);
236         }
237 
238         /**
239         Get all factories available in container.
240 
241         Get all factories available in container.
242 
243         Returns:
244         	InputRange!(Pair!(ObjectFactory, string)) a pair of factory => identity.
245         **/
246         InputRange!(Pair!(ObjectFactory, string)) getFactories() {
247             import std.algorithm : map;
248 
249             return this.factories.contents.byKeyValue.map!(
250                 a => Pair!(ObjectFactory, string)(a.value, a.key)
251             ).inputRangeObject;
252         }
253     }
254 }