MentaContainer

Rev

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