KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > config > BeanTypeStrategy


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.config;
31
32 import com.caucho.config.j2ee.InjectIntrospector;
33 import com.caucho.config.types.Validator;
34 import com.caucho.loader.Environment;
35 import com.caucho.loader.EnvironmentListener;
36 import com.caucho.loader.WeakDestroyListener;
37 import com.caucho.util.CharBuffer;
38 import com.caucho.util.L10N;
39 import com.caucho.util.NotImplementedException;
40 import com.caucho.vfs.Depend;
41 import com.caucho.vfs.PersistentDependency;
42 import com.caucho.xml.CauchoNode;
43 import com.caucho.xml.QAbstractNode;
44 import com.caucho.xml.QName;
45
46 import org.w3c.dom.Node JavaDoc;
47
48 import javax.annotation.PostConstruct;
49 import javax.annotation.PreDestroy;
50 import javax.el.ELContext;
51 import java.lang.ref.SoftReference JavaDoc;
52 import java.lang.reflect.InvocationTargetException JavaDoc;
53 import java.lang.reflect.Method JavaDoc;
54 import java.lang.reflect.Modifier JavaDoc;
55 import java.util.ArrayList JavaDoc;
56 import java.util.HashMap JavaDoc;
57
58 public class BeanTypeStrategy extends TypeStrategy {
59   protected static final L10N L = new L10N(BeanTypeStrategy.class);
60
61   private static final QName TEXT = new QName("text");
62   private static final QName VALUE = new QName("value");
63
64   private SoftReference JavaDoc<Method JavaDoc []> _methodsRef;
65   private HashMap JavaDoc<QName,AttributeStrategy> _nsAttributeMap
66     = new HashMap JavaDoc<QName,AttributeStrategy>();
67   private final Class JavaDoc _type;
68
69   private ArrayList JavaDoc<BuilderProgram> _injectList
70     = new ArrayList JavaDoc<BuilderProgram>();
71
72   private final Method JavaDoc _setParent;
73   private final Method JavaDoc _setLocation;
74   private final Method JavaDoc _addDependency;
75   private final Method JavaDoc _setSystemId;
76   private final Method JavaDoc _setNode;
77   private final ArrayList JavaDoc<Method JavaDoc> _initList = new ArrayList JavaDoc<Method JavaDoc>();
78   private final ArrayList JavaDoc<Method JavaDoc> _destroyList = new ArrayList JavaDoc<Method JavaDoc>();
79   private final Method JavaDoc _replaceObject;
80
81   BeanTypeStrategy(Class JavaDoc type)
82   {
83     _type = type;
84
85     Method JavaDoc setParent = null;
86
87     _injectList = InjectIntrospector.introspect(type);
88
89     setParent = findMethod("setParent", new Class JavaDoc[] { null });
90
91     _setParent = setParent;
92
93     scanMethods(type);
94
95     /*
96     Method init = findMethod("init", new Class[0]);
97     if (init != null)
98       _initList.add(init);
99     */

100
101     _replaceObject = findMethod("replaceObject", new Class JavaDoc[0]);
102
103     _setLocation = findMethod("setConfigLocation",
104                               new Class JavaDoc[] { String JavaDoc.class, int.class });
105
106     _addDependency = findMethod("addDependency",
107                                 new Class JavaDoc[] { PersistentDependency.class });
108
109     _setSystemId = findMethod("setConfigSystemId",
110                               new Class JavaDoc[] { String JavaDoc.class });
111
112     /*
113     _setSystemId = findMethod("setConfigSystemId",
114                               new Class[0] { String.class });
115     */

116
117     _setNode = findMethod("setConfigNode", new Class JavaDoc[] { Node.class });
118   }
119
120   /**
121    * Returns the type class.
122    */

123   public Class JavaDoc getType()
124   {
125     return _type;
126   }
127
128   /**
129    * Returns the type name.
130    */

131   public String JavaDoc getTypeName()
132   {
133     return getType().getName();
134   }
135
136   /**
137    * Creates an instance of the type.
138    *caucho.com/bugtrack
139    */

140   public Object JavaDoc create()
141     throws Exception JavaDoc
142   {
143     Object JavaDoc bean = _type.newInstance();
144
145     return bean;
146   }
147
148   /**
149    * If the bean implements a setParent method, call it with the actual parent.
150    *
151    * @param bean the bean to be configured
152    * @param parent the parent bean
153    * @throws Exception
154    */

155   public void setParent(Object JavaDoc bean, Object JavaDoc parent)
156     throws Exception JavaDoc
157   {
158     if (_setParent != null) {
159       try {
160         // XXX: should this be an error?
161
if (parent != null &&
162             _setParent.getParameterTypes()[0].isAssignableFrom(parent.getClass()))
163           _setParent.invoke(bean, parent);
164       } catch (InvocationTargetException JavaDoc e) {
165         if (e.getCause() instanceof Exception JavaDoc)
166           throw (Exception JavaDoc) e.getCause();
167         else
168           throw e;
169       }
170     }
171   }
172
173   /**
174    * Returns the appropriate strategy for the bean.
175    *
176    * @param attrName
177    * @return the strategy
178    * @throws ConfigException
179    */

180   public AttributeStrategy getAttributeStrategy(QName attrName)
181           throws Exception JavaDoc
182   {
183     AttributeStrategy strategy = _nsAttributeMap.get(attrName);
184
185     if (strategy == null) {
186       strategy = getAttributeStrategyImpl(attrName);
187
188       if (strategy == null && attrName.getName().equals("#text")) {
189         strategy = getAttributeStrategyImpl(TEXT);
190
191         if (strategy == null)
192           strategy = getAttributeStrategyImpl(VALUE);
193       }
194
195       if (strategy == null)
196         throw new ConfigException(L.l("'{0}' is an unknown property of '{1}'.",
197                                       attrName.getName(), _type.getName()));
198
199       _nsAttributeMap.put(attrName, strategy);
200     }
201
202     return strategy;
203   }
204
205   /**
206    * Returns the type's configured value
207    */

208   public Object JavaDoc configure(NodeBuilder builder, Node node, Object JavaDoc parent)
209     throws Exception JavaDoc
210   {
211     return builder.configureChildImpl(this, node, parent);
212   }
213
214   public void configureBean(NodeBuilder builder, Object JavaDoc bean, Node node)
215          throws Exception JavaDoc
216   {
217     Thread JavaDoc thread = Thread.currentThread();
218     ClassLoader JavaDoc oldLoader = thread.getContextClassLoader();
219
220     try {
221       if (node instanceof CauchoNode) {
222         CauchoNode cauchoNode = (CauchoNode) node;
223
224         if (_setNode != null)
225           _setNode.invoke(bean, cauchoNode);
226
227         if (_setLocation != null)
228           _setLocation.invoke(bean, cauchoNode.getFilename(), cauchoNode.getLine());
229
230         if (_setSystemId != null)
231           _setSystemId.invoke(bean, cauchoNode.getBaseURI());
232       }
233
234       if (_addDependency != null && (node instanceof QAbstractNode)) {
235         QAbstractNode qAbstractNode = (QAbstractNode) node;
236         ArrayList JavaDoc<Depend> dependList = qAbstractNode.getDependencyList();
237
238         for (int i = 0; dependList != null && i < dependList.size(); i++)
239           _addDependency.invoke(bean, dependList.get(i));
240       }
241
242       for (int i = 0; i < _injectList.size(); i++) {
243     try {
244       _injectList.get(i).configure(bean);
245     } catch (Throwable JavaDoc e) {
246       throw new ConfigException(e);
247     }
248       }
249
250       super.configureBean(builder, bean, node);
251
252       if (bean instanceof Validator)
253         builder.addValidator((Validator) bean);
254     } finally {
255       thread.setContextClassLoader(oldLoader);
256     }
257   }
258
259   /**
260    * Returns the attribute builder.
261    */

262   protected AttributeStrategy getAttributeStrategyImpl(QName qName)
263     throws Exception JavaDoc
264   {
265     String JavaDoc name = qName.getName();
266
267     AttributeStrategy strategy = null;
268
269     Method JavaDoc builderMethod = findProgramBuilderMethod();
270
271     AttributeStrategy flow = getFlowAttribute(qName);
272     if (flow != null) {
273       if (builderMethod != null)
274         return new ProgramAttributeStrategy(builderMethod);
275
276       return flow;
277     }
278
279     // hack for addText
280
if (name.equals("#text"))
281       name = "text";
282
283     Method JavaDoc setterMethod = findOneArgSetter(name);
284     Method JavaDoc createMethod = findCreate(name);
285     if (createMethod != null) {
286       return new CreateAttributeStrategy(createMethod, setterMethod);
287     }
288
289     if (setterMethod == null) {
290     }
291     else if (qName.getName().equals("#text")) {
292       strategy = TextAttributeMarshal.create(setterMethod);
293
294       return strategy;
295     }
296     else {
297       strategy = new SetterAttributeStrategy(setterMethod);
298
299       return strategy;
300     }
301
302     if (builderMethod != null)
303       return new ProgramAttributeStrategy(builderMethod);
304
305     strategy = getEnvironmentAttribute(qName);
306
307     if (strategy != null)
308       return strategy;
309
310     Method JavaDoc method = findSetPropertyMethod();
311
312     if (method != null) {
313       if (QName.class.isAssignableFrom(method.getParameterTypes()[0]))
314         return new QNameMapAttributeStrategy(method, qName);
315       else
316         return new PrimitivePropertyStrategy(method, name);
317     }
318
319     return null;
320   }
321
322   /**
323    * Returns the attribute builder.
324    */

325   public AttributeStrategy getFlowAttribute(QName name)
326     throws Exception JavaDoc
327   {
328     return TypeStrategyFactory.getFlowAttribute(getType(),name);
329   }
330
331   /**
332    * Returns the attribute builder.
333    */

334   public AttributeStrategy getEnvironmentAttribute(QName name)
335     throws Exception JavaDoc
336   {
337     return null;
338   }
339
340   /**
341    * Initializes the bean.
342    */

343   public void init(Object JavaDoc bean)
344     throws Exception JavaDoc
345   {
346     try {
347       for (int i = 0; i < _initList.size(); i++)
348     _initList.get(i).invoke(bean);
349     } catch (InvocationTargetException JavaDoc e) {
350       Throwable JavaDoc cause = e.getCause();
351
352       if (cause instanceof Exception JavaDoc)
353     throw (Exception JavaDoc) cause;
354       else
355     throw new ConfigException(cause);
356     }
357     
358     for (int i = 0; i < _destroyList.size(); i++) {
359       Method JavaDoc method = _destroyList.get(i);
360       EnvironmentListener listener;
361       listener = new WeakDestroyListener(method, bean);
362     
363       Environment.addEnvironmentListener(listener);
364     }
365   }
366
367   /**
368    * Replaces the bean object.
369    *
370    * @param bean the bean or factory to replace
371    */

372   public Object JavaDoc replaceObject(Object JavaDoc bean)
373     throws Exception JavaDoc
374   {
375     if (_replaceObject != null)
376       return _replaceObject.invoke(bean);
377     else
378       return bean;
379   }
380
381   protected Method JavaDoc findOneArgSetter(String JavaDoc attributeName)
382   {
383     String JavaDoc methodSuffix = attributeNameToBeanName(attributeName);
384
385     Method JavaDoc method = findSetter("set" + methodSuffix);
386
387     if (method == null)
388       method = findSetter("add" + methodSuffix);
389
390     return method;
391   }
392
393   private void scanMethods(Class JavaDoc cl)
394   {
395     if (cl == null || Object JavaDoc.class.equals(cl))
396       return;
397
398     Method JavaDoc []methods = cl.getDeclaredMethods();
399
400     for (int i = 0; i < methods.length; i++) {
401       Method JavaDoc method = methods[i];
402
403       if (method.isAnnotationPresent(PostConstruct.class)) {
404     if (method.getParameterTypes().length != 0)
405       throw new ConfigException(L.l("{0}::{1} is an invalid @PostConstruct method. @PostConstruct must have zero arguments.",
406                     method.getDeclaringClass().getName(),
407                     method.getName()));
408
409     if (! hasMethod(_initList, method))
410       _initList.add(method);
411       }
412       else if (method.isAnnotationPresent(PreDestroy.class)) {
413     if (method.getParameterTypes().length != 0)
414       throw new ConfigException(L.l("{0}::{1} is an invalid @PreDestroy method. @PreDestroy must have zero arguments.",
415                     method.getDeclaringClass().getName(),
416                     method.getName()));
417
418     _destroyList.add(method);
419       }
420     }
421
422     scanMethods(cl.getSuperclass());
423   }
424
425   private boolean hasMethod(ArrayList JavaDoc<Method JavaDoc> methodList, Method JavaDoc method)
426   {
427     for (int i = 0; i < methodList.size(); i++) {
428       if (methodList.get(i).getName() == method.getName())
429     return true;
430     }
431
432     return false;
433   }
434   
435   protected Method JavaDoc findCreate(String JavaDoc attributeName)
436   {
437     String JavaDoc methodSuffix = attributeNameToBeanName(attributeName);
438
439     return findMethod("create" + methodSuffix, new Class JavaDoc[0]);
440   }
441
442   protected Method JavaDoc findSetter(String JavaDoc methodName)
443   {
444     Method JavaDoc method = findSetter(methodName, false);
445
446     if (method != null)
447       return method;
448
449     return findSetter(methodName, true);
450   }
451
452   /**
453    * returns a one-arg public method or NULL
454    */

455   protected Method JavaDoc findSetter(String JavaDoc methodName,
456                               boolean ignoreCase)
457   {
458     Method JavaDoc bestMethod = null;
459
460     Method JavaDoc[] methods = getMethods();
461
462     for (int i = 0; i < methods.length; i++) {
463       Method JavaDoc method = methods[i];
464
465       if (! Modifier.isPublic(method.getModifiers()))
466         continue;
467
468       if (!ignoreCase && !method.getName().equals(methodName))
469         continue;
470       if (ignoreCase && !method.getName().equalsIgnoreCase(methodName))
471         continue;
472       if (method.getParameterTypes().length != 1)
473         continue;
474
475       if (method.getParameterTypes()[0].equals(String JavaDoc.class))
476         return method;
477
478       bestMethod = method;
479     }
480
481     return bestMethod;
482   }
483
484   /**
485    * Translates a configuration name to a bean name.
486    *
487    * <pre>
488    * foo-bar maps to fooBar
489    * </pre>
490    */

491   protected static String JavaDoc attributeNameToBeanName(String JavaDoc name)
492   {
493     CharBuffer cb = CharBuffer.allocate();
494
495     for (int i = 0; i < name.length(); i++) {
496       char ch = name.charAt(i);
497
498       if (ch == '-')
499         cb.append(Character.toUpperCase(name.charAt(++i)));
500       else if (ch == ':')
501         cb.clear();
502       else if (ch == '.')
503         cb.append('_');
504       else
505         cb.append(ch);
506     }
507
508     if (Character.isLowerCase(cb.charAt(0)))
509       cb.setCharAt(0, Character.toUpperCase(cb.charAt(0)));
510
511     return cb.close();
512   }
513
514   protected Method JavaDoc findSetPropertyMethod()
515   {
516     Method JavaDoc method;
517
518     method = findSetPropertyMethod("setProperty");
519     if (method != null)
520       return method;
521
522     method = findSetPropertyMethod("setAttribute");
523     if (method != null)
524       return method;
525
526     method = findSetPropertyMethod("put");
527     if (method != null)
528       return method;
529
530     method = findSetPropertyMethod("set");
531     if (method != null)
532       return method;
533
534     return method;
535   }
536
537   protected Method JavaDoc findSetPropertyMethod(String JavaDoc methodName)
538   {
539     Method JavaDoc method;
540
541     method = findSetPropertyMethod(methodName,
542                                    String JavaDoc.class, BuilderProgram.class);
543     if (method != null)
544       return method;
545
546     method = findSetPropertyMethod(methodName,
547                                    Object JavaDoc.class, BuilderProgram.class);
548     if (method != null)
549       return method;
550
551     method = findSetPropertyMethod(methodName, QName.class, null);
552     if (method != null)
553       return method;
554
555     method = findSetPropertyMethod(methodName, String JavaDoc.class, String JavaDoc.class);
556     if (method != null)
557       return method;
558
559     method = findSetPropertyMethod(methodName, String JavaDoc.class, Object JavaDoc.class);
560     if (method != null)
561       return method;
562     method = findSetPropertyMethod(methodName, String JavaDoc.class, null);
563     if (method != null)
564       return method;
565
566     method = findSetPropertyMethod(methodName, Object JavaDoc.class, Object JavaDoc.class);
567     if (method != null)
568       return method;
569     method = findSetPropertyMethod(methodName, Object JavaDoc.class, null);
570     if (method != null)
571       return method;
572
573     return null;
574   }
575
576   protected Method JavaDoc findSetPropertyMethod(String JavaDoc name,
577                                          Class JavaDoc keyClass,
578                                          Class JavaDoc valueClass)
579   {
580     Method JavaDoc []methods = getMethods();
581
582     for (int i = 0; i < methods.length; i++) {
583       Method JavaDoc method = methods[i];
584
585       if (! method.getName().equals(name))
586         continue;
587       else if (! Modifier.isPublic(method.getModifiers()))
588         continue;
589
590       Class JavaDoc []methodTypes = method.getParameterTypes();
591
592       if (methodTypes.length != 2)
593         continue;
594
595       if (! methodTypes[0].equals(keyClass))
596         continue;
597
598       if (valueClass != null && ! methodTypes[1].equals(valueClass))
599         continue;
600
601       return method;
602     }
603
604     return null;
605   }
606
607   protected Method JavaDoc findProgramBuilderMethod()
608   {
609     Method JavaDoc method = findMethod("addBuilderProgram",
610                                new Class JavaDoc[] { BuilderProgram.class });
611
612     if (method != null)
613       return method;
614
615     return method;
616   }
617
618   protected Method JavaDoc findMethod(String JavaDoc methodName, Class JavaDoc[] parameterTypes)
619   {
620     Method JavaDoc []methods = getMethods();
621
622     loop:
623     for (int i = 0; i < methods.length; i++) {
624       Method JavaDoc method = methods[i];
625
626       if (! method.getName().equals(methodName))
627         continue;
628       else if (! Modifier.isPublic(method.getModifiers()))
629         continue;
630
631       Class JavaDoc []methodTypes = method.getParameterTypes();
632
633       if (methodTypes.length != parameterTypes.length)
634         continue;
635
636       for (int j = 0; j < methodTypes.length; j++) {
637         if (parameterTypes[j] != null && ! methodTypes[j].equals(parameterTypes[j]))
638           continue loop;
639       }
640
641       return method;
642     }
643
644     return null;
645   }
646
647   private Method JavaDoc []getMethods()
648   {
649     SoftReference JavaDoc<Method JavaDoc []> methodsRef = _methodsRef;
650     Method JavaDoc [] methods;
651
652     if (methodsRef != null && (methods = methodsRef.get()) != null)
653       return methods;
654
655     ArrayList JavaDoc<Method JavaDoc> methodList = new ArrayList JavaDoc<Method JavaDoc>();
656
657     getMethods(methodList, _type);
658
659     methods = methodList.toArray(new Method JavaDoc[methodList.size()]);
660
661     _methodsRef = new SoftReference JavaDoc<Method JavaDoc []>(methods);
662
663     return methods;
664   }
665
666   public static void getMethods(ArrayList JavaDoc<Method JavaDoc> list, Class JavaDoc type)
667   {
668     if (type == null)
669       return;
670
671     if (Modifier.isPublic(type.getModifiers())) {
672       Method JavaDoc []methods = type.getDeclaredMethods();
673
674       for (int i = 0; i < methods.length; i++) {
675         if (! contains(list, methods[i]))
676           list.add(methods[i]);
677       }
678     }
679
680     Class JavaDoc []interfaces = type.getInterfaces();
681     for (int i = 0; i < interfaces.length; i++)
682       getMethods(list, interfaces[i]);
683
684     getMethods(list, type.getSuperclass());
685   }
686
687   public static boolean contains(ArrayList JavaDoc<Method JavaDoc> list, Method JavaDoc method)
688   {
689     for (int i = 0; i < list.size(); i++) {
690       if (isMatch(list.get(i), method))
691         return true;
692     }
693
694     return false;
695   }
696
697   public static boolean isMatch(Method JavaDoc methodA, Method JavaDoc methodB)
698   {
699     if (! methodA.getName().equals(methodB.getName()))
700       return false;
701
702     Class JavaDoc []paramA = methodA.getParameterTypes();
703     Class JavaDoc []paramB = methodB.getParameterTypes();
704
705     if (paramA.length != paramB.length)
706       return false;
707
708     for (int i = 0; i < paramA.length; i++) {
709       if (! paramA[i].equals(paramB[i]))
710         return false;
711     }
712
713     return true;
714   }
715
716   protected static void invokeBeanMethod(Object JavaDoc bean, Method JavaDoc method, Object JavaDoc[] args)
717     throws ConfigException
718   {
719     try {
720       method.invoke(bean, args);
721     }
722     catch (IllegalArgumentException JavaDoc e) {
723       //e.printStackTrace();
724
throw new ConfigException(L.l("{0}: parameter type mismatch invoking method `{1}'.", bean.getClass().getName(), method.toString()), e);
725     }
726     catch (IllegalAccessException JavaDoc e) {
727       throw new ConfigException(L.l("Bean method `{0}' isn't accessible.", method.toString()), e);
728     }
729     catch (InvocationTargetException JavaDoc e) {
730       throw new ConfigException(e.getCause());
731     }
732   }
733
734   protected ELContext getEnvironment()
735   {
736     return Config.getEnvironment();
737   }
738
739   protected static NotImplementedException notImplemented()
740   {
741     return new NotImplementedException(L.l("Not implemented."));
742   }
743
744   public String JavaDoc toString()
745   {
746     return "BeanTypeStrategy[" + _type.getName() + "]";
747   }
748 }
749
Popular Tags