1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 | Object.defineProperty(exports, "__esModule", { value: true });
|
6 | const fs = require("fs");
|
7 | const path = require("path");
|
8 | const globby = require("globby");
|
9 | const ConfigProvider_1 = require("../config/ConfigProvider");
|
10 | const LogManager_1 = require("../log/LogManager");
|
11 | const PromiseUtil_1 = require("../util/PromiseUtil");
|
12 | const Lifecycle_1 = require("./Lifecycle");
|
13 | const IoCException_1 = require("./IoCException");
|
14 | const ObjectDefinition_1 = require("./objectdefinition/ObjectDefinition");
|
15 | const BaseSingletonDefinition_1 = require("./objectdefinition/BaseSingletonDefinition");
|
16 | const PreinstantiatedSingletonDefinition_1 = require("./objectdefinition/PreinstantiatedSingletonDefinition");
|
17 | class Context extends Lifecycle_1.Lifecycle {
|
18 | constructor(name, parentContext, options = {}) {
|
19 | super(name, options.logger || LogManager_1.LogManager.getLogger(__filename));
|
20 | this.config = options.config || new ConfigProvider_1.default();
|
21 | this.shutdownTimer = options.shutdownTimer || 100;
|
22 | this.parentContext = parentContext;
|
23 | this.objectDefinitions = new Map();
|
24 | this.startedObjects = new Map();
|
25 | this.objectDefinitionInspector = [];
|
26 | this.objectGroups = new Map();
|
27 | this.registerDefinition(new PreinstantiatedSingletonDefinition_1.PreinstantiatedSingletonDefinition(this, '__CONTEXT__'));
|
28 | }
|
29 |
|
30 |
|
31 |
|
32 | lcStart() {
|
33 | if (this.parentContext) {
|
34 | return this.parentContext.lcStart().then(() => super.lcStart());
|
35 | }
|
36 | return super.lcStart();
|
37 | }
|
38 | lcStop() {
|
39 | const stopPromise = super.lcStop();
|
40 | if (this.parentContext) {
|
41 | return stopPromise.then(() => this.parentContext.lcStop());
|
42 | }
|
43 | return stopPromise;
|
44 | }
|
45 | doStart() {
|
46 | this.applyObjectDefinitionModifiers();
|
47 | return PromiseUtil_1.PromiseUtil.map(Array.from(this.objectDefinitions.values()).filter((o) => !o.isLazy()), (objectDefinition) => objectDefinition.getInstance())
|
48 | .catch((err) => {
|
49 | this.getLogger().error({ err }, 'There was an error starting context. At least one non-lazy object threw an exception during startup. Stopping context');
|
50 | return this.lcStop().then(() => {
|
51 | throw err;
|
52 | });
|
53 | })
|
54 | .then(() => {
|
55 | this.getLogger().debug(`Context ${this.getName()} started`);
|
56 | });
|
57 | }
|
58 | async doStop() {
|
59 | this.getLogger().debug(`Waiting ${this.shutdownTimer}ms to stop context`);
|
60 | await PromiseUtil_1.PromiseUtil.sleepPromise(this.shutdownTimer);
|
61 | return PromiseUtil_1.PromiseUtil.map(Array.from(this.startedObjects.values()), (startedObject) => startedObject.lcStop()).then(() => {
|
62 |
|
63 | });
|
64 | }
|
65 |
|
66 |
|
67 |
|
68 | clone(name) {
|
69 | this.assertState(Lifecycle_1.LifecycleState.NOT_STARTED);
|
70 | const copy = new Context(name, this.parentContext, { logger: this.logger, config: this.config });
|
71 | this.objectDefinitions.forEach((objectDefinition) => {
|
72 | if (objectDefinition.getName() !== '__CONTEXT__') {
|
73 | copy.registerDefinition(objectDefinition.copy());
|
74 | }
|
75 | });
|
76 | return copy;
|
77 | }
|
78 | importContext(otherContext, overwrite = false) {
|
79 | this.assertState(Lifecycle_1.LifecycleState.NOT_STARTED);
|
80 | otherContext.assertState(Lifecycle_1.LifecycleState.NOT_STARTED);
|
81 | otherContext.objectDefinitions.forEach((objectDefinition) => {
|
82 | if (objectDefinition.getName() !== '__CONTEXT__') {
|
83 | this.registerDefinition(objectDefinition, overwrite);
|
84 | }
|
85 | });
|
86 | }
|
87 |
|
88 |
|
89 |
|
90 | addObjectDefinitionInspector(inspector) {
|
91 | this.objectDefinitionInspector.push(inspector);
|
92 | }
|
93 |
|
94 |
|
95 |
|
96 | registerDefinition(objectDefinition, overwrite = false) {
|
97 | this.assertState(Lifecycle_1.LifecycleState.NOT_STARTED);
|
98 | if (!(objectDefinition instanceof ObjectDefinition_1.ObjectDefinition)) {
|
99 | throw new IoCException_1.IoCException('Provided input for registration is not an instance of ObjectDefinition');
|
100 | }
|
101 | if (!overwrite && this.objectDefinitions.has(objectDefinition.getName())) {
|
102 | throw new IoCException_1.IoCException(`Object definition with name ${objectDefinition.getName()} already exists in this context`);
|
103 | }
|
104 | if (this.parentContext && this.parentContext.objectDefinitions.has(objectDefinition.getName()) && objectDefinition.getName() !== '__CONTEXT__') {
|
105 | throw new IoCException_1.IoCException(`Parent context already has an object definition with name ${objectDefinition.getName()}`);
|
106 | }
|
107 | this.objectDefinitions.set(objectDefinition.getName(), objectDefinition);
|
108 | objectDefinition.setContext(this);
|
109 | objectDefinition.onStateOnce(Lifecycle_1.LifecycleState.STARTED, () => this.startedObjects.set(objectDefinition.getName(), objectDefinition));
|
110 | objectDefinition.onStateOnce(Lifecycle_1.LifecycleState.STOPPED, () => this.startedObjects.delete(objectDefinition.getName()));
|
111 | }
|
112 | registerSingletons(...singletons) {
|
113 | singletons.forEach((singleton) => {
|
114 | if (singleton instanceof ObjectDefinition_1.ObjectDefinition) {
|
115 | this.registerDefinition(singleton);
|
116 | }
|
117 | else if (singleton instanceof Function) {
|
118 | this.registerDefinition(new BaseSingletonDefinition_1.BaseSingletonDefinition(singleton));
|
119 | this.logger.debug(`Registering singleton ${singleton.name}`);
|
120 | }
|
121 | else {
|
122 | throw new IoCException_1.IoCException(`Not sure how to convert input into SingletonDefinition: ${singleton}`);
|
123 | }
|
124 | });
|
125 | }
|
126 | registerSingletonsInDir(patterns, options) {
|
127 | Context.findMatchingFiles(patterns, options).filter((file) => ['.js', '.ts'].indexOf(path.extname(file)) >= 0).forEach((file) => {
|
128 | if (file.includes('.d.ts')) {
|
129 | return;
|
130 | }
|
131 | let expectedClass = path.basename(file);
|
132 | expectedClass = expectedClass.substr(0, expectedClass.length - 3);
|
133 | const loaded = require(file);
|
134 | if (loaded) {
|
135 | if (typeof loaded === 'object' && loaded.__esModule && loaded.default && loaded.default.constructor) {
|
136 | this.registerSingletons(loaded.default);
|
137 | }
|
138 | else if (typeof loaded === 'object' &&
|
139 | !(loaded instanceof Function) &&
|
140 | loaded[expectedClass] &&
|
141 | loaded[expectedClass].constructor) {
|
142 | this.registerSingletons(loaded[expectedClass]);
|
143 | }
|
144 | else if (loaded instanceof Function && loaded.name && loaded.name === expectedClass) {
|
145 | this.registerSingletons(loaded);
|
146 | }
|
147 | else {
|
148 | throw new IoCException_1.IoCException(`Couldn't register singleton for ${file}`);
|
149 | }
|
150 | }
|
151 | });
|
152 | }
|
153 | static requireFilesInDir(dir) {
|
154 | Context.walkDirSync(dir).filter((file) => path.extname(file) === '.js').forEach((file) => {
|
155 |
|
156 | require(file);
|
157 | });
|
158 | }
|
159 | |
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 | static walkDirSync(dir, filelist = []) {
|
168 | fs.readdirSync(dir).forEach((file) => {
|
169 | filelist = fs.statSync(path.join(dir, file)).isDirectory()
|
170 | ? Context.walkDirSync(path.join(dir, file), filelist)
|
171 | : filelist.concat(path.join(dir, file));
|
172 | });
|
173 | return filelist;
|
174 | }
|
175 | |
176 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 | static findMatchingFiles(patterns, { isGlob, globOptions }) {
|
184 |
|
185 | if (globby.hasMagic(patterns) || Array.isArray(patterns) || isGlob) {
|
186 | return globby.sync(patterns, globOptions);
|
187 | }
|
188 |
|
189 | return Context.walkDirSync(patterns);
|
190 | }
|
191 |
|
192 |
|
193 |
|
194 | |
195 |
|
196 |
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 |
|
203 |
|
204 |
|
205 |
|
206 | getConfig(key, defaultValue) {
|
207 | return this.config.getConfig(key, defaultValue);
|
208 | }
|
209 | |
210 |
|
211 |
|
212 |
|
213 |
|
214 | hasConfig(key) {
|
215 | return this.config.hasConfig(key);
|
216 | }
|
217 |
|
218 |
|
219 |
|
220 | addObjectNameToGroup(groupName, objectName) {
|
221 | if (!this.objectGroups.has(groupName)) {
|
222 | this.objectGroups.set(groupName, []);
|
223 | }
|
224 | this.objectGroups.get(groupName).push(objectName);
|
225 | }
|
226 | getGroupObjectNames(groupName) {
|
227 | if (!this.objectGroups.has(groupName)) {
|
228 | return [];
|
229 | }
|
230 | return this.objectGroups.get(groupName);
|
231 | }
|
232 |
|
233 |
|
234 |
|
235 | getObjectByName(beanName) {
|
236 | const beanDefinition = this.getDefinitionByName(beanName);
|
237 | return beanDefinition.getInstance();
|
238 | }
|
239 | getObjectByType(className) {
|
240 | const beanDefinition = this.getDefinitionByType(className);
|
241 | return beanDefinition.getInstance();
|
242 | }
|
243 | getObjectsByType(className) {
|
244 | const beanDefinitions = this.getDefinitionsByType(className);
|
245 | const instances = PromiseUtil_1.PromiseUtil.map(beanDefinitions, (bd) => bd.getInstance());
|
246 | return instances.then((arr) => {
|
247 | arr.sort((a, b) => {
|
248 | const aPos = Object.hasOwnProperty.call(a, 'getOrder') ? a.getOrder() : 0;
|
249 | const bPos = Object.hasOwnProperty.call(b, 'getOrder') ? b.getOrder() : 0;
|
250 | if (aPos === bPos) {
|
251 | return 0;
|
252 | }
|
253 | else if (aPos < bPos) {
|
254 | return -1;
|
255 | }
|
256 | return 1;
|
257 | });
|
258 | return arr;
|
259 | });
|
260 | }
|
261 | getObjectsByGroup(groupName) {
|
262 | const objectNames = this.getGroupObjectNames(groupName);
|
263 | const objectDefinitions = objectNames.map((objectName) => this.getDefinitionByName(objectName));
|
264 | return objectDefinitions.reduce(async (acum, def) => {
|
265 | (await acum).push(await def.getInstance());
|
266 | return Promise.resolve(acum);
|
267 | }, Promise.resolve([]));
|
268 | }
|
269 |
|
270 |
|
271 |
|
272 | getDefinitionByName(objectName) {
|
273 | const val = this.objectDefinitions.get(objectName);
|
274 | if (val) {
|
275 | return val;
|
276 | }
|
277 | if (this.parentContext) {
|
278 | return this.parentContext.getDefinitionByName(objectName);
|
279 | }
|
280 | throw new IoCException_1.IoCException(`No object definition with name ${objectName} registered in the context`);
|
281 | }
|
282 | getDefinitionByType(className) {
|
283 | const resp = this.getDefinitionsByType(className);
|
284 | if (resp.length > 1) {
|
285 | throw new IoCException_1.IoCException(`Found more than one object definition in the context that produces a ${className}`);
|
286 | }
|
287 | return resp[0];
|
288 | }
|
289 | getDefinitionsByType(className, failOnMissing = true) {
|
290 | const resp = new Map();
|
291 | if (this.parentContext) {
|
292 | this.parentContext
|
293 | .getDefinitionsByType(className, false)
|
294 | .forEach((objectDefinition) => resp.set(objectDefinition.getName(), objectDefinition));
|
295 | }
|
296 | this.objectDefinitions.forEach((objectDefinition) => {
|
297 | if (objectDefinition.getProducedClass().name === className && objectDefinition.autowireCandidate) {
|
298 | resp.set(objectDefinition.getName(), objectDefinition);
|
299 | }
|
300 | });
|
301 | if (resp.size === 0 && failOnMissing) {
|
302 | throw new IoCException_1.IoCException(`Couldn't find a bean that produces class ${className}`);
|
303 | }
|
304 | return Array.from(resp.values());
|
305 | }
|
306 | getDefinitionsByGroup(groupName) {
|
307 | const objectNames = this.getGroupObjectNames(groupName);
|
308 | return objectNames.map((objectName) => this.getDefinitionByName(objectName));
|
309 | }
|
310 | applyObjectDefinitionModifiers() {
|
311 |
|
312 | this.objectDefinitionInspector.forEach((inspector) => {
|
313 | this.objectDefinitions.forEach((objectDefinition, key) => {
|
314 | const result = inspector.inspect(objectDefinition);
|
315 | if (result) {
|
316 | this.objectDefinitions.set(key, result);
|
317 | }
|
318 | });
|
319 | });
|
320 | }
|
321 | }
|
322 | exports.Context = Context;
|
323 |
|
\ | No newline at end of file |