MentaContainer

Rev

Rev 97 | Rev 102 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 soliveira 1
package org.mentacontainer.impl;
2
 
3
import java.lang.reflect.Method;
71 soliveira 4
import java.util.Collections;
2 soliveira 5
import java.util.HashSet;
71 soliveira 6
import java.util.Hashtable;
7
import java.util.LinkedList;
8
import java.util.List;
2 soliveira 9
import java.util.Map;
10
import java.util.Set;
11
 
88 soliveira 12
import org.mentacontainer.ConfigurableFactory;
2 soliveira 13
import org.mentacontainer.Container;
94 soliveira 14
import org.mentacontainer.Factory;
15
import org.mentacontainer.Interceptable;
57 soliveira 16
import org.mentacontainer.Scope;
4 soliveira 17
import org.mentacontainer.util.InjectionUtils;
22 soliveira 18
import org.mentacontainer.util.InjectionUtils.Provider;
2 soliveira 19
 
20 soliveira 20
/**
21
 * The implementation of the IoC container.
22
 *
23
 * @author sergio.oliveira.jr@gmail.com
24
 */
2 soliveira 25
public class MentaContainer implements Container {
26
 
91 soliveira 27
        private Map<String, Factory> factoriesByName = new Hashtable<String, Factory>();
51 soliveira 28
 
91 soliveira 29
        private Map<Class<? extends Object>, List<String>> factoryKeysByType = new Hashtable<Class<? extends Object>, List<String>>();
30
 
71 soliveira 31
        private Map<String, Scope> scopes = new Hashtable<String, Scope>();
51 soliveira 32
 
71 soliveira 33
        private Map<String, Object> singletonsCache = new Hashtable<String, Object>();
57 soliveira 34
 
71 soliveira 35
        private Map<String, ThreadLocal<Object>> threadLocalsCache = new Hashtable<String, ThreadLocal<Object>>();
91 soliveira 36
 
97 soliveira 37
        private Set<SetterDependency> setterDependencies = Collections.synchronizedSet(new HashSet<SetterDependency>());
57 soliveira 38
 
97 soliveira 39
        private Set<ConstructorDependency> constructorDependencies = Collections.synchronizedSet(new HashSet<ConstructorDependency>());
40
 
84 soliveira 41
        @Override
91 soliveira 42
        public Class<? extends Object> getType(String key) {
43
 
44
                Factory factory = factoriesByName.get(key);
45
 
46
                if (factory == null) return null;
47
 
48
                return factory.getType();
49
        }
50
 
51
        @Override
71 soliveira 52
        public void clear(Scope scope) {
57 soliveira 53
 
54
                if (scope == Scope.SINGLETON) {
55
 
90 soliveira 56
                        List<ClearableHolder> listToClear = new LinkedList<ClearableHolder>();
71 soliveira 57
 
58
                        synchronized(this) {
59
 
60
                        for(String key : singletonsCache.keySet()) {
61
 
91 soliveira 62
                                Factory comp = factoriesByName.get(key);
71 soliveira 63
 
88 soliveira 64
                                if (comp instanceof Interceptable) {
71 soliveira 65
 
88 soliveira 66
                                        Interceptable<Object> c = (Interceptable<Object>) comp;
71 soliveira 67
 
68
                                        Object value = singletonsCache.get(key);
69
 
90 soliveira 70
                                        listToClear.add(new ClearableHolder(c, value));
71 soliveira 71
                                }
72
                        }
73
 
74
                        singletonsCache.clear();
64 soliveira 75
                        }
76
 
71 soliveira 77
                        // clear everything inside a non-synchronized block...
57 soliveira 78
 
90 soliveira 79
                        for(ClearableHolder cp : listToClear) cp.clear();
71 soliveira 80
 
57 soliveira 81
                } else if (scope == Scope.THREAD) {
82
 
90 soliveira 83
                        List<ClearableHolder> listToClear = new LinkedList<ClearableHolder>();
71 soliveira 84
 
85
                        synchronized(this) {
86
 
87
                        for(String key : threadLocalsCache.keySet()) {
88
 
91 soliveira 89
                                Factory comp = factoriesByName.get(key);
71 soliveira 90
 
88 soliveira 91
                                if (comp instanceof Interceptable) {
71 soliveira 92
 
88 soliveira 93
                                        Interceptable<Object> c = (Interceptable<Object>) comp;
71 soliveira 94
 
95
                                        ThreadLocal<Object> t = threadLocalsCache.get(key);
96
 
97
                                        Object value = t.get();
98
 
90 soliveira 99
                                        if (value != null) listToClear.add(new ClearableHolder(c, value));
71 soliveira 100
                                }
101
                        }
102
 
103
                        for(ThreadLocal<Object> t : threadLocalsCache.values()) {
104
 
105
                                t.set(null);
106
                        }
64 soliveira 107
                        }
108
 
71 soliveira 109
                        // clear everything inside a non-synchronized block...
110
 
90 soliveira 111
                        for(ClearableHolder cp : listToClear) cp.clear();
57 soliveira 112
                }
113
        }
58 soliveira 114
 
84 soliveira 115
        @Override
71 soliveira 116
        public <T> T clear(String key) {
58 soliveira 117
 
91 soliveira 118
                if (!factoriesByName.containsKey(key)) return null;
59 soliveira 119
 
58 soliveira 120
                Scope scope = scopes.get(key);
121
 
122
                if (scope == Scope.SINGLETON) {
123
 
90 soliveira 124
                        ClearableHolder cp = null;
58 soliveira 125
 
71 soliveira 126
                        Object value = null;
127
 
128
                        synchronized(this) {
129
 
130
                        value = singletonsCache.remove(key);
131
 
132
                        if (value != null) {
133
 
91 soliveira 134
                                Factory comp = factoriesByName.get(key);
71 soliveira 135
 
88 soliveira 136
                                if (comp instanceof Interceptable) {
71 soliveira 137
 
88 soliveira 138
                                        Interceptable<Object> c = (Interceptable<Object>) comp;
71 soliveira 139
 
90 soliveira 140
                                        cp = new ClearableHolder(c, value);
71 soliveira 141
                                }
142
                        }
64 soliveira 143
                        }
144
 
71 soliveira 145
                        if (cp != null) cp.c.onCleared(cp.value);
146
 
64 soliveira 147
                        return (T) value;
148
 
58 soliveira 149
                } else if (scope == Scope.THREAD) {
150
 
90 soliveira 151
                        ClearableHolder cp = null;
58 soliveira 152
 
71 soliveira 153
                        Object retVal = null;
154
 
155
                        synchronized(this) {
156
 
157
                        ThreadLocal<Object> t = threadLocalsCache.get(key);
158
 
159
                        if (t != null) {
160
 
161
                                Object o = t.get();
162
 
163
                                if (o != null) {
164
 
91 soliveira 165
                                        Factory comp = factoriesByName.get(key);
71 soliveira 166
 
88 soliveira 167
                                        if (comp instanceof Interceptable) {
71 soliveira 168
 
88 soliveira 169
                                                Interceptable<Object> c = (Interceptable<Object>) comp;
71 soliveira 170
 
90 soliveira 171
                                                cp = new ClearableHolder(c, o);
71 soliveira 172
                                        }
173
 
174
                                        t.set(null);
175
 
176
                                        retVal = o;
177
                                }
178
                        }
58 soliveira 179
                        }
180
 
71 soliveira 181
                        if (cp != null) cp.c.onCleared(cp.value);
182
 
183
                        return (T) retVal;
58 soliveira 184
 
185
                } else if (scope == Scope.NONE) {
186
 
187
                        return null; // always...
188
 
189
                } else {
190
 
191
                        throw new UnsupportedOperationException("Scope not supported: " + scope);
192
                }
193
        }
4 soliveira 194
 
84 soliveira 195
        @Override
71 soliveira 196
        public <T> T get(String key) {
4 soliveira 197
 
91 soliveira 198
                if (!factoriesByName.containsKey(key)) return null;
4 soliveira 199
 
91 soliveira 200
                Factory c = factoriesByName.get(key);
51 soliveira 201
 
57 soliveira 202
                Scope scope = scopes.get(key);
203
 
20 soliveira 204
                Object target = null;
4 soliveira 205
 
20 soliveira 206
                try {
4 soliveira 207
 
57 soliveira 208
                        if (scope == Scope.SINGLETON) {
71 soliveira 209
 
210
                                boolean needsToCreate = false;
211
 
212
                                synchronized(this) {
4 soliveira 213
 
71 soliveira 214
                                if (singletonsCache.containsKey(key)) {
215
 
216
                                        target = singletonsCache.get(key);
217
 
218
                                        return (T) target; // no need to wire again...
219
 
220
                                } else {
221
 
222
                                        needsToCreate = true;
223
                                }
224
                                }
225
 
226
                                if (needsToCreate) {
227
 
228
                                        // getInstance needs to be in a non-synchronized block
229
 
20 soliveira 230
                                        target = c.getInstance();
71 soliveira 231
 
90 soliveira 232
                                        checkInterceptable(c, target);
233
 
71 soliveira 234
                                        synchronized(this) {
4 soliveira 235
 
71 soliveira 236
                                                singletonsCache.put(key, target);
237
                                        }
20 soliveira 238
                                }
57 soliveira 239
 
240
                        } else if (scope == Scope.THREAD) {
241
 
71 soliveira 242
                                boolean needsToCreate = false;
243
 
244
                                boolean needsToAddToCache = false;
245
 
246
                                ThreadLocal<Object> t = null;
247
 
248
                                synchronized(this) {
249
 
250
                                if (threadLocalsCache.containsKey(key)) {
251
 
252
                                        t = threadLocalsCache.get(key);
253
 
254
                                        target = t.get();
255
 
256
                                        if (target == null) { // different thread...
257
 
258
                                                needsToCreate = true;
259
 
260
                                                // don't return... let it be wired...
261
 
262
                                        } else {
263
 
264
                                                return (T) target; // no need to wire again...
265
 
266
                                        }
267
 
268
                                } else {
269
 
270
                                        t = new ThreadLocal<Object>();
271
 
272
                                        needsToCreate = true;
273
 
274
                                                needsToAddToCache = true;
275
 
276
                                        // let it be wired...
277
                                }
278
                                }
279
 
280
                                if (needsToCreate) {
57 soliveira 281
 
71 soliveira 282
                                        // getInstance needs to be in a non-synchronized block
57 soliveira 283
 
284
                                        target = c.getInstance();
285
 
90 soliveira 286
                                        checkInterceptable(c, target);
287
 
57 soliveira 288
                                        t.set(target);
71 soliveira 289
                                }
290
 
291
                                if (needsToAddToCache) {
57 soliveira 292
 
71 soliveira 293
                                        synchronized(this) {
57 soliveira 294
 
71 soliveira 295
                                                threadLocalsCache.put(key, t);
296
                                        }
57 soliveira 297
                                }
298
 
299
                        } else if (scope == Scope.NONE) {
4 soliveira 300
 
20 soliveira 301
                                target = c.getInstance();
57 soliveira 302
 
90 soliveira 303
                                checkInterceptable(c, target);
304
 
57 soliveira 305
                        } else {
306
 
307
                                throw new UnsupportedOperationException("Don't know how to handle scope: " + scope);
20 soliveira 308
                        }
4 soliveira 309
 
20 soliveira 310
                        if (target != null) {
4 soliveira 311
 
97 soliveira 312
                                for (SetterDependency d : setterDependencies) {
4 soliveira 313
 
20 soliveira 314
                                        // has dependency ?
315
                                        Method m = d.check(target.getClass());
4 soliveira 316
 
20 soliveira 317
                                        if (m != null) {
4 soliveira 318
 
20 soliveira 319
                                                String sourceKey = d.getSource();
4 soliveira 320
 
20 soliveira 321
                                                if (sourceKey.equals(key)) {
4 soliveira 322
 
20 soliveira 323
                                                        // cannot depend on itself... also avoid recursive StackOverflow...
4 soliveira 324
 
20 soliveira 325
                                                        continue;
4 soliveira 326
 
20 soliveira 327
                                                }
2 soliveira 328
 
20 soliveira 329
                                                Object source = get(sourceKey);
2 soliveira 330
 
94 soliveira 331
                                                try {
4 soliveira 332
 
94 soliveira 333
                                                        // inject
334
                                                        m.invoke(target, source);
4 soliveira 335
 
94 soliveira 336
                                                } catch (Exception e) {
4 soliveira 337
 
94 soliveira 338
                                                        throw new RuntimeException("Cannot inject dependency: method = " + (m != null ? m.getName() : "NULL") + " / source = "
339
                                                                + (source != null ? source : "NULL") + " / target = " + target, e);
4 soliveira 340
 
20 soliveira 341
                                                }
342
                                        }
343
                                }
4 soliveira 344
                        }
2 soliveira 345
 
41 soliveira 346
                        return (T) target; // return target nicely with all the dependencies
4 soliveira 347
 
20 soliveira 348
                } catch (Exception e) {
4 soliveira 349
 
20 soliveira 350
                        throw new RuntimeException(e);
351
                }
2 soliveira 352
        }
90 soliveira 353
 
354
        private final void checkInterceptable(Factory f, Object value) {
355
 
356
                if (f instanceof Interceptable) {
357
 
358
                        Interceptable i = (Interceptable) f;
359
 
360
                        ((Interceptable) f).onCreated(value);
361
                }
362
        }
91 soliveira 363
 
364
        private void mapeTypeToKeys(String key, Factory factory) {
365
 
366
                Class<? extends Object> type = factory.getType();
367
 
368
                synchronized(factoryKeysByType) {
369
 
370
                        List<String> list = factoryKeysByType.get(type);
371
 
372
                        if (list == null) {
373
 
374
                                list = new LinkedList<String>();
375
 
376
                                factoryKeysByType.put(type, list);
377
                        }
378
 
379
                        list.add(key);
380
                }
381
        }
4 soliveira 382
 
84 soliveira 383
        @Override
91 soliveira 384
        public Factory ioc(String key, Factory factory, Scope scope) {
20 soliveira 385
 
91 soliveira 386
                factoriesByName.put(key, factory);
44 soliveira 387
 
91 soliveira 388
                mapeTypeToKeys(key, factory);
389
 
51 soliveira 390
                singletonsCache.remove(key); // just in case we are overriding a previous singleton bean...
391
 
57 soliveira 392
                threadLocalsCache.remove(key); // just in case we are overriding a previous thread local...
51 soliveira 393
 
57 soliveira 394
                scopes.put(key, scope);
395
 
91 soliveira 396
                return factory;
20 soliveira 397
        }
398
 
84 soliveira 399
        @Override
95 soliveira 400
        public Factory ioc(String key, Factory factory) {
51 soliveira 401
 
95 soliveira 402
                return ioc(key, factory, Scope.NONE);
51 soliveira 403
        }
404
 
84 soliveira 405
        @Override
88 soliveira 406
        public ConfigurableFactory ioc(String key, Class<? extends Object> klass) {
20 soliveira 407
 
91 soliveira 408
                ConfigurableFactory cc = new ClassFactory(this, klass);
45 soliveira 409
 
410
                ioc(key, cc);
411
 
412
                return cc;
20 soliveira 413
        }
414
 
84 soliveira 415
        @Override
88 soliveira 416
        public ConfigurableFactory ioc(String key, Class<? extends Object> klass, Scope scope) {
20 soliveira 417
 
91 soliveira 418
                ConfigurableFactory cc = new ClassFactory(this, klass);
45 soliveira 419
 
57 soliveira 420
                ioc(key, cc, scope);
45 soliveira 421
 
422
                return cc;
20 soliveira 423
        }
101 soliveira 424
 
84 soliveira 425
        @Override
101 soliveira 426
        public void autowire(String sourceFromContainer) {
94 soliveira 427
 
101 soliveira 428
                // autowire by constructor and setter...
429
 
430
                autowireBySetter(sourceFromContainer);
431
 
432
                autowireByConstructor(sourceFromContainer);
433
        }
434
 
435
        @Override
436
        public void autowire(String sourceFromContainer, String beanProperty) {
437
 
438
                // only by setter, because constructors does not have parameter names or properties...
439
 
440
                autowireBySetter(beanProperty, sourceFromContainer);
441
        }
442
 
443
        private void autowireBySetter(String targetProperty, String sourceFromContainer) {
444
 
94 soliveira 445
                Class<? extends Object> sourceType = getType(sourceFromContainer);
4 soliveira 446
 
94 soliveira 447
                SetterDependency d = new SetterDependency(targetProperty, sourceFromContainer, sourceType);
448
 
97 soliveira 449
                setterDependencies.add(d);
20 soliveira 450
        }
2 soliveira 451
 
101 soliveira 452
        private void autowireBySetter(String targetProperty) {
20 soliveira 453
 
97 soliveira 454
                autowireBySetter(targetProperty, targetProperty);
20 soliveira 455
        }
97 soliveira 456
 
101 soliveira 457
        private void autowireByConstructor(String sourceFromContainer) {
97 soliveira 458
 
459
                Class<? extends Object> sourceType = getType(sourceFromContainer);
460
 
461
                ConstructorDependency d = new ConstructorDependency(sourceFromContainer, sourceType);
462
 
463
                constructorDependencies.add(d);
464
        }
465
 
466
        Set<ConstructorDependency> getConstructorDependencies() {
467
 
468
                return constructorDependencies;
469
        }
2 soliveira 470
 
84 soliveira 471
        @Override
40 soliveira 472
        public Container populate(Object bean) {
22 soliveira 473
 
474
                Provider p = new Provider() {
475
 
84 soliveira 476
                        @Override
22 soliveira 477
                        public Object get(String key) {
478
 
479
                                return MentaContainer.this.get(key);
480
                        }
481
 
84 soliveira 482
                        @Override
58 soliveira 483
                        public boolean hasValue(String key) {
22 soliveira 484
 
58 soliveira 485
                                return MentaContainer.this.check(key);
22 soliveira 486
                        }
487
 
488
                };
40 soliveira 489
 
490
                try {
2 soliveira 491
 
40 soliveira 492
                        InjectionUtils.getObject(bean, p, false, null, true, false, true);
493
 
494
                } catch(Exception e) {
495
 
496
                        throw new RuntimeException("Error populating bean: " + bean, e);
497
                }
2 soliveira 498
 
20 soliveira 499
                return this;
500
        }
2 soliveira 501
 
84 soliveira 502
        @Override
60 soliveira 503
        public synchronized boolean check(String key) {
58 soliveira 504
 
91 soliveira 505
                if (!factoriesByName.containsKey(key)) return false;
59 soliveira 506
 
58 soliveira 507
                Scope scope = scopes.get(key);
508
 
509
                if (scope == Scope.NONE) {
510
 
511
                        return false; // always...
512
 
513
                } else if (scope == Scope.SINGLETON) {
514
 
515
                        return singletonsCache.containsKey(key);
516
 
517
                } else if (scope == Scope.THREAD) {
518
 
519
                        ThreadLocal<Object> t = threadLocalsCache.get(key);
520
 
66 soliveira 521
                        if (t != null) return t.get() != null;
58 soliveira 522
 
66 soliveira 523
                        return false;
524
 
58 soliveira 525
                } else {
526
 
527
                        throw new UnsupportedOperationException("This scope is not supported: " + scope);
528
                }
20 soliveira 529
        }
71 soliveira 530
 
90 soliveira 531
        private static class ClearableHolder {
532
 
533
                private Interceptable<Object> c;
534
                private Object value;
71 soliveira 535
 
90 soliveira 536
                public ClearableHolder(Interceptable<Object> c, Object value) {
71 soliveira 537
                        this.c = c;
538
                        this.value = value;
539
                }
540
 
90 soliveira 541
                public void clear() {
542
                        c.onCleared(value);
543
                }
71 soliveira 544
 
545
        }
2 soliveira 546
}