MentaContainer

Rev

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