1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 1999 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. *
  54. */
  55. package org.apache.commons.el;
  56. import java.beans.BeanInfo;
  57. import java.beans.EventSetDescriptor;
  58. import java.beans.IndexedPropertyDescriptor;
  59. import java.beans.IntrospectionException;
  60. import java.beans.Introspector;
  61. import java.beans.PropertyDescriptor;
  62. import java.lang.reflect.Method;
  63. import java.lang.reflect.Modifier;
  64. import java.text.MessageFormat;
  65. import java.util.HashMap;
  66. import java.util.Map;
  67. import javax.servlet.jsp.el.ELException;
  68. /**
  69. *
  70. * <p>Manages the BeanInfo for one class - contains the BeanInfo, and
  71. * also a mapping from property name to BeanInfoProperty. There are
  72. * also static methods for accessing the BeanInfoManager for a class -
  73. * those mappings are cached permanently so that once the
  74. * BeanInfoManager is calculated, it doesn't have to be calculated
  75. * again.
  76. *
  77. * @author Nathan Abramson - Art Technology Group
  78. * @version $Change: 181181 $$DateTime: 2001/06/26 09:55:09 $$Author: luehe $
  79. **/
  80. public class BeanInfoManager
  81. {
  82. //-------------------------------------
  83. // Properties
  84. //-------------------------------------
  85. // property beanClass
  86. Class mBeanClass;
  87. public Class getBeanClass ()
  88. { return mBeanClass; }
  89. //-------------------------------------
  90. // Member variables
  91. //-------------------------------------
  92. // The BeanInfo
  93. BeanInfo mBeanInfo;
  94. // Mapping from property name to BeanInfoProperty
  95. Map mPropertyByName;
  96. // Mapping from property name to BeanInfoIndexedProperty
  97. Map mIndexedPropertyByName;
  98. // Mapping from event set name to event set descriptor
  99. Map mEventSetByName;
  100. // Flag if this is initialized
  101. boolean mInitialized;
  102. // The global mapping from class to BeanInfoManager
  103. static Map mBeanInfoManagerByClass = new HashMap ();
  104. //-------------------------------------
  105. /**
  106. *
  107. * Constructor
  108. **/
  109. BeanInfoManager (Class pBeanClass)
  110. {
  111. mBeanClass = pBeanClass;
  112. }
  113. //-------------------------------------
  114. /**
  115. *
  116. * Returns the BeanInfoManager for the specified class
  117. **/
  118. public static BeanInfoManager getBeanInfoManager (Class pClass)
  119. {
  120. BeanInfoManager ret = (BeanInfoManager)
  121. mBeanInfoManagerByClass.get (pClass);
  122. if (ret == null) {
  123. ret = createBeanInfoManager (pClass);
  124. }
  125. return ret;
  126. }
  127. //-------------------------------------
  128. /**
  129. *
  130. * Creates and registers the BeanInfoManager for the given class if
  131. * it isn't already registered.
  132. **/
  133. static synchronized BeanInfoManager createBeanInfoManager (Class pClass)
  134. {
  135. // Because this method is synchronized statically, the
  136. // BeanInfoManager is not initialized at this time (otherwise it
  137. // could end up being a bottleneck for the entire system). It is
  138. // put into the map in an uninitialized state. The first time
  139. // someone tries to use it, it will be initialized (with proper
  140. // synchronizations in place to make sure it is only initialized
  141. // once).
  142. BeanInfoManager ret = (BeanInfoManager)
  143. mBeanInfoManagerByClass.get (pClass);
  144. if (ret == null) {
  145. ret = new BeanInfoManager (pClass);
  146. mBeanInfoManagerByClass.put (pClass, ret);
  147. }
  148. return ret;
  149. }
  150. //-------------------------------------
  151. /**
  152. *
  153. * Returns the BeanInfoProperty for the specified property in the
  154. * given class, or null if not found.
  155. **/
  156. public static BeanInfoProperty getBeanInfoProperty
  157. (Class pClass,
  158. String pPropertyName,
  159. Logger pLogger)
  160. throws ELException
  161. {
  162. return getBeanInfoManager (pClass).getProperty (pPropertyName, pLogger);
  163. }
  164. //-------------------------------------
  165. /**
  166. *
  167. * Returns the BeanInfoIndexedProperty for the specified property in
  168. * the given class, or null if not found.
  169. **/
  170. public static BeanInfoIndexedProperty getBeanInfoIndexedProperty
  171. (Class pClass,
  172. String pIndexedPropertyName,
  173. Logger pLogger)
  174. throws ELException
  175. {
  176. return getBeanInfoManager
  177. (pClass).getIndexedProperty (pIndexedPropertyName, pLogger);
  178. }
  179. //-------------------------------------
  180. /**
  181. *
  182. * Makes sure that this class has been initialized, and synchronizes
  183. * the initialization if it's required.
  184. **/
  185. void checkInitialized (Logger pLogger)
  186. throws ELException
  187. {
  188. if (!mInitialized) {
  189. synchronized (this) {
  190. if (!mInitialized) {
  191. initialize (pLogger);
  192. mInitialized = true;
  193. }
  194. }
  195. }
  196. }
  197. //-------------------------------------
  198. /**
  199. *
  200. * Initializes by mapping property names to BeanInfoProperties
  201. **/
  202. void initialize (Logger pLogger)
  203. throws ELException
  204. {
  205. try {
  206. mBeanInfo = Introspector.getBeanInfo (mBeanClass);
  207. mPropertyByName = new HashMap ();
  208. mIndexedPropertyByName = new HashMap ();
  209. PropertyDescriptor [] pds = mBeanInfo.getPropertyDescriptors ();
  210. for (int i = 0; pds != null && i < pds.length; i++) {
  211. // Treat as both an indexed property and a normal property
  212. PropertyDescriptor pd = pds [i];
  213. if (pd instanceof IndexedPropertyDescriptor) {
  214. IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
  215. Method readMethod = getPublicMethod (ipd.getIndexedReadMethod ());
  216. Method writeMethod = getPublicMethod (ipd.getIndexedWriteMethod ());
  217. BeanInfoIndexedProperty property = new BeanInfoIndexedProperty
  218. (readMethod,
  219. writeMethod,
  220. ipd);
  221. mIndexedPropertyByName.put (ipd.getName (), property);
  222. }
  223. Method readMethod = getPublicMethod (pd.getReadMethod ());
  224. Method writeMethod = getPublicMethod (pd.getWriteMethod ());
  225. BeanInfoProperty property = new BeanInfoProperty
  226. (readMethod,
  227. writeMethod,
  228. pd);
  229. mPropertyByName.put (pd.getName (), property);
  230. }
  231. mEventSetByName = new HashMap ();
  232. EventSetDescriptor [] esds = mBeanInfo.getEventSetDescriptors ();
  233. for (int i = 0; esds != null && i < esds.length; i++) {
  234. EventSetDescriptor esd = esds [i];
  235. mEventSetByName.put (esd.getName (), esd);
  236. }
  237. }
  238. catch (IntrospectionException exc) {
  239. if (pLogger.isLoggingWarning ()) {
  240. pLogger.logWarning
  241. (Constants.EXCEPTION_GETTING_BEANINFO,
  242. exc,
  243. mBeanClass.getName ());
  244. }
  245. }
  246. }
  247. //-------------------------------------
  248. /**
  249. *
  250. * Returns the BeanInfo for the class
  251. **/
  252. BeanInfo getBeanInfo (Logger pLogger)
  253. throws ELException
  254. {
  255. checkInitialized (pLogger);
  256. return mBeanInfo;
  257. }
  258. //-------------------------------------
  259. /**
  260. *
  261. * Returns the BeanInfoProperty for the given property name, or null
  262. * if not found.
  263. **/
  264. public BeanInfoProperty getProperty (String pPropertyName,
  265. Logger pLogger)
  266. throws ELException
  267. {
  268. checkInitialized (pLogger);
  269. return (BeanInfoProperty) mPropertyByName.get (pPropertyName);
  270. }
  271. //-------------------------------------
  272. /**
  273. *
  274. * Returns the BeanInfoIndexedProperty for the given property name,
  275. * or null if not found.
  276. **/
  277. public BeanInfoIndexedProperty getIndexedProperty
  278. (String pIndexedPropertyName,
  279. Logger pLogger)
  280. throws ELException
  281. {
  282. checkInitialized (pLogger);
  283. return (BeanInfoIndexedProperty)
  284. mIndexedPropertyByName.get (pIndexedPropertyName);
  285. }
  286. //-------------------------------------
  287. /**
  288. *
  289. * Returns the EventSetDescriptor for the given event set name, or
  290. * null if not found.
  291. **/
  292. public EventSetDescriptor getEventSet (String pEventSetName,
  293. Logger pLogger)
  294. throws ELException
  295. {
  296. checkInitialized (pLogger);
  297. return (EventSetDescriptor) mEventSetByName.get (pEventSetName);
  298. }
  299. //-------------------------------------
  300. // Finding the public version of a method - if a PropertyDescriptor
  301. // is obtained for a non-public class that implements a public
  302. // interface, the read/write methods will be for the class, and
  303. // therefore inaccessible. To correct this, a version of the same
  304. // method must be found in a superclass or interface.
  305. //-------------------------------------
  306. /**
  307. *
  308. * Returns a publicly-accessible version of the given method, by
  309. * searching for a public declaring class.
  310. **/
  311. static Method getPublicMethod (Method pMethod)
  312. {
  313. if (pMethod == null) {
  314. return null;
  315. }
  316. // See if the method is already available from a public class
  317. Class cl = pMethod.getDeclaringClass ();
  318. if (Modifier.isPublic (cl.getModifiers ())) {
  319. return pMethod;
  320. }
  321. // Otherwise, try to find a public class that declares the method
  322. Method ret = getPublicMethod (cl, pMethod);
  323. if (ret != null) {
  324. return ret;
  325. }
  326. else {
  327. return pMethod;
  328. }
  329. }
  330. //-------------------------------------
  331. /**
  332. *
  333. * If the given class is public and has a Method that declares the
  334. * same name and arguments as the given method, then that method is
  335. * returned. Otherwise the superclass and interfaces are searched
  336. * recursively.
  337. **/
  338. static Method getPublicMethod (Class pClass,
  339. Method pMethod)
  340. {
  341. // See if this is a public class declaring the method
  342. if (Modifier.isPublic (pClass.getModifiers ())) {
  343. try {
  344. Method m;
  345. try {
  346. m = pClass.getDeclaredMethod (pMethod.getName (),
  347. pMethod.getParameterTypes ());
  348. } catch (java.security.AccessControlException ex) {
  349. // kludge to accommodate J2EE RI's default settings
  350. // TODO: see if we can simply replace
  351. // getDeclaredMethod() with getMethod() ...?
  352. m = pClass.getMethod(pMethod.getName (),
  353. pMethod.getParameterTypes ());
  354. }
  355. if (Modifier.isPublic (m.getModifiers ())) {
  356. return m;
  357. }
  358. }
  359. catch (NoSuchMethodException exc) {}
  360. }
  361. // Search the interfaces
  362. {
  363. Class [] interfaces = pClass.getInterfaces ();
  364. if (interfaces != null) {
  365. for (int i = 0; i < interfaces.length; i++) {
  366. Method m = getPublicMethod (interfaces [i], pMethod);
  367. if (m != null) {
  368. return m;
  369. }
  370. }
  371. }
  372. }
  373. // Search the superclass
  374. {
  375. Class superclass = pClass.getSuperclass ();
  376. if (superclass != null) {
  377. Method m = getPublicMethod (superclass, pMethod);
  378. if (m != null) {
  379. return m;
  380. }
  381. }
  382. }
  383. return null;
  384. }
  385. //-------------------------------------
  386. }