MentaContainer

Rev

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