MentaContainer

Rev

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