Rev 119 | Rev 135 | 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; |
106 | soliveira | 15 | import org.mentacontainer.Interceptor; |
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 | |
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 |
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 | |||
106 | soliveira | 62 | Factory factory = factoriesByName.get(key); |
71 | soliveira | 63 | |
106 | soliveira | 64 | if (factory instanceof Interceptor) { |
71 | soliveira | 65 | |
106 | soliveira | 66 | Interceptor c = (Interceptor) factory; |
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 | |||
106 | soliveira | 89 | Factory factory = factoriesByName.get(key); |
71 | soliveira | 90 | |
106 | soliveira | 91 | if (factory instanceof Interceptor) { |
71 | soliveira | 92 | |
106 | soliveira | 93 | Interceptor c = (Interceptor) factory; |
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 | |||
120 | soliveira | 105 | t.remove(); |
71 | soliveira | 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 | |||
106 | soliveira | 134 | Factory factory = factoriesByName.get(key); |
71 | soliveira | 135 | |
106 | soliveira | 136 | if (factory instanceof Interceptor) { |
71 | soliveira | 137 | |
106 | soliveira | 138 | Interceptor c = (Interceptor) factory; |
71 | soliveira | 139 | |
90 | soliveira | 140 | cp = new ClearableHolder(c, value); |
71 | soliveira | 141 | } |
142 | } |
||
64 | soliveira | 143 | } |
144 | |||
110 | soliveira | 145 | if (cp != null) cp.c.onCleared(cp.value); |
71 | soliveira | 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 | |||
106 | soliveira | 165 | Factory factory = factoriesByName.get(key); |
71 | soliveira | 166 | |
106 | soliveira | 167 | if (factory instanceof Interceptor) { |
71 | soliveira | 168 | |
106 | soliveira | 169 | Interceptor c = (Interceptor) factory; |
71 | soliveira | 170 | |
90 | soliveira | 171 | cp = new ClearableHolder(c, o); |
71 | soliveira | 172 | } |
173 | |||
120 | soliveira | 174 | t.remove(); |
71 | soliveira | 175 | |
176 | retVal = o; |
||
177 | } |
||
178 | } |
||
58 | soliveira | 179 | } |
180 | |||
110 | soliveira | 181 | if (cp != null) cp.c.onCleared(cp.value); |
71 | soliveira | 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 | |||
106 | soliveira | 356 | if (f instanceof Interceptor) { |
90 | soliveira | 357 | |
106 | soliveira | 358 | Interceptor i = (Interceptor) f; |
90 | soliveira | 359 | |
106 | soliveira | 360 | ((Interceptor) f).onCreated(value); |
90 | soliveira | 361 | } |
362 | } |
||
91 | soliveira | 363 | |
84 | soliveira | 364 | @Override |
91 | soliveira | 365 | public Factory ioc(String key, Factory factory, Scope scope) { |
20 | soliveira | 366 | |
91 | soliveira | 367 | factoriesByName.put(key, factory); |
44 | soliveira | 368 | |
51 | soliveira | 369 | singletonsCache.remove(key); // just in case we are overriding a previous singleton bean... |
370 | |||
120 | soliveira | 371 | ThreadLocal<Object> threadLocal = threadLocalsCache.remove(key); // just in case we are overriding a previous thread local... |
372 | if (threadLocal != null) { |
||
373 | threadLocal.remove(); |
||
374 | } |
||
51 | soliveira | 375 | |
57 | soliveira | 376 | scopes.put(key, scope); |
377 | |||
102 | soliveira | 378 | forConstructMethod.add(new ConstructorDependency(key, factory.getType())); |
379 | |||
91 | soliveira | 380 | return factory; |
20 | soliveira | 381 | } |
382 | |||
84 | soliveira | 383 | @Override |
95 | soliveira | 384 | public Factory ioc(String key, Factory factory) { |
51 | soliveira | 385 | |
95 | soliveira | 386 | return ioc(key, factory, Scope.NONE); |
51 | soliveira | 387 | } |
388 | |||
84 | soliveira | 389 | @Override |
88 | soliveira | 390 | public ConfigurableFactory ioc(String key, Class<? extends Object> klass) { |
20 | soliveira | 391 | |
91 | soliveira | 392 | ConfigurableFactory cc = new ClassFactory(this, klass); |
45 | soliveira | 393 | |
394 | ioc(key, cc); |
||
395 | |||
396 | return cc; |
||
20 | soliveira | 397 | } |
398 | |||
84 | soliveira | 399 | @Override |
88 | soliveira | 400 | public ConfigurableFactory ioc(String key, Class<? extends Object> klass, Scope scope) { |
20 | soliveira | 401 | |
91 | soliveira | 402 | ConfigurableFactory cc = new ClassFactory(this, klass); |
45 | soliveira | 403 | |
57 | soliveira | 404 | ioc(key, cc, scope); |
45 | soliveira | 405 | |
406 | return cc; |
||
20 | soliveira | 407 | } |
101 | soliveira | 408 | |
84 | soliveira | 409 | @Override |
101 | soliveira | 410 | public void autowire(String sourceFromContainer) { |
94 | soliveira | 411 | |
101 | soliveira | 412 | // autowire by constructor and setter... |
413 | |||
414 | autowireBySetter(sourceFromContainer); |
||
415 | |||
416 | autowireByConstructor(sourceFromContainer); |
||
417 | } |
||
418 | |||
419 | @Override |
||
420 | public void autowire(String sourceFromContainer, String beanProperty) { |
||
421 | |||
119 | soliveira | 422 | // autowire by constructor and setter... |
101 | soliveira | 423 | |
424 | autowireBySetter(beanProperty, sourceFromContainer); |
||
119 | soliveira | 425 | |
426 | autowireByConstructor(sourceFromContainer); |
||
101 | soliveira | 427 | } |
428 | |||
429 | private void autowireBySetter(String targetProperty, String sourceFromContainer) { |
||
430 | |||
94 | soliveira | 431 | Class<? extends Object> sourceType = getType(sourceFromContainer); |
4 | soliveira | 432 | |
94 | soliveira | 433 | SetterDependency d = new SetterDependency(targetProperty, sourceFromContainer, sourceType); |
434 | |||
97 | soliveira | 435 | setterDependencies.add(d); |
20 | soliveira | 436 | } |
2 | soliveira | 437 | |
101 | soliveira | 438 | private void autowireBySetter(String targetProperty) { |
20 | soliveira | 439 | |
97 | soliveira | 440 | autowireBySetter(targetProperty, targetProperty); |
20 | soliveira | 441 | } |
97 | soliveira | 442 | |
101 | soliveira | 443 | private void autowireByConstructor(String sourceFromContainer) { |
97 | soliveira | 444 | |
445 | Class<? extends Object> sourceType = getType(sourceFromContainer); |
||
446 | |||
447 | ConstructorDependency d = new ConstructorDependency(sourceFromContainer, sourceType); |
||
448 | |||
449 | constructorDependencies.add(d); |
||
450 | } |
||
451 | |||
452 | Set<ConstructorDependency> getConstructorDependencies() { |
||
453 | |||
454 | return constructorDependencies; |
||
455 | } |
||
102 | soliveira | 456 | |
457 | @Override |
||
458 | public <T> T construct(Class<? extends Object> klass) { |
||
459 | |||
460 | ClassFactory f = new ClassFactory(this, klass, forConstructMethod); |
||
461 | |||
462 | return (T) f.getInstance(); |
||
463 | } |
||
2 | soliveira | 464 | |
84 | soliveira | 465 | @Override |
103 | soliveira | 466 | public void inject(Object bean) { |
22 | soliveira | 467 | |
468 | Provider p = new Provider() { |
||
469 | |||
84 | soliveira | 470 | @Override |
22 | soliveira | 471 | public Object get(String key) { |
472 | |||
473 | return MentaContainer.this.get(key); |
||
474 | } |
||
475 | |||
84 | soliveira | 476 | @Override |
58 | soliveira | 477 | public boolean hasValue(String key) { |
22 | soliveira | 478 | |
58 | soliveira | 479 | return MentaContainer.this.check(key); |
22 | soliveira | 480 | } |
481 | |||
482 | }; |
||
40 | soliveira | 483 | |
484 | try { |
||
2 | soliveira | 485 | |
40 | soliveira | 486 | InjectionUtils.getObject(bean, p, false, null, true, false, true); |
487 | |||
488 | } catch(Exception e) { |
||
489 | |||
490 | throw new RuntimeException("Error populating bean: " + bean, e); |
||
491 | } |
||
20 | soliveira | 492 | } |
2 | soliveira | 493 | |
84 | soliveira | 494 | @Override |
60 | soliveira | 495 | public synchronized boolean check(String key) { |
58 | soliveira | 496 | |
91 | soliveira | 497 | if (!factoriesByName.containsKey(key)) return false; |
59 | soliveira | 498 | |
58 | soliveira | 499 | Scope scope = scopes.get(key); |
500 | |||
501 | if (scope == Scope.NONE) { |
||
502 | |||
503 | return false; // always... |
||
504 | |||
505 | } else if (scope == Scope.SINGLETON) { |
||
506 | |||
507 | return singletonsCache.containsKey(key); |
||
508 | |||
509 | } else if (scope == Scope.THREAD) { |
||
510 | |||
511 | ThreadLocal<Object> t = threadLocalsCache.get(key); |
||
512 | |||
66 | soliveira | 513 | if (t != null) return t.get() != null; |
58 | soliveira | 514 | |
66 | soliveira | 515 | return false; |
516 | |||
58 | soliveira | 517 | } else { |
518 | |||
519 | throw new UnsupportedOperationException("This scope is not supported: " + scope); |
||
520 | } |
||
20 | soliveira | 521 | } |
71 | soliveira | 522 | |
90 | soliveira | 523 | private static class ClearableHolder { |
524 | |||
106 | soliveira | 525 | private Interceptor c; |
90 | soliveira | 526 | private Object value; |
71 | soliveira | 527 | |
106 | soliveira | 528 | public ClearableHolder(Interceptor c, Object value) { |
71 | soliveira | 529 | this.c = c; |
530 | this.value = value; |
||
531 | } |
||
532 | |||
90 | soliveira | 533 | public void clear() { |
110 | soliveira | 534 | c.onCleared(value); |
90 | soliveira | 535 | } |
71 | soliveira | 536 | |
537 | } |
||
2 | soliveira | 538 | } |