MentaContainer

Rev

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