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 | } |