001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.webbeans.component;
031:
032: import com.caucho.config.*;
033: import com.caucho.config.j2ee.*;
034: import com.caucho.config.program.ConfigProgram;
035: import com.caucho.config.types.*;
036: import com.caucho.ejb3.gen.*;
037: import com.caucho.util.*;
038: import com.caucho.webbeans.*;
039: import com.caucho.webbeans.bytecode.*;
040: import com.caucho.webbeans.cfg.*;
041: import com.caucho.webbeans.context.*;
042: import com.caucho.webbeans.event.*;
043: import com.caucho.webbeans.manager.*;
044:
045: import java.lang.reflect.*;
046: import java.lang.annotation.*;
047: import java.util.*;
048:
049: import javax.annotation.*;
050: import javax.webbeans.*;
051:
052: /**
053: * Configuration for the xml web bean component.
054: */
055: public class ClassComponent extends ComponentImpl {
056: private static final L10N L = new L10N(ClassComponent.class);
057:
058: private static final Object[] NULL_ARGS = new Object[0];
059:
060: private Class _cl;
061: private boolean _isBound;
062:
063: private Constructor _ctor;
064: private ComponentImpl[] _ctorArgs;
065:
066: private Object _scopeAdapter;
067:
068: private HashMap<Method, ArrayList<WbInterceptor>> _interceptorMap;
069: private Class _proxyClass;
070:
071: private String _mbeanName;
072: private Class _mbeanInterface;
073:
074: public ClassComponent(WbWebBeans webbeans) {
075: super (webbeans);
076: }
077:
078: public void setInstanceClass(Class cl) {
079: _cl = cl;
080:
081: if (getTargetType() == null)
082: setTargetType(cl);
083: }
084:
085: public Class getInstanceClass() {
086: return _cl;
087: }
088:
089: public void setConstructor(Constructor ctor) {
090: _ctor = ctor;
091: }
092:
093: public void setMBeanName(String name) {
094: _mbeanName = name;
095: }
096:
097: public Class getMBeanInterface() {
098: return _mbeanInterface;
099: }
100:
101: public void init() {
102: introspect();
103:
104: super .init();
105: }
106:
107: /**
108: * Called for implicit introspection.
109: */
110: public void introspect() {
111: Class cl = getInstanceClass();
112: Class scopeClass = null;
113:
114: if (getType() == null) {
115: for (Annotation ann : cl.getDeclaredAnnotations()) {
116: if (ann.annotationType().isAnnotationPresent(
117: ComponentType.class)) {
118: if (getType() != null)
119: throw new ConfigException(
120: L
121: .l(
122: "{0}: component type annotation @{1} conflicts with @{2}. WebBeans components may only have a single @ComponentType.",
123: cl.getName(), getType()
124: .getType()
125: .getName(),
126: ann.annotationType()
127: .getName()));
128:
129: setType(_webbeans.createComponentType(ann
130: .annotationType()));
131: }
132: }
133: }
134:
135: if (getType() == null)
136: setType(_webbeans.createComponentType(Component.class));
137:
138: if (getScope() == null) {
139: for (Annotation ann : cl.getDeclaredAnnotations()) {
140: if (ann.annotationType().isAnnotationPresent(
141: ScopeType.class)) {
142: if (scopeClass != null)
143: throw new ConfigException(
144: L
145: .l(
146: "{0}: @ScopeType annotation @{1} conflicts with @{2}. WebBeans components may only have a single @ScopeType.",
147: cl.getName(),
148: scopeClass.getName(),
149: ann.annotationType()
150: .getName()));
151:
152: scopeClass = ann.annotationType();
153: setScope(_webbeans.getScopeContext(scopeClass));
154: }
155: }
156: }
157:
158: if (getName() == null) {
159: String name = cl.getSimpleName();
160:
161: name = Character.toLowerCase(name.charAt(0))
162: + name.substring(1);
163:
164: setName(name);
165: }
166:
167: introspectProduces();
168: introspectConstructor();
169:
170: if (getBindingList().size() == 0)
171: introspectBindings();
172:
173: introspectMBean();
174: }
175:
176: /**
177: * Introspects the methods for any @Produces
178: */
179: private void introspectProduces() {
180: if (_cl == null)
181: return;
182:
183: for (Method method : _cl.getDeclaredMethods()) {
184: if (Modifier.isStatic(method.getModifiers()))
185: continue;
186:
187: if (!method.isAnnotationPresent(Produces.class))
188: continue;
189:
190: if (method.isAnnotationPresent(In.class))
191: throw error(
192: method,
193: L
194: .l("@Produces method may not have an @In annotation."));
195:
196: ProducesComponent comp = new ProducesComponent(_webbeans,
197: this , method);
198:
199: _webbeans.addWbComponent(comp);
200:
201: comp.init();
202: }
203: }
204:
205: /**
206: * Introspects the constructor
207: */
208: protected void introspectConstructor() {
209: if (_ctor != null)
210: return;
211:
212: try {
213: Constructor best = null;
214: Constructor second = null;
215:
216: for (Constructor ctor : _cl.getDeclaredConstructors()) {
217: if (best == null) {
218: best = ctor;
219: } else if (hasBindingAnnotation(ctor)) {
220: if (best != null && hasBindingAnnotation(best))
221: throw new ConfigException(
222: L
223: .l(
224: "WebBean {0} has two constructors with binding annotations.",
225: ctor
226: .getDeclaringClass()
227: .getName()));
228: best = ctor;
229: second = null;
230: } else if (ctor.getParameterTypes().length == 0) {
231: best = ctor;
232: } else if (best.getParameterTypes().length == 0) {
233: } else {
234: second = ctor;
235: }
236: }
237:
238: if (second != null)
239: throw new ConfigException(
240: L
241: .l(
242: "{0}: WebBean does not have a unique constructor. One constructor must be marked with @In or have a binding annotation.",
243: _cl.getName()));
244:
245: _ctor = best;
246: } catch (RuntimeException e) {
247: throw e;
248: } catch (Exception e) {
249: throw ConfigException.create(e);
250: }
251: }
252:
253: /**
254: * Introspects for MBeans annotation
255: */
256: private void introspectMBean() {
257: if (_cl == null)
258: return;
259: else if (_mbeanInterface != null)
260: return;
261:
262: for (Class iface : _cl.getInterfaces()) {
263: if (iface.getName().endsWith("MBean")
264: || iface.getName().endsWith("MXBean")) {
265: _mbeanInterface = iface;
266: return;
267: }
268: }
269: }
270:
271: protected String getMBeanName() {
272: if (_mbeanName != null)
273: return _mbeanName;
274:
275: if (_mbeanInterface == null)
276: return null;
277:
278: String typeName = _mbeanInterface.getSimpleName();
279:
280: if (typeName.endsWith("MXBean"))
281: typeName = typeName.substring(0, typeName.length()
282: - "MXBean".length());
283: else if (typeName.endsWith("MBean"))
284: typeName = typeName.substring(0, typeName.length()
285: - "MBean".length());
286:
287: String name = getName();
288:
289: if (name == null)
290: return "type=" + typeName;
291: else if (name.equals(""))
292: return "type=" + typeName + ",name=default";
293: else
294: return "type=" + typeName + ",name=" + name;
295: }
296:
297: /**
298: * Creates a new instance of the component.
299: */
300: @Override
301: public Object get(ConfigContext env) {
302: try {
303: Object value;
304: boolean isNew = false;
305:
306: if (env.canInject(_scope)) {
307: if (_scope != null) {
308: value = _scope.get(this , false);
309:
310: if (value != null)
311: return value;
312: } else {
313: value = env.get(this );
314:
315: if (value != null)
316: return value;
317: }
318:
319: value = createNew(env);
320:
321: if (_scope != null) {
322: _scope.put(this , value);
323: env = new ConfigContext(this , value, _scope);
324: } else
325: env.put(this , value);
326:
327: init(value, env);
328: } else {
329: if (env != null) {
330: value = env.get(this );
331: if (value != null)
332: return value;
333: }
334:
335: value = _scopeAdapter;
336: if (value == null) {
337: ScopeAdapter scopeAdapter = ScopeAdapter
338: .create(getInstanceClass());
339: _scopeAdapter = scopeAdapter.wrap(this );
340: value = _scopeAdapter;
341: }
342:
343: env.put(this , value);
344: }
345:
346: return value;
347: } catch (RuntimeException e) {
348: throw e;
349: } catch (Exception e) {
350: throw new RuntimeException(e);
351: }
352: }
353:
354: @Override
355: protected Object createNew(ConfigContext env) {
356: try {
357: if (!_isBound)
358: bind();
359:
360: Object[] args;
361: if (_ctorArgs.length > 0) {
362: args = new Object[_ctorArgs.length];
363:
364: for (int i = 0; i < args.length; i++)
365: args[i] = _ctorArgs[i].create();
366: } else
367: args = NULL_ARGS;
368:
369: Object value = _ctor.newInstance(args);
370:
371: if (isSingleton())
372: SerializationAdapter.setHandle(value, getHandle());
373:
374: if (env != null)
375: env.put(this , value);
376:
377: return value;
378: } catch (RuntimeException e) {
379: throw e;
380: } catch (Exception e) {
381: throw new RuntimeException(e);
382: }
383: }
384:
385: /**
386: * Binds parameters
387: */
388: public void bind() {
389: synchronized (this ) {
390: if (_isBound)
391: return;
392: _isBound = true;
393:
394: ArrayList<ConfigProgram> injectList = new ArrayList<ConfigProgram>();
395: InjectIntrospector.introspectInject(injectList, _cl);
396: _injectProgram = new ConfigProgram[injectList.size()];
397: injectList.toArray(_injectProgram);
398:
399: ArrayList<ConfigProgram> initList = new ArrayList<ConfigProgram>();
400: InjectIntrospector.introspectInit(initList, _cl);
401: _initProgram = new ConfigProgram[initList.size()];
402: initList.toArray(_initProgram);
403:
404: ArrayList<ConfigProgram> destroyList = new ArrayList<ConfigProgram>();
405: InjectIntrospector.introspectDestroy(destroyList, _cl);
406: _destroyProgram = new ConfigProgram[destroyList.size()];
407: destroyList.toArray(_destroyProgram);
408:
409: String loc = _ctor.getDeclaringClass().getName() + "(): ";
410: Type[] param = _ctor.getGenericParameterTypes();
411: Annotation[][] paramAnn = _ctor.getParameterAnnotations();
412:
413: _ctorArgs = new ComponentImpl[param.length];
414:
415: for (int i = 0; i < param.length; i++) {
416: _ctorArgs[i] = _webbeans.bindParameter(loc, param[i],
417: paramAnn[i]);
418:
419: if (_ctorArgs[i] == null)
420: throw new ConfigException(L.l(
421: "{0} does not have valid arguments", _ctor));
422: }
423:
424: introspectObservers();
425:
426: /*
427: introspectInterceptors();
428:
429: if (_interceptorMap != null) {
430: _proxyClass = InterceptorGenerator.gen(getInstanceClass(),
431: _ctor, _interceptorMap);
432:
433: Constructor proxyCtor = _proxyClass.getConstructors()[0];
434:
435: _ctor = proxyCtor;
436: }
437: */
438:
439: PojoBean bean = new PojoBean(_cl);
440: bean.setSingleton(isSingleton());
441: bean.introspect();
442:
443: Class instanceClass = bean.generateClass();
444:
445: if (instanceClass == _cl && isSingleton())
446: instanceClass = SerializationAdapter.gen(_cl);
447:
448: if (instanceClass != _cl) {
449: try {
450: _ctor = instanceClass.getConstructor(_ctor
451: .getParameterTypes());
452:
453: setInstanceClass(instanceClass);
454: } catch (Exception e) {
455: throw ConfigException.create(e);
456: }
457: }
458: }
459: }
460:
461: /**
462: * Introspects the methods for any @Produces
463: */
464: protected void introspectBindings() {
465: ArrayList<WbBinding> bindings = new ArrayList<WbBinding>();
466:
467: for (Annotation ann : getInstanceClass().getAnnotations()) {
468: if (ann.annotationType().isAnnotationPresent(
469: BindingType.class))
470: bindings.add(new WbBinding(ann));
471:
472: if (ann instanceof Named)
473: setName(((Named) ann).value());
474: }
475:
476: if (bindings.size() > 0)
477: setBindingList(bindings);
478: }
479:
480: /**
481: * Introspects any observers.
482: */
483: protected void introspectObservers() {
484: for (Method method : getInstanceClass().getDeclaredMethods()) {
485: int param = findObserverAnnotation(method);
486:
487: if (param < 0)
488: continue;
489:
490: if (method.isAnnotationPresent(In.class))
491: throw error(method,
492: "@Observer may not have an @In attribute");
493:
494: ArrayList<WbBinding> bindingList = new ArrayList<WbBinding>();
495:
496: Annotation[][] annList = method.getParameterAnnotations();
497: if (annList != null && annList[param] != null) {
498: for (Annotation ann : annList[param]) {
499: if (ann.annotationType().isAnnotationPresent(
500: EventBindingType.class))
501: bindingList.add(new WbBinding(ann));
502: }
503: }
504:
505: ObserverImpl observer = new ObserverImpl(this , method,
506: param);
507: observer.setBindingList(bindingList);
508:
509: _webbeans.getContainer().addObserver(observer);
510: }
511: }
512:
513: /**
514: * Introspects any intercepted methods
515: */
516: protected void introspectInterceptors() {
517: for (Method method : getInstanceClass().getMethods()) {
518: if (method.getDeclaringClass().equals(Object.class))
519: continue;
520:
521: ArrayList<Annotation> interceptorTypes = findInterceptorTypes(method);
522:
523: if (interceptorTypes == null)
524: continue;
525:
526: ArrayList<WbInterceptor> interceptors = _webbeans
527: .findInterceptors(interceptorTypes);
528:
529: if (interceptors != null) {
530: if (_interceptorMap == null)
531: _interceptorMap = new HashMap<Method, ArrayList<WbInterceptor>>();
532:
533: _interceptorMap.put(method, interceptors);
534: }
535: }
536: }
537:
538: private ArrayList<Annotation> findInterceptorTypes(Method method) {
539: ArrayList<Annotation> types = null;
540:
541: for (Annotation ann : method.getAnnotations()) {
542: if (ann.annotationType().isAnnotationPresent(
543: InterceptorBindingType.class)) {
544: if (types == null)
545: types = new ArrayList<Annotation>();
546:
547: types.add(ann);
548: }
549: }
550:
551: return types;
552: }
553:
554: private boolean hasBindingAnnotation(Constructor ctor) {
555: if (ctor.isAnnotationPresent(In.class))
556: return true;
557:
558: Annotation[][] paramAnn = ctor.getParameterAnnotations();
559:
560: for (Annotation[] annotations : paramAnn) {
561: for (Annotation ann : annotations) {
562: if (ann.annotationType().isAnnotationPresent(
563: BindingType.class))
564: return true;
565: }
566: }
567:
568: return false;
569: }
570:
571: private int findObserverAnnotation(Method method) {
572: Annotation[][] paramAnn = method.getParameterAnnotations();
573: int observer = -1;
574:
575: for (int i = 0; i < paramAnn.length; i++) {
576: for (Annotation ann : paramAnn[i]) {
577: if (ann instanceof Observes) {
578: if (observer >= 0)
579: throw WebBeansContainer
580: .error(
581: method,
582: L
583: .l("Only one param may have an @Observer"));
584:
585: observer = i;
586: }
587: }
588: }
589:
590: return observer;
591: }
592: }
|