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