1 16 17 package org.springframework.beans; 18 19 import java.beans.BeanInfo ; 20 import java.beans.IntrospectionException ; 21 import java.beans.Introspector ; 22 import java.beans.PropertyDescriptor ; 23 import java.lang.ref.Reference ; 24 import java.lang.ref.WeakReference ; 25 import java.util.Collections ; 26 import java.util.HashMap ; 27 import java.util.HashSet ; 28 import java.util.Iterator ; 29 import java.util.Map ; 30 import java.util.Set ; 31 import java.util.WeakHashMap ; 32 33 import org.apache.commons.logging.Log; 34 import org.apache.commons.logging.LogFactory; 35 36 58 public class CachedIntrospectionResults { 59 60 private static final Log logger = LogFactory.getLog(CachedIntrospectionResults.class); 61 62 66 static final Set acceptedClassLoaders = Collections.synchronizedSet(new HashSet ()); 67 68 73 static final Map classCache = Collections.synchronizedMap(new WeakHashMap ()); 74 75 76 88 public static void acceptClassLoader(ClassLoader classLoader) { 89 if (classLoader != null) { 90 acceptedClassLoaders.add(classLoader); 91 } 92 } 93 94 101 public static void clearClassLoader(ClassLoader classLoader) { 102 if (classLoader == null) { 103 return; 104 } 105 synchronized (classCache) { 106 for (Iterator it = classCache.keySet().iterator(); it.hasNext();) { 107 Class beanClass = (Class ) it.next(); 108 if (isUnderneathClassLoader(beanClass.getClassLoader(), classLoader)) { 109 it.remove(); 110 } 111 } 112 } 113 synchronized (acceptedClassLoaders) { 114 for (Iterator it = acceptedClassLoaders.iterator(); it.hasNext();) { 115 ClassLoader registeredLoader = (ClassLoader ) it.next(); 116 if (isUnderneathClassLoader(registeredLoader, classLoader)) { 117 it.remove(); 118 } 119 } 120 } 121 } 122 123 131 static CachedIntrospectionResults forClass(Class beanClass) throws BeansException { 132 CachedIntrospectionResults results = null; 133 Object value = classCache.get(beanClass); 134 if (value instanceof Reference ) { 135 Reference ref = (Reference ) value; 136 results = (CachedIntrospectionResults) ref.get(); 137 } 138 else { 139 results = (CachedIntrospectionResults) value; 140 } 141 if (results == null) { 142 results = new CachedIntrospectionResults(beanClass); 144 if (isCacheSafe(beanClass) || isClassLoaderAccepted(beanClass.getClassLoader())) { 145 classCache.put(beanClass, results); 146 } 147 else { 148 if (logger.isDebugEnabled()) { 149 logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe"); 150 } 151 classCache.put(beanClass, new WeakReference (results)); 152 } 153 } 154 else { 155 if (logger.isTraceEnabled()) { 156 logger.trace("Using cached introspection results for class [" + beanClass.getName() + "]"); 157 } 158 } 159 return results; 160 } 161 162 169 private static boolean isClassLoaderAccepted(ClassLoader classLoader) { 170 Object [] acceptedLoaderArray = acceptedClassLoaders.toArray(); 173 for (int i = 0; i < acceptedLoaderArray.length; i++) { 174 ClassLoader registeredLoader = (ClassLoader ) acceptedLoaderArray[i]; 175 if (isUnderneathClassLoader(classLoader, registeredLoader)) { 176 return true; 177 } 178 } 179 return false; 180 } 181 182 190 private static boolean isCacheSafe(Class clazz) { 191 ClassLoader target = clazz.getClassLoader(); 192 if (target == null) { 193 return false; 194 } 195 ClassLoader cur = CachedIntrospectionResults.class.getClassLoader(); 196 if (cur == target) { 197 return true; 198 } 199 while (cur != null) { 200 cur = cur.getParent(); 201 if (cur == target) { 202 return true; 203 } 204 } 205 return false; 206 } 207 208 214 private static boolean isUnderneathClassLoader(ClassLoader candidate, ClassLoader parent) { 215 if (candidate == null) { 216 return false; 217 } 218 if (candidate == parent) { 219 return true; 220 } 221 ClassLoader classLoaderToCheck = candidate; 222 while (classLoaderToCheck != null) { 223 classLoaderToCheck = classLoaderToCheck.getParent(); 224 if (classLoaderToCheck == parent) { 225 return true; 226 } 227 } 228 return false; 229 } 230 231 232 233 private final BeanInfo beanInfo; 234 235 236 private final Map propertyDescriptorCache; 237 238 239 244 private CachedIntrospectionResults(Class beanClass) throws BeansException { 245 try { 246 if (logger.isTraceEnabled()) { 247 logger.trace("Getting BeanInfo for class [" + beanClass.getName() + "]"); 248 } 249 this.beanInfo = Introspector.getBeanInfo(beanClass); 250 251 Class classToFlush = beanClass; 256 do { 257 Introspector.flushFromCaches(classToFlush); 258 classToFlush = classToFlush.getSuperclass(); 259 } 260 while (classToFlush != null); 261 262 if (logger.isTraceEnabled()) { 263 logger.trace("Caching PropertyDescriptors for class [" + beanClass.getName() + "]"); 264 } 265 this.propertyDescriptorCache = new HashMap (); 266 267 PropertyDescriptor [] pds = this.beanInfo.getPropertyDescriptors(); 269 for (int i = 0; i < pds.length; i++) { 270 PropertyDescriptor pd = pds[i]; 271 if (logger.isTraceEnabled()) { 272 logger.trace("Found bean property '" + pd.getName() + "'" + 273 (pd.getPropertyType() != null ? 274 " of type [" + pd.getPropertyType().getName() + "]" : "") + 275 (pd.getPropertyEditorClass() != null ? 276 "; editor [" + pd.getPropertyEditorClass().getName() + "]" : "")); 277 } 278 this.propertyDescriptorCache.put(pd.getName(), pd); 279 } 280 } 281 catch (IntrospectionException ex) { 282 throw new FatalBeanException("Cannot get BeanInfo for object of class [" + beanClass.getName() + "]", ex); 283 } 284 } 285 286 BeanInfo getBeanInfo() { 287 return this.beanInfo; 288 } 289 290 Class getBeanClass() { 291 return this.beanInfo.getBeanDescriptor().getBeanClass(); 292 } 293 294 PropertyDescriptor getPropertyDescriptor(String propertyName) { 295 return (PropertyDescriptor ) this.propertyDescriptorCache.get(propertyName); 296 } 297 298 } 299 | Popular Tags |