Source Code Cross Referenced for PetiteContainer.java in  » Development » jodd » jodd » petite » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Development » jodd » jodd.petite 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        // Copyright (c) 2003-2007, Jodd Team (jodd.sf.net). All Rights Reserved.
002:
003:        package jodd.petite;
004:
005:        import jodd.petite.scope.Scope;
006:        import jodd.petite.scope.SingletonScope;
007:        import jodd.petite.scope.DefaultScope;
008:        import jodd.petite.scope.ScopeReplacer;
009:        import jodd.petite.meta.PetiteBean;
010:        import jodd.petite.meta.PetiteInject;
011:        import jodd.util.StringUtil;
012:        import jodd.introspector.DefaultIntrospector;
013:        import jodd.introspector.ClassDescriptor;
014:        import jodd.bean.BeanUtil;
015:
016:        import java.util.ArrayList;
017:        import java.util.HashMap;
018:        import java.util.Iterator;
019:        import java.util.List;
020:        import java.util.Map;
021:        import java.lang.reflect.Field;
022:        import java.lang.reflect.Constructor;
023:        import java.lang.annotation.Annotation;
024:
025:        /**
026:         * Petite Container.
027:         */
028:        public class PetiteContainer {
029:
030:            // ---------------------------------------------------------------- get beans
031:
032:            /**
033:             * Returns Petite bean instance.
034:             * Parameter is name of registered petite bean. Bean can be registered with
035:             * explicit name using {@link PetiteContainer#register(String, Class, Class)}
036:             * or by named generated from its class {@link PetiteContainer#register(Class, Class)}
037:             * (from optional annotation or simple type name).
038:             * <p>
039:             * Petite container will find the bean in corresponding scope and all its dependecies,
040:             * either by constructor or property injection. When using constructor injection, cyclic dependecies
041:             * can not be prevented, but they are at least detected.
042:             *
043:             * @see PetiteContainer#create(Class) 
044:             */
045:            public Object getBean(String name) {
046:                return getBean(name, new HashMap<String, Object>());
047:            }
048:
049:            /**
050:             * Returns petite bean instance.
051:             */
052:            protected Object getBean(String name,
053:                    Map<String, Object> acquiredBeans) {
054:
055:                // First check if bean is already acquired within this call.
056:                // This prevents cyclic dependencies problem. It is expected than single
057:                // object tree path contains less elements, therefore, this search is faster
058:                // then the next one.
059:                Object bean = acquiredBeans.get(name);
060:                if (bean != null) {
061:                    if (bean == Void.TYPE) {
062:                        throw new PetiteException(
063:                                "Cycle dependecies on constructor injection detected!");
064:                    }
065:                    return bean;
066:                }
067:
068:                // Lookup for registered bean definition.
069:                BeanDef def = beanNames.get(name);
070:                if (def == null) {
071:                    return null;
072:                }
073:
074:                // Find the bean in its scope
075:                bean = def.scopeLookup();
076:                if (bean == null) {
077:                    // Create a new bean in the scope
078:                    bean = newBeanInstance(def.name, def.type, acquiredBeans);
079:                    wire(bean, acquiredBeans);
080:                    def.scopeRegister(bean);
081:                }
082:                return bean;
083:
084:            }
085:
086:            // ---------------------------------------------------------------- create
087:
088:            /**
089:             * Creates a new intance of specified type and wires it with the container.
090:             * <p>
091:             * If type is actually a petite bean (has annotation), {@link #getBean(String)} will be invoked.
092:             * <p>
093:             * Otherwise, a new instance will be created and then wired with container beans.
094:             * Since in this case is not possible to check if specified type is registered in container,
095:             * newly created instance is <b>not</b> present in the petite scopes!
096:             * That is important for cyclic dependencies cases, where the same type is acquired
097:             * later in the bean-graph.
098:             */
099:            @SuppressWarnings({"unchecked"})
100:            public Object create(Class type) {
101:                PetiteBean ann = (PetiteBean) type
102:                        .getAnnotation(PetiteBean.class);
103:                if (ann != null) {
104:                    return getBean(ann.value().trim());
105:                }
106:                Map<String, Object> acquiredBeans = new HashMap<String, Object>();
107:                Object bean = newBeanInstance(null, type, acquiredBeans);
108:                wire(bean, acquiredBeans);
109:                return bean;
110:            }
111:
112:            // ---------------------------------------------------------------- new instance
113:
114:            /**
115:             * Cache for constructors per type.
116:             */
117:            protected Map<Class, Object[]> beanCtors = new HashMap<Class, Object[]>();
118:
119:            /**
120:             * Creates new petite bean or type instance and performs constructor injection.
121:             * This method may be called from {@link #create(Class)} and {@link #getBean(String)}, therefore
122:             * the frist argument (bean name) also indicates if the bean is inside the container (when is not null).
123:             * <p>
124:             * After creating a bean, it will be added to acquired beans (if it is from the container).
125:             *
126:             * @param name  optional bean name, if set then it is a name under which petite bean was registered
127:             * @param type  petite bean type
128:             * @param acquiredBeans     acquired beans until this moment
129:             */
130:            protected Object newBeanInstance(String name, Class type,
131:                    Map<String, Object> acquiredBeans) {
132:                Object[] ctorData = beanCtors.get(type);
133:                if (ctorData == null) {
134:                    ClassDescriptor cd = DefaultIntrospector.lookup(type);
135:                    Constructor[] allCtors = cd.getAllCtors(true);
136:                    boolean founded = false;
137:                    List<Annotation> annotations = new ArrayList<Annotation>();
138:                    nextCtor: for (Constructor actor : allCtors) {
139:                        annotations.clear();
140:                        Annotation[][] paramAnnotations = actor
141:                                .getParameterAnnotations();
142:                        for (Annotation[] anns : paramAnnotations) {
143:                            boolean has = false;
144:                            for (Annotation a : anns) {
145:                                if (a.annotationType() == PetiteInject.class) {
146:                                    annotations.add(a);
147:                                    has = true;
148:                                    break;
149:                                }
150:                            }
151:                            if (has == false) {
152:                                continue nextCtor;
153:                            }
154:                        }
155:                        // suitable constructor found
156:                        Annotation[] beanRefAnnotations = annotations
157:                                .toArray(new Annotation[annotations.size()]);
158:                        ctorData = new Object[] { actor,
159:                                actor.getParameterTypes(), beanRefAnnotations };
160:                        Object[] previous = beanCtors.get(type);
161:                        boolean addOk = false;
162:                        if (previous == null) {
163:                            addOk = true;
164:                        } else {
165:                            if (((Class[]) previous[1]).length == 0) { // previous was default ctor
166:                                addOk = true;
167:                            } else if (((Class[]) ctorData[1]).length == 0) { // current is a default ctor, ignore
168:                                ctorData = previous;
169:                                continue;
170:                            }
171:                        }
172:                        if (addOk == true) {
173:                            beanCtors.put(type, ctorData);
174:                        } else {
175:                            throw new PetiteException(
176:                                    "Type '"
177:                                            + type.getName()
178:                                            + "' contains more than one suitable constructor.");
179:                        }
180:                        founded = true;
181:                    }
182:                    if (founded == false) {
183:                        throw new PetiteException("Type '" + type.getName()
184:                                + "' has no default or suitable constructor.");
185:                    }
186:                }
187:
188:                Class[] args = (Class[]) ctorData[1];
189:                // default ctors
190:                if (args.length == 0) {
191:                    Object bean;
192:                    try {
193:                        bean = type.newInstance();
194:                    } catch (Exception ex) {
195:                        throw new PetiteException(
196:                                "Unable to create new bean instance '" + type
197:                                        + "' using default constructor.", ex);
198:                    }
199:                    if (name != null) {
200:                        acquiredBeans.put(name, bean);
201:                    }
202:                    return bean;
203:                }
204:
205:                // other ctors
206:                if (name != null) {
207:                    acquiredBeans.put(name, Void.TYPE); // puts a dummy marker for cyclic dependency check
208:                }
209:                Constructor c = (Constructor) ctorData[0];
210:                Object[] argObjs = new Object[args.length];
211:                for (int i = 0; i < args.length; i++) {
212:                    Class arg = args[i];
213:                    PetiteInject pbr = (PetiteInject) ((Annotation[]) ctorData[2])[i];
214:                    String beanRef = pbr.value();
215:                    if (beanRef.length() == 0) {
216:                        beanRef = StringUtil.uncapitalize(arg.getSimpleName());
217:                    }
218:                    argObjs[i] = getBean(beanRef, acquiredBeans);
219:                    if (argObjs[i] == null) {
220:                        throw new PetiteException("Reference '" + beanRef
221:                                + "' not found for constructor '" + c + "'.");
222:                    }
223:                }
224:                Object bean;
225:                try {
226:                    bean = c.newInstance(argObjs);
227:                } catch (Exception ex) {
228:                    throw new PetiteException(
229:                            "Unable to create new bean instance '" + type
230:                                    + "' using constructor: '" + c + "'.", ex);
231:                }
232:                if (name != null) {
233:                    acquiredBeans.put(name, bean);
234:                }
235:                return bean;
236:            }
237:
238:            // ---------------------------------------------------------------- wire
239:
240:            protected boolean trackBeanWiring;
241:
242:            public boolean isTrackBeanWiring() {
243:                return trackBeanWiring;
244:            }
245:
246:            /**
247:             * Stores additional information about targets where bean was wired in.
248:             * This will lower performance and raise memory usage. This data are needed
249:             * for run-time bean replacing.
250:             */
251:            public void setTrackBeanWiring(boolean trackBeanWiring) {
252:                this .trackBeanWiring = trackBeanWiring;
253:            }
254:
255:            /**
256:             * Cache of reference (i.e. annotated) fields for one type.
257:             */
258:            protected Map<Class, Field[]> beanReferences = new HashMap<Class, Field[]>();
259:
260:            /**
261:             * Wires beans by injecting instances in properties marked with {@link PetiteInject} annotation.
262:             * @see #wire(Object, java.util.Map) 
263:             */
264:            public void wire(Object bean) {
265:                wire(bean, new HashMap<String, Object>());
266:            }
267:
268:            /**
269:             * Wires beans by injecting instances in properties marked with {@link PetiteInject} annotation.
270:             */
271:            protected void wire(Object bean, Map<String, Object> acquiredBeans) {
272:                Class type = bean.getClass();
273:                Field[] fields = beanReferences.get(type);
274:                if (fields == null) {
275:                    ClassDescriptor cd = DefaultIntrospector.lookup(type);
276:                    ArrayList<Field> list = new ArrayList<Field>();
277:                    Field[] allFields = cd.getAllFields(true);
278:                    for (Field field : allFields) {
279:                        PetiteInject ref = field
280:                                .getAnnotation(PetiteInject.class);
281:                        if (ref == null) {
282:                            continue;
283:                        }
284:                        list.add(field);
285:                    }
286:                    fields = list.toArray(new Field[list.size()]);
287:                    beanReferences.put(type, fields);
288:                }
289:
290:                for (Field field : fields) {
291:                    PetiteInject ref = field.getAnnotation(PetiteInject.class);
292:                    String refName = ref.value().trim();
293:                    if (refName.length() == 0) {
294:                        refName = field.getName();
295:                    }
296:                    Object value = getBean(refName, acquiredBeans);
297:                    if (value == null) {
298:                        throw new PetiteException("Reference '" + refName
299:                                + "' not found for property '"
300:                                + type.toString().substring(6) + '#'
301:                                + field.getName() + "'.");
302:                    }
303:                    if (trackBeanWiring == true) { // track wirings, slow
304:                        BeanDef bd = beanNames.get(refName);
305:                        bd.injectedTo(resolveBeanName(type), field.getName());
306:                    }
307:                    BeanUtil.setDeclaredProperty(bean, field.getName(), value);
308:                }
309:            }
310:
311:            // ---------------------------------------------------------------- scopes
312:
313:            protected Class<? extends Scope> defaultScope = SingletonScope.class;
314:
315:            /**
316:             * Returns default scope type.
317:             */
318:            public Class<? extends Scope> getDefaultScope() {
319:                return defaultScope;
320:            }
321:
322:            /**
323:             * Sets default scope type.
324:             */
325:            public void setDefaultScope(Class<? extends Scope> defaultScope) {
326:                if (defaultScope == DefaultScope.class) {
327:                    throw new PetiteException(
328:                            "Default Petite bean scope must be a concrete scope implementation.");
329:                }
330:                this .defaultScope = defaultScope;
331:            }
332:
333:            // ---------------------------------------------------------------- registration
334:
335:            /**
336:             * Map of all beans definitions.
337:             */
338:            protected Map<String, BeanDef> beanNames = new HashMap<String, BeanDef>();
339:
340:            /**
341:             * Map of all bean scopes.
342:             */
343:            protected Map<Class<? extends Scope>, Scope> scopes = new HashMap<Class<? extends Scope>, Scope>();
344:
345:            /**
346:             * Registers petite bean class. If class is not annotated, it will be registered with default scope.
347:             * @see PetiteContainer#register(Class, Class)
348:             * @see PetiteContainer#register(String, Class, Class) 
349:             */
350:            public void register(Class type) {
351:                register(type, defaultScope);
352:            }
353:
354:            /**
355:             * Registers petite bean class within specified scope. Class may be annotated, but this is not required.
356:             * If annotation is omitted, bean will be registered with uncapitalized simple type name and defaults.
357:             * @see PetiteContainer#register(String, Class, Class)
358:             */
359:            @SuppressWarnings({"unchecked"})
360:            public void register(Class type, Class<? extends Scope> scopeType) {
361:                PetiteBean petiteBean = (PetiteBean) type
362:                        .getAnnotation(PetiteBean.class);
363:                String name = null;
364:                if (petiteBean != null) {
365:                    name = petiteBean.value().trim();
366:                    scopeType = petiteBean.scope();
367:                }
368:                if ((name == null) || (name.length() == 0)) {
369:                    name = StringUtil.uncapitalize(type.getSimpleName());
370:                }
371:                register(name, type, scopeType);
372:            }
373:
374:            /**
375:             * Registers any class as petite bean class with default scope.
376:             * @see PetiteContainer#register(String, Class, Class) 
377:             */
378:            public void register(String name, Class type) {
379:                register(name, type, defaultScope);
380:            }
381:
382:            /**
383:             * Registers any class as petite bean class. {@link PetiteBean} annotation is not required
384:             * and is ignored during this registration. Bean names must be unique. This is the actual method
385:             * that performs the registration.
386:             */
387:            public void register(String name, Class type,
388:                    Class<? extends Scope> scopeType) {
389:                if (scopeType == DefaultScope.class) {
390:                    scopeType = defaultScope;
391:                }
392:                Scope scope = scopes.get(scopeType);
393:                if (scope == null) {
394:                    try {
395:                        scope = scopeType.newInstance();
396:                        scopes.put(scopeType, scope);
397:                    } catch (Exception ex) {
398:                        throw new PetiteException(
399:                                "Unable to create the Petite scope: '"
400:                                        + scopeType + "' for bean '" + name
401:                                        + "'.", ex);
402:                    }
403:                }
404:                BeanDef existing = beanNames.put(name, new BeanDef(name, type,
405:                        scope));
406:                if (existing != null) {
407:                    throw new PetiteException("Unable to register class '"
408:                            + type.getSimpleName() + "'. Petite bean class '"
409:                            + existing.type.getSimpleName()
410:                            + "' already registered with the name '" + name
411:                            + "'.");
412:                }
413:            }
414:
415:            // ---------------------------------------------------------------- remove
416:
417:            /**
418:             * Removes bean definition from the container.
419:             * Returns <code>true</code> if bean specified by provided name
420:             * was removed from the container, otherwise it returns <code>false</code>.
421:             * @see PetiteContainer#remove(Class)
422:             */
423:            public boolean remove(String name) {
424:                BeanDef bd = beanNames.remove(name);
425:                if (bd == null) {
426:                    return false;
427:                }
428:                beanCtors.remove(bd.type);
429:                beanReferences.remove(bd.type);
430:                bd.scopeRemove();
431:                return true;
432:            }
433:
434:            /**
435:             * Removes petite bean from the container. Returns <code>true</code> if bean specified by provided name
436:             * was removed from the container, otherwise it returns <code>false</code>.
437:             * @see PetiteContainer#remove(String) 
438:             */
439:            public boolean remove(Class type) {
440:                String name = resolveBeanName(type);
441:                return remove(name);
442:            }
443:
444:            /**
445:             * Resolves bean name from bean annotation or class name.
446:             */
447:            @SuppressWarnings({"unchecked"})
448:            protected String resolveBeanName(Class type) {
449:                PetiteBean petiteBean = (PetiteBean) type
450:                        .getAnnotation(PetiteBean.class);
451:                String name = null;
452:                if (petiteBean != null) {
453:                    name = petiteBean.value().trim();
454:                }
455:                if ((name == null) || (name.length() == 0)) {
456:                    name = StringUtil.uncapitalize(type.getSimpleName());
457:                }
458:                return name;
459:            }
460:
461:            // ---------------------------------------------------------------- replace class
462:
463:            /**
464:             * Replaces a class in the container. It will find all targets where original class
465:             * has been injected and will replace existing instances, while storing them in
466:             * appropriate scopes. 
467:             */
468:            public void replace(final Class newType) {
469:                final String newName = resolveBeanName(newType);
470:                final BeanDef bd = beanNames.get(newName);
471:                if (bd == null) {
472:                    register(newType); // new types are simply registered
473:                    return;
474:                }
475:                beanCtors.remove(bd.type);
476:                beanReferences.remove(bd.type);
477:                bd.scopeRemove();
478:                bd.type = newType;
479:
480:                if (bd.injectectionTargets == null) {
481:                    return;
482:                }
483:                for (final BeanDef.InjectionTarget ref : bd.injectectionTargets) {
484:                    for (Scope scope : scopes.values()) {
485:                        scope.replaceIn(ref.beanName, new ScopeReplacer() {
486:                            public Object replace(Object target) {
487:                                Object oldObj = BeanUtil.getDeclaredProperty(
488:                                        target, ref.fieldName);
489:                                Map<String, Object> acquiredBeans = new HashMap<String, Object>();
490:                                Object newObj = newBeanInstance(bd.name,
491:                                        bd.type, acquiredBeans);
492:                                // copy fields since it is not possible to wire bean to all scopes, since
493:                                // maybe some scope depends on current thread, request, etc.
494:                                BeanUtil.copyFields(oldObj, newObj, true);
495:                                BeanUtil.setProperty(target, ref.fieldName,
496:                                        newObj);
497:                                return newObj;
498:                            }
499:
500:                            public String getBeanName() {
501:                                return newName;
502:                            }
503:                        });
504:                    }
505:                }
506:            }
507:
508:            // ---------------------------------------------------------------- stats and misc
509:
510:            /**
511:             * Returns total number of registered beans.
512:             */
513:            public int getTotalBeans() {
514:                return beanNames.size();
515:            }
516:
517:            /**
518:             * Return total number of used scopes.
519:             */
520:            public int getTotalScopes() {
521:                return scopes.size();
522:            }
523:
524:            /**
525:             * Returns iterator over all registered classes.
526:             */
527:            public Iterator<Class> classIterator() {
528:                final Iterator<BeanDef> it = beanNames.values().iterator();
529:                return new Iterator<Class>() {
530:                    public boolean hasNext() {
531:                        return it.hasNext();
532:                    }
533:
534:                    public Class next() {
535:                        return it.next().type;
536:                    }
537:
538:                    public void remove() {
539:                        it.remove();
540:                    }
541:                };
542:            }
543:
544:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.