1 16 17 package org.springframework.scripting.support; 18 19 import java.util.HashMap ; 20 import java.util.Iterator ; 21 import java.util.Map ; 22 23 import net.sf.cglib.asm.Type; 24 import net.sf.cglib.core.Signature; 25 import net.sf.cglib.proxy.InterfaceMaker; 26 import org.apache.commons.logging.Log; 27 import org.apache.commons.logging.LogFactory; 28 29 import org.springframework.aop.TargetSource; 30 import org.springframework.aop.framework.AopInfrastructureBean; 31 import org.springframework.aop.framework.ProxyFactory; 32 import org.springframework.aop.support.DelegatingIntroductionInterceptor; 33 import org.springframework.beans.BeanUtils; 34 import org.springframework.beans.PropertyValue; 35 import org.springframework.beans.factory.BeanClassLoaderAware; 36 import org.springframework.beans.factory.BeanDefinitionStoreException; 37 import org.springframework.beans.factory.BeanFactory; 38 import org.springframework.beans.factory.BeanFactoryAware; 39 import org.springframework.beans.factory.DisposableBean; 40 import org.springframework.beans.factory.config.BeanDefinition; 41 import org.springframework.beans.factory.config.BeanPostProcessor; 42 import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; 43 import org.springframework.beans.factory.support.AbstractBeanFactory; 44 import org.springframework.beans.factory.support.DefaultListableBeanFactory; 45 import org.springframework.beans.factory.support.RootBeanDefinition; 46 import org.springframework.context.ResourceLoaderAware; 47 import org.springframework.core.Conventions; 48 import org.springframework.core.Ordered; 49 import org.springframework.core.io.DefaultResourceLoader; 50 import org.springframework.core.io.ResourceLoader; 51 import org.springframework.scripting.ScriptFactory; 52 import org.springframework.scripting.ScriptSource; 53 import org.springframework.util.Assert; 54 import org.springframework.util.ClassUtils; 55 import org.springframework.util.ObjectUtils; 56 import org.springframework.util.StringUtils; 57 58 134 public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProcessorAdapter 135 implements BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware, DisposableBean, Ordered { 136 137 143 public static final String INLINE_SCRIPT_PREFIX = "inline:"; 144 145 public static final String REFRESH_CHECK_DELAY_ATTRIBUTE = 146 Conventions.getQualifiedAttributeName(ScriptFactoryPostProcessor.class, "refreshCheckDelay"); 147 148 private static final String SCRIPT_FACTORY_NAME_PREFIX = "scriptFactory."; 149 150 private static final String SCRIPTED_OBJECT_NAME_PREFIX = "scriptedObject."; 151 152 153 154 protected final Log logger = LogFactory.getLog(getClass()); 155 156 private long defaultRefreshCheckDelay = -1; 157 158 private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); 159 160 private AbstractBeanFactory beanFactory; 161 162 private ResourceLoader resourceLoader = new DefaultResourceLoader(); 163 164 final DefaultListableBeanFactory scriptBeanFactory = new DefaultListableBeanFactory(); 165 166 167 private final Map scriptSourceCache = new HashMap (); 168 169 170 178 public void setDefaultRefreshCheckDelay(long defaultRefreshCheckDelay) { 179 this.defaultRefreshCheckDelay = defaultRefreshCheckDelay; 180 } 181 182 public void setBeanClassLoader(ClassLoader classLoader) { 183 this.beanClassLoader = classLoader; 184 } 185 186 public void setBeanFactory(BeanFactory beanFactory) { 187 if (!(beanFactory instanceof AbstractBeanFactory)) { 188 throw new IllegalStateException ( 189 "ScriptFactoryPostProcessor must run in AbstractBeanFactory, not in " + beanFactory); 190 } 191 this.beanFactory = (AbstractBeanFactory) beanFactory; 192 193 this.scriptBeanFactory.setParentBeanFactory(this.beanFactory); 195 196 this.scriptBeanFactory.copyConfigurationFrom(this.beanFactory); 198 199 for (Iterator it = this.scriptBeanFactory.getBeanPostProcessors().iterator(); it.hasNext();) { 202 BeanPostProcessor postProcessor = (BeanPostProcessor) it.next(); 203 if (postProcessor instanceof AopInfrastructureBean) { 204 it.remove(); 205 } 206 } 207 } 208 209 public void setResourceLoader(ResourceLoader resourceLoader) { 210 this.resourceLoader = resourceLoader; 211 } 212 213 public int getOrder() { 214 return Integer.MIN_VALUE; 215 } 216 217 218 public Class predictBeanType(Class beanClass, String beanName) { 219 if (!ScriptFactory.class.isAssignableFrom(beanClass)) { 221 return null; 222 } 223 224 RootBeanDefinition bd = this.beanFactory.getMergedBeanDefinition(beanName); 225 String scriptFactoryBeanName = SCRIPT_FACTORY_NAME_PREFIX + beanName; 226 String scriptedObjectBeanName = SCRIPTED_OBJECT_NAME_PREFIX + beanName; 227 prepareScriptBeans(bd, scriptFactoryBeanName, scriptedObjectBeanName); 228 229 ScriptFactory scriptFactory = 230 (ScriptFactory) this.scriptBeanFactory.getBean(scriptFactoryBeanName, ScriptFactory.class); 231 ScriptSource scriptSource = 232 getScriptSource(scriptFactoryBeanName, scriptFactory.getScriptSourceLocator()); 233 Class [] interfaces = scriptFactory.getScriptInterfaces(); 234 235 Class scriptedType = null; 236 try { 237 scriptedType = scriptFactory.getScriptedObjectType(scriptSource); 238 } 239 catch (Exception ex) { 240 if (logger.isDebugEnabled()) { 241 logger.debug("Could not determine the scripted object type for script factory [" + scriptFactory + "]", ex); 242 } 243 } 244 245 if (scriptedType != null) { 246 return scriptedType; 247 } 248 else if (ObjectUtils.isEmpty(interfaces)) { 249 if (bd.isSingleton()) { 250 return this.scriptBeanFactory.getBean(scriptedObjectBeanName).getClass(); 251 } 252 else { 253 return null; 254 } 255 } 256 else { 257 return (interfaces.length == 1 ? interfaces[0] : createCompositeInterface(interfaces)); 258 } 259 } 260 261 public Object postProcessBeforeInstantiation(Class beanClass, String beanName) { 262 if (!ScriptFactory.class.isAssignableFrom(beanClass)) { 264 return null; 265 } 266 267 RootBeanDefinition bd = this.beanFactory.getMergedBeanDefinition(beanName); 268 String scriptFactoryBeanName = SCRIPT_FACTORY_NAME_PREFIX + beanName; 269 String scriptedObjectBeanName = SCRIPTED_OBJECT_NAME_PREFIX + beanName; 270 prepareScriptBeans(bd, scriptFactoryBeanName, scriptedObjectBeanName); 271 272 long refreshCheckDelay = resolveRefreshCheckDelay(bd); 273 if (refreshCheckDelay >= 0) { 274 ScriptFactory scriptFactory = 275 (ScriptFactory) this.scriptBeanFactory.getBean(scriptFactoryBeanName, ScriptFactory.class); 276 ScriptSource scriptSource = 277 getScriptSource(scriptFactoryBeanName, scriptFactory.getScriptSourceLocator()); 278 Class [] interfaces = scriptFactory.getScriptInterfaces(); 279 RefreshableScriptTargetSource ts = 280 new RefreshableScriptTargetSource(this.scriptBeanFactory, scriptedObjectBeanName, scriptSource); 281 ts.setRefreshCheckDelay(refreshCheckDelay); 282 return createRefreshableProxy(ts, interfaces); 283 } 284 285 return this.scriptBeanFactory.getBean(scriptedObjectBeanName); 286 } 287 288 289 297 protected void prepareScriptBeans( 298 RootBeanDefinition bd, String scriptFactoryBeanName, String scriptedObjectBeanName) { 299 300 synchronized (this.scriptBeanFactory) { 302 if (!this.scriptBeanFactory.containsBeanDefinition(scriptedObjectBeanName)) { 303 304 this.scriptBeanFactory.registerBeanDefinition( 305 scriptFactoryBeanName, createScriptFactoryBeanDefinition(bd)); 306 ScriptFactory scriptFactory = 307 (ScriptFactory) this.scriptBeanFactory.getBean(scriptFactoryBeanName, ScriptFactory.class); 308 ScriptSource scriptSource = 309 getScriptSource(scriptFactoryBeanName, scriptFactory.getScriptSourceLocator()); 310 Class [] interfaces = scriptFactory.getScriptInterfaces(); 311 312 Class [] scriptedInterfaces = interfaces; 313 if (scriptFactory.requiresConfigInterface() && !bd.getPropertyValues().isEmpty()) { 314 PropertyValue[] pvs = bd.getPropertyValues().getPropertyValues(); 315 Class configInterface = createConfigInterface(pvs, interfaces); 316 scriptedInterfaces = (Class []) ObjectUtils.addObjectToArray(interfaces, configInterface); 317 } 318 319 RootBeanDefinition objectBd = createScriptedObjectBeanDefinition( 320 bd, scriptFactoryBeanName, scriptSource, scriptedInterfaces); 321 long refreshCheckDelay = resolveRefreshCheckDelay(bd); 322 if (refreshCheckDelay >= 0) { 323 objectBd.setSingleton(false); 324 } 325 326 this.scriptBeanFactory.registerBeanDefinition(scriptedObjectBeanName, objectBd); 327 } 328 } 329 } 330 331 341 protected long resolveRefreshCheckDelay(BeanDefinition beanDefinition) { 342 long refreshCheckDelay = this.defaultRefreshCheckDelay; 343 Object attributeValue = beanDefinition.getAttribute(REFRESH_CHECK_DELAY_ATTRIBUTE); 344 if (attributeValue instanceof Number ) { 345 refreshCheckDelay = ((Number ) attributeValue).longValue(); 346 } 347 else if (attributeValue instanceof String ) { 348 refreshCheckDelay = Long.parseLong((String ) attributeValue); 349 } 350 else if (attributeValue != null) { 351 throw new BeanDefinitionStoreException( 352 "Invalid refresh check delay attribute [" + REFRESH_CHECK_DELAY_ATTRIBUTE + 353 "] with value [" + attributeValue + "]: needs to be of type Number or String"); 354 } 355 return refreshCheckDelay; 356 } 357 358 366 protected RootBeanDefinition createScriptFactoryBeanDefinition(RootBeanDefinition bd) { 367 RootBeanDefinition scriptBd = new RootBeanDefinition(); 368 scriptBd.setBeanClassName(bd.getBeanClassName()); 369 scriptBd.getConstructorArgumentValues().addArgumentValues(bd.getConstructorArgumentValues()); 370 return scriptBd; 371 } 372 373 381 protected ScriptSource getScriptSource(String beanName, String scriptSourceLocator) { 382 synchronized (this.scriptSourceCache) { 383 ScriptSource scriptSource = (ScriptSource) this.scriptSourceCache.get(beanName); 384 if (scriptSource == null) { 385 scriptSource = convertToScriptSource(scriptSourceLocator, this.resourceLoader); 386 this.scriptSourceCache.put(beanName, scriptSource); 387 } 388 return scriptSource; 389 } 390 } 391 392 401 protected ScriptSource convertToScriptSource(String scriptSourceLocator, ResourceLoader resourceLoader) { 402 if (scriptSourceLocator.startsWith(INLINE_SCRIPT_PREFIX)) { 403 return new StaticScriptSource(scriptSourceLocator.substring(INLINE_SCRIPT_PREFIX.length())); 404 } 405 else { 406 return new ResourceScriptSource(resourceLoader.getResource(scriptSourceLocator)); 407 } 408 } 409 410 421 protected Class createConfigInterface(PropertyValue[] pvs, Class [] interfaces) { 422 Assert.notEmpty(pvs, "Property values must not be empty"); 423 InterfaceMaker maker = new InterfaceMaker(); 424 for (int i = 0; i < pvs.length; i++) { 425 String propertyName = pvs[i].getName(); 426 Class propertyType = BeanUtils.findPropertyType(propertyName, interfaces); 427 String setterName = "set" + StringUtils.capitalize(propertyName); 428 Signature signature = new Signature(setterName, Type.VOID_TYPE, new Type[] {Type.getType(propertyType)}); 429 maker.add(signature, new Type[0]); 430 } 431 return maker.create(); 432 } 433 434 443 protected Class createCompositeInterface(Class [] interfaces) { 444 return ClassUtils.createCompositeInterface(interfaces, this.beanClassLoader); 445 } 446 447 458 protected RootBeanDefinition createScriptedObjectBeanDefinition( 459 RootBeanDefinition bd, String scriptFactoryBeanName, ScriptSource scriptSource, Class [] interfaces) { 460 461 RootBeanDefinition objectBd = new RootBeanDefinition(bd); 462 objectBd.setFactoryBeanName(scriptFactoryBeanName); 463 objectBd.setFactoryMethodName("getScriptedObject"); 464 objectBd.getConstructorArgumentValues().clear(); 465 objectBd.getConstructorArgumentValues().addIndexedArgumentValue(0, scriptSource); 466 objectBd.getConstructorArgumentValues().addIndexedArgumentValue(1, interfaces); 467 return objectBd; 468 } 469 470 478 protected Object createRefreshableProxy(TargetSource ts, Class [] interfaces) { 479 ProxyFactory proxyFactory = new ProxyFactory(); 480 proxyFactory.setTargetSource(ts); 481 482 if (interfaces == null) { 483 interfaces = ClassUtils.getAllInterfacesForClass(ts.getTargetClass()); 484 } 485 proxyFactory.setInterfaces(interfaces); 486 487 DelegatingIntroductionInterceptor introduction = new DelegatingIntroductionInterceptor(ts); 488 introduction.suppressInterface(TargetSource.class); 489 proxyFactory.addAdvice(introduction); 490 491 return proxyFactory.getProxy(this.beanClassLoader); 492 } 493 494 495 498 public void destroy() { 499 this.scriptBeanFactory.destroySingletons(); 500 } 501 502 } 503 | Popular Tags |