MentaContainer

Rev

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