SPR-6268: Add proxy-target-class to <lang:groovy/>

This commit is contained in:
David Syer 2011-06-17 12:14:01 +00:00
parent 5bfeb34b89
commit 64fd0b081d
11 changed files with 305 additions and 120 deletions

View File

@ -70,9 +70,10 @@ class ScriptBeanDefinitionParser extends AbstractBeanDefinitionParser {
private static final String SCRIPT_INTERFACES_ATTRIBUTE = "script-interfaces"; private static final String SCRIPT_INTERFACES_ATTRIBUTE = "script-interfaces";
private static final String REFRESH_CHECK_DELAY_ATTRIBUTE = "refresh-check-delay"; private static final String REFRESH_CHECK_DELAY_ATTRIBUTE = "refresh-check-delay";
private static final String CUSTOMIZER_REF_ATTRIBUTE = "customizer-ref";
private static final String PROXY_TARGET_CLASS_ATTRIBUTE = "proxy-target-class";
private static final String CUSTOMIZER_REF_ATTRIBUTE = "customizer-ref";
/** /**
* The {@link org.springframework.scripting.ScriptFactory} class that this * The {@link org.springframework.scripting.ScriptFactory} class that this
@ -80,7 +81,6 @@ class ScriptBeanDefinitionParser extends AbstractBeanDefinitionParser {
*/ */
private final String scriptFactoryClassName; private final String scriptFactoryClassName;
/** /**
* Create a new instance of this parser, creating bean definitions for the * Create a new instance of this parser, creating bean definitions for the
* supplied {@link org.springframework.scripting.ScriptFactory} class. * supplied {@link org.springframework.scripting.ScriptFactory} class.
@ -90,7 +90,6 @@ class ScriptBeanDefinitionParser extends AbstractBeanDefinitionParser {
this.scriptFactoryClassName = scriptFactoryClassName; this.scriptFactoryClassName = scriptFactoryClassName;
} }
/** /**
* Parses the dynamic object element and returns the resulting bean definition. * Parses the dynamic object element and returns the resulting bean definition.
* Registers a {@link ScriptFactoryPostProcessor} if needed. * Registers a {@link ScriptFactoryPostProcessor} if needed.
@ -110,7 +109,8 @@ class ScriptBeanDefinitionParser extends AbstractBeanDefinitionParser {
GenericBeanDefinition bd = new GenericBeanDefinition(); GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClassName(this.scriptFactoryClassName); bd.setBeanClassName(this.scriptFactoryClassName);
bd.setSource(parserContext.extractSource(element)); bd.setSource(parserContext.extractSource(element));
bd.setAttribute(ScriptFactoryPostProcessor.LANGUAGE_ATTRIBUTE, element.getLocalName());
// Determine bean scope. // Determine bean scope.
String scope = element.getAttribute(SCOPE_ATTRIBUTE); String scope = element.getAttribute(SCOPE_ATTRIBUTE);
if (StringUtils.hasLength(scope)) { if (StringUtils.hasLength(scope)) {
@ -123,8 +123,7 @@ class ScriptBeanDefinitionParser extends AbstractBeanDefinitionParser {
// Only "byType" and "byName" supported, but maybe other default inherited... // Only "byType" and "byName" supported, but maybe other default inherited...
if (autowireMode == GenericBeanDefinition.AUTOWIRE_AUTODETECT) { if (autowireMode == GenericBeanDefinition.AUTOWIRE_AUTODETECT) {
autowireMode = GenericBeanDefinition.AUTOWIRE_BY_TYPE; autowireMode = GenericBeanDefinition.AUTOWIRE_BY_TYPE;
} } else if (autowireMode == GenericBeanDefinition.AUTOWIRE_CONSTRUCTOR) {
else if (autowireMode == GenericBeanDefinition.AUTOWIRE_CONSTRUCTOR) {
autowireMode = GenericBeanDefinition.AUTOWIRE_NO; autowireMode = GenericBeanDefinition.AUTOWIRE_NO;
} }
bd.setAutowireMode(autowireMode); bd.setAutowireMode(autowireMode);
@ -134,31 +133,34 @@ class ScriptBeanDefinitionParser extends AbstractBeanDefinitionParser {
bd.setDependencyCheck(parserContext.getDelegate().getDependencyCheck(dependencyCheck)); bd.setDependencyCheck(parserContext.getDelegate().getDependencyCheck(dependencyCheck));
// Retrieve the defaults for bean definitions within this parser context // Retrieve the defaults for bean definitions within this parser context
BeanDefinitionDefaults beanDefinitionDefaults = BeanDefinitionDefaults beanDefinitionDefaults = parserContext.getDelegate().getBeanDefinitionDefaults();
parserContext.getDelegate().getBeanDefinitionDefaults();
// Determine init method and destroy method. // Determine init method and destroy method.
String initMethod = element.getAttribute(INIT_METHOD_ATTRIBUTE); String initMethod = element.getAttribute(INIT_METHOD_ATTRIBUTE);
if (StringUtils.hasLength(initMethod)) { if (StringUtils.hasLength(initMethod)) {
bd.setInitMethodName(initMethod); bd.setInitMethodName(initMethod);
} } else if (beanDefinitionDefaults.getInitMethodName() != null) {
else if (beanDefinitionDefaults.getInitMethodName() != null) {
bd.setInitMethodName(beanDefinitionDefaults.getInitMethodName()); bd.setInitMethodName(beanDefinitionDefaults.getInitMethodName());
} }
String destroyMethod = element.getAttribute(DESTROY_METHOD_ATTRIBUTE); String destroyMethod = element.getAttribute(DESTROY_METHOD_ATTRIBUTE);
if (StringUtils.hasLength(destroyMethod)) { if (StringUtils.hasLength(destroyMethod)) {
bd.setDestroyMethodName(destroyMethod); bd.setDestroyMethodName(destroyMethod);
} } else if (beanDefinitionDefaults.getDestroyMethodName() != null) {
else if (beanDefinitionDefaults.getDestroyMethodName() != null) {
bd.setDestroyMethodName(beanDefinitionDefaults.getDestroyMethodName()); bd.setDestroyMethodName(beanDefinitionDefaults.getDestroyMethodName());
} }
// Attach any refresh metadata. // Attach any refresh metadata.
String refreshCheckDelay = element.getAttribute(REFRESH_CHECK_DELAY_ATTRIBUTE); String refreshCheckDelay = element.getAttribute(REFRESH_CHECK_DELAY_ATTRIBUTE);
if (StringUtils.hasText(refreshCheckDelay)) { if (StringUtils.hasText(refreshCheckDelay)) {
bd.setAttribute( bd.setAttribute(ScriptFactoryPostProcessor.REFRESH_CHECK_DELAY_ATTRIBUTE, new Long(refreshCheckDelay));
ScriptFactoryPostProcessor.REFRESH_CHECK_DELAY_ATTRIBUTE, new Long(refreshCheckDelay)); }
// Attach any proxy target class metadata.
String proxyTargetClass = element.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE);
if (StringUtils.hasText(proxyTargetClass)) {
Boolean flag = new Boolean(proxyTargetClass);
bd.setAttribute(ScriptFactoryPostProcessor.PROXY_TARGET_CLASS_ATTRIBUTE, flag);
} }
// Add constructor arguments. // Add constructor arguments.
@ -168,14 +170,13 @@ class ScriptBeanDefinitionParser extends AbstractBeanDefinitionParser {
if (element.hasAttribute(SCRIPT_INTERFACES_ATTRIBUTE)) { if (element.hasAttribute(SCRIPT_INTERFACES_ATTRIBUTE)) {
cav.addIndexedArgumentValue(constructorArgNum++, element.getAttribute(SCRIPT_INTERFACES_ATTRIBUTE)); cav.addIndexedArgumentValue(constructorArgNum++, element.getAttribute(SCRIPT_INTERFACES_ATTRIBUTE));
} }
// This is used for Groovy. It's a bean reference to a customizer bean. // This is used for Groovy. It's a bean reference to a customizer bean.
if (element.hasAttribute(CUSTOMIZER_REF_ATTRIBUTE)) { if (element.hasAttribute(CUSTOMIZER_REF_ATTRIBUTE)) {
String customizerBeanName = element.getAttribute(CUSTOMIZER_REF_ATTRIBUTE); String customizerBeanName = element.getAttribute(CUSTOMIZER_REF_ATTRIBUTE);
if (!StringUtils.hasText(customizerBeanName)) { if (!StringUtils.hasText(customizerBeanName)) {
parserContext.getReaderContext().error("Attribute 'customizer-ref' has empty value", element); parserContext.getReaderContext().error("Attribute 'customizer-ref' has empty value", element);
} } else {
else {
cav.addIndexedArgumentValue(constructorArgNum++, new RuntimeBeanReference(customizerBeanName)); cav.addIndexedArgumentValue(constructorArgNum++, new RuntimeBeanReference(customizerBeanName));
} }
} }
@ -197,15 +198,12 @@ class ScriptBeanDefinitionParser extends AbstractBeanDefinitionParser {
if (hasScriptSource && !elements.isEmpty()) { if (hasScriptSource && !elements.isEmpty()) {
readerContext.error("Only one of 'script-source' and 'inline-script' should be specified.", element); readerContext.error("Only one of 'script-source' and 'inline-script' should be specified.", element);
return null; return null;
} } else if (hasScriptSource) {
else if (hasScriptSource) {
return element.getAttribute(SCRIPT_SOURCE_ATTRIBUTE); return element.getAttribute(SCRIPT_SOURCE_ATTRIBUTE);
} } else if (!elements.isEmpty()) {
else if (!elements.isEmpty()) {
Element inlineElement = (Element) elements.get(0); Element inlineElement = (Element) elements.get(0);
return "inline:" + DomUtils.getTextValue(inlineElement); return "inline:" + DomUtils.getTextValue(inlineElement);
} } else {
else {
readerContext.error("Must specify either 'script-source' or 'inline-script'.", element); readerContext.error("Must specify either 'script-source' or 'inline-script'.", element);
return null; return null;
} }

View File

@ -19,6 +19,7 @@ package org.springframework.scripting.config;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext; import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -31,6 +32,8 @@ public class ScriptingDefaultsParser implements BeanDefinitionParser {
private static final String REFRESH_CHECK_DELAY_ATTRIBUTE = "refresh-check-delay"; private static final String REFRESH_CHECK_DELAY_ATTRIBUTE = "refresh-check-delay";
private static final String PROXY_TARGET_CLASS_ATTRIBUTE = "proxy-target-class";
public BeanDefinition parse(Element element, ParserContext parserContext) { public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinition bd = BeanDefinition bd =
@ -39,6 +42,10 @@ public class ScriptingDefaultsParser implements BeanDefinitionParser {
if (StringUtils.hasText(refreshCheckDelay)) { if (StringUtils.hasText(refreshCheckDelay)) {
bd.getPropertyValues().add("defaultRefreshCheckDelay", new Long(refreshCheckDelay)); bd.getPropertyValues().add("defaultRefreshCheckDelay", new Long(refreshCheckDelay));
} }
String proxyTargetClass = element.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE);
if (StringUtils.hasText(proxyTargetClass)) {
bd.getPropertyValues().add("defaultProxyTargetClass", new TypedStringValue(proxyTargetClass, Boolean.class));
}
return null; return null;
} }

View File

@ -45,6 +45,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionValidationException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ResourceLoaderAware; import org.springframework.context.ResourceLoaderAware;
@ -135,8 +136,8 @@ import org.springframework.util.StringUtils;
* @author Mark Fisher * @author Mark Fisher
* @since 2.0 * @since 2.0
*/ */
public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProcessorAdapter public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements
implements BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware, DisposableBean, Ordered { BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware, DisposableBean, Ordered {
/** /**
* The {@link org.springframework.core.io.Resource}-style prefix that denotes * The {@link org.springframework.core.io.Resource}-style prefix that denotes
@ -146,19 +147,26 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
*/ */
public static final String INLINE_SCRIPT_PREFIX = "inline:"; public static final String INLINE_SCRIPT_PREFIX = "inline:";
public static final String REFRESH_CHECK_DELAY_ATTRIBUTE = public static final String REFRESH_CHECK_DELAY_ATTRIBUTE = Conventions.getQualifiedAttributeName(
Conventions.getQualifiedAttributeName(ScriptFactoryPostProcessor.class, "refreshCheckDelay"); ScriptFactoryPostProcessor.class, "refreshCheckDelay");
public static final String PROXY_TARGET_CLASS_ATTRIBUTE = Conventions.getQualifiedAttributeName(
ScriptFactoryPostProcessor.class, "proxyTargetClass");
public static final String LANGUAGE_ATTRIBUTE = Conventions.getQualifiedAttributeName(
ScriptFactoryPostProcessor.class, "language");
private static final String SCRIPT_FACTORY_NAME_PREFIX = "scriptFactory."; private static final String SCRIPT_FACTORY_NAME_PREFIX = "scriptFactory.";
private static final String SCRIPTED_OBJECT_NAME_PREFIX = "scriptedObject."; private static final String SCRIPTED_OBJECT_NAME_PREFIX = "scriptedObject.";
/** Logger available to subclasses */ /** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
private long defaultRefreshCheckDelay = -1; private long defaultRefreshCheckDelay = -1;
private boolean defaultProxyTargetClass = false;
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private ConfigurableBeanFactory beanFactory; private ConfigurableBeanFactory beanFactory;
@ -170,7 +178,6 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
/** Map from bean name String to ScriptSource object */ /** Map from bean name String to ScriptSource object */
private final Map<String, ScriptSource> scriptSourceCache = new HashMap<String, ScriptSource>(); private final Map<String, ScriptSource> scriptSourceCache = new HashMap<String, ScriptSource>();
/** /**
* Set the delay between refresh checks, in milliseconds. * Set the delay between refresh checks, in milliseconds.
* Default is -1, indicating no refresh checks at all. * Default is -1, indicating no refresh checks at all.
@ -183,14 +190,22 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
this.defaultRefreshCheckDelay = defaultRefreshCheckDelay; this.defaultRefreshCheckDelay = defaultRefreshCheckDelay;
} }
/**
* Flag to signal that refreshable proxies should be created to proxy the target class not its interfaces.
* @param defaultProxyTargetClass the flag value to set
*/
public void setDefaultProxyTargetClass(boolean defaultProxyTargetClass) {
this.defaultProxyTargetClass = defaultProxyTargetClass;
}
public void setBeanClassLoader(ClassLoader classLoader) { public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader; this.beanClassLoader = classLoader;
} }
public void setBeanFactory(BeanFactory beanFactory) { public void setBeanFactory(BeanFactory beanFactory) {
if (!(beanFactory instanceof ConfigurableBeanFactory)) { if (!(beanFactory instanceof ConfigurableBeanFactory)) {
throw new IllegalStateException("ScriptFactoryPostProcessor doesn't work with a BeanFactory " + throw new IllegalStateException("ScriptFactoryPostProcessor doesn't work with a BeanFactory "
"which does not implement ConfigurableBeanFactory: " + beanFactory.getClass()); + "which does not implement ConfigurableBeanFactory: " + beanFactory.getClass());
} }
this.beanFactory = (ConfigurableBeanFactory) beanFactory; this.beanFactory = (ConfigurableBeanFactory) beanFactory;
@ -217,7 +232,6 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
return Integer.MIN_VALUE; return Integer.MIN_VALUE;
} }
@Override @Override
public Class predictBeanType(Class beanClass, String beanName) { public Class predictBeanType(Class beanClass, String beanName) {
// We only apply special treatment to ScriptFactory implementations here. // We only apply special treatment to ScriptFactory implementations here.
@ -233,18 +247,15 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
prepareScriptBeans(bd, scriptFactoryBeanName, scriptedObjectBeanName); prepareScriptBeans(bd, scriptFactoryBeanName, scriptedObjectBeanName);
ScriptFactory scriptFactory = this.scriptBeanFactory.getBean(scriptFactoryBeanName, ScriptFactory.class); ScriptFactory scriptFactory = this.scriptBeanFactory.getBean(scriptFactoryBeanName, ScriptFactory.class);
ScriptSource scriptSource = ScriptSource scriptSource = getScriptSource(scriptFactoryBeanName, scriptFactory.getScriptSourceLocator());
getScriptSource(scriptFactoryBeanName, scriptFactory.getScriptSourceLocator());
Class[] interfaces = scriptFactory.getScriptInterfaces(); Class[] interfaces = scriptFactory.getScriptInterfaces();
Class scriptedType = scriptFactory.getScriptedObjectType(scriptSource); Class scriptedType = scriptFactory.getScriptedObjectType(scriptSource);
if (scriptedType != null) { if (scriptedType != null) {
return scriptedType; return scriptedType;
} } else if (!ObjectUtils.isEmpty(interfaces)) {
else if (!ObjectUtils.isEmpty(interfaces)) {
return (interfaces.length == 1 ? interfaces[0] : createCompositeInterface(interfaces)); return (interfaces.length == 1 ? interfaces[0] : createCompositeInterface(interfaces));
} } else {
else {
if (bd.isSingleton()) { if (bd.isSingleton()) {
Object bean = this.scriptBeanFactory.getBean(scriptedObjectBeanName); Object bean = this.scriptBeanFactory.getBean(scriptedObjectBeanName);
if (bean != null) { if (bean != null) {
@ -252,15 +263,14 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
} }
} }
} }
} } catch (Exception ex) {
catch (Exception ex) { if (ex instanceof BeanCreationException
if (ex instanceof BeanCreationException && && ((BeanCreationException) ex).getMostSpecificCause() instanceof BeanCurrentlyInCreationException) {
((BeanCreationException) ex).getMostSpecificCause() instanceof BeanCurrentlyInCreationException) {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Could not determine scripted object type for bean '" + beanName + "': " + ex.getMessage()); logger.trace("Could not determine scripted object type for bean '" + beanName + "': "
+ ex.getMessage());
} }
} } else {
else {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Could not determine scripted object type for bean '" + beanName + "'", ex); logger.debug("Could not determine scripted object type for bean '" + beanName + "'", ex);
} }
@ -283,8 +293,7 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
prepareScriptBeans(bd, scriptFactoryBeanName, scriptedObjectBeanName); prepareScriptBeans(bd, scriptFactoryBeanName, scriptedObjectBeanName);
ScriptFactory scriptFactory = this.scriptBeanFactory.getBean(scriptFactoryBeanName, ScriptFactory.class); ScriptFactory scriptFactory = this.scriptBeanFactory.getBean(scriptFactoryBeanName, ScriptFactory.class);
ScriptSource scriptSource = ScriptSource scriptSource = getScriptSource(scriptFactoryBeanName, scriptFactory.getScriptSourceLocator());
getScriptSource(scriptFactoryBeanName, scriptFactory.getScriptSourceLocator());
boolean isFactoryBean = false; boolean isFactoryBean = false;
try { try {
Class scriptedObjectType = scriptFactory.getScriptedObjectType(scriptSource); Class scriptedObjectType = scriptFactory.getScriptedObjectType(scriptSource);
@ -292,19 +301,25 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
if (scriptedObjectType != null) { if (scriptedObjectType != null) {
isFactoryBean = FactoryBean.class.isAssignableFrom(scriptedObjectType); isFactoryBean = FactoryBean.class.isAssignableFrom(scriptedObjectType);
} }
} } catch (Exception ex) {
catch (Exception ex) { throw new BeanCreationException(beanName, "Could not determine scripted object type for " + scriptFactory,
throw new BeanCreationException( ex);
beanName, "Could not determine scripted object type for " + scriptFactory, ex);
} }
long refreshCheckDelay = resolveRefreshCheckDelay(bd); long refreshCheckDelay = resolveRefreshCheckDelay(bd);
if (refreshCheckDelay >= 0) { if (refreshCheckDelay >= 0) {
Class[] interfaces = scriptFactory.getScriptInterfaces(); Class[] interfaces = scriptFactory.getScriptInterfaces();
RefreshableScriptTargetSource ts = new RefreshableScriptTargetSource( RefreshableScriptTargetSource ts = new RefreshableScriptTargetSource(this.scriptBeanFactory,
this.scriptBeanFactory, scriptedObjectBeanName, scriptFactory, scriptSource, isFactoryBean); scriptedObjectBeanName, scriptFactory, scriptSource, isFactoryBean);
boolean proxyTargetClass = resolveProxyTargetClass(bd);
String language = (String) bd.getAttribute(LANGUAGE_ATTRIBUTE);
if (proxyTargetClass && (language==null || !language.equals("groovy"))) {
throw new BeanDefinitionValidationException(
"Cannot use proxyTargetClass=true with script beans where language is not groovy (found "
+ language + ")");
}
ts.setRefreshCheckDelay(refreshCheckDelay); ts.setRefreshCheckDelay(refreshCheckDelay);
return createRefreshableProxy(ts, interfaces); return createRefreshableProxy(ts, interfaces, proxyTargetClass);
} }
if (isFactoryBean) { if (isFactoryBean) {
@ -313,7 +328,6 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
return this.scriptBeanFactory.getBean(scriptedObjectBeanName); return this.scriptBeanFactory.getBean(scriptedObjectBeanName);
} }
/** /**
* Prepare the script beans in the internal BeanFactory that this * Prepare the script beans in the internal BeanFactory that this
* post-processor uses. Each original bean definition will be split * post-processor uses. Each original bean definition will be split
@ -322,18 +336,18 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
* @param scriptFactoryBeanName the name of the internal ScriptFactory bean * @param scriptFactoryBeanName the name of the internal ScriptFactory bean
* @param scriptedObjectBeanName the name of the internal scripted object bean * @param scriptedObjectBeanName the name of the internal scripted object bean
*/ */
protected void prepareScriptBeans( protected void prepareScriptBeans(BeanDefinition bd, String scriptFactoryBeanName, String scriptedObjectBeanName) {
BeanDefinition bd, String scriptFactoryBeanName, String scriptedObjectBeanName) {
// Avoid recreation of the script bean definition in case of a prototype. // Avoid recreation of the script bean definition in case of a prototype.
synchronized (this.scriptBeanFactory) { synchronized (this.scriptBeanFactory) {
if (!this.scriptBeanFactory.containsBeanDefinition(scriptedObjectBeanName)) { if (!this.scriptBeanFactory.containsBeanDefinition(scriptedObjectBeanName)) {
this.scriptBeanFactory.registerBeanDefinition( this.scriptBeanFactory.registerBeanDefinition(scriptFactoryBeanName,
scriptFactoryBeanName, createScriptFactoryBeanDefinition(bd)); createScriptFactoryBeanDefinition(bd));
ScriptFactory scriptFactory = this.scriptBeanFactory.getBean(scriptFactoryBeanName, ScriptFactory.class); ScriptFactory scriptFactory = this.scriptBeanFactory
ScriptSource scriptSource = .getBean(scriptFactoryBeanName, ScriptFactory.class);
getScriptSource(scriptFactoryBeanName, scriptFactory.getScriptSourceLocator()); ScriptSource scriptSource = getScriptSource(scriptFactoryBeanName,
scriptFactory.getScriptSourceLocator());
Class<?>[] interfaces = scriptFactory.getScriptInterfaces(); Class<?>[] interfaces = scriptFactory.getScriptInterfaces();
Class<?>[] scriptedInterfaces = interfaces; Class<?>[] scriptedInterfaces = interfaces;
@ -342,8 +356,8 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
scriptedInterfaces = ObjectUtils.addObjectToArray(interfaces, configInterface); scriptedInterfaces = ObjectUtils.addObjectToArray(interfaces, configInterface);
} }
BeanDefinition objectBd = createScriptedObjectBeanDefinition( BeanDefinition objectBd = createScriptedObjectBeanDefinition(bd, scriptFactoryBeanName, scriptSource,
bd, scriptFactoryBeanName, scriptSource, scriptedInterfaces); scriptedInterfaces);
long refreshCheckDelay = resolveRefreshCheckDelay(bd); long refreshCheckDelay = resolveRefreshCheckDelay(bd);
if (refreshCheckDelay >= 0) { if (refreshCheckDelay >= 0) {
objectBd.setScope(BeanDefinition.SCOPE_PROTOTYPE); objectBd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
@ -369,18 +383,31 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
Object attributeValue = beanDefinition.getAttribute(REFRESH_CHECK_DELAY_ATTRIBUTE); Object attributeValue = beanDefinition.getAttribute(REFRESH_CHECK_DELAY_ATTRIBUTE);
if (attributeValue instanceof Number) { if (attributeValue instanceof Number) {
refreshCheckDelay = ((Number) attributeValue).longValue(); refreshCheckDelay = ((Number) attributeValue).longValue();
} } else if (attributeValue instanceof String) {
else if (attributeValue instanceof String) {
refreshCheckDelay = Long.parseLong((String) attributeValue); refreshCheckDelay = Long.parseLong((String) attributeValue);
} } else if (attributeValue != null) {
else if (attributeValue != null) { throw new BeanDefinitionStoreException("Invalid refresh check delay attribute ["
throw new BeanDefinitionStoreException( + REFRESH_CHECK_DELAY_ATTRIBUTE + "] with value [" + attributeValue
"Invalid refresh check delay attribute [" + REFRESH_CHECK_DELAY_ATTRIBUTE + + "]: needs to be of type Number or String");
"] with value [" + attributeValue + "]: needs to be of type Number or String");
} }
return refreshCheckDelay; return refreshCheckDelay;
} }
protected boolean resolveProxyTargetClass(BeanDefinition beanDefinition) {
boolean proxyTargetClass = this.defaultProxyTargetClass;
Object attributeValue = beanDefinition.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE);
if (attributeValue instanceof Boolean) {
proxyTargetClass = ((Boolean) attributeValue).booleanValue();
} else if (attributeValue instanceof String) {
proxyTargetClass = new Boolean((String) attributeValue);
} else if (attributeValue != null) {
throw new BeanDefinitionStoreException("Invalid refresh check delay attribute ["
+ REFRESH_CHECK_DELAY_ATTRIBUTE + "] with value [" + attributeValue
+ "]: needs to be of type Number or String");
}
return proxyTargetClass;
}
/** /**
* Create a ScriptFactory bean definition based on the given script definition, * Create a ScriptFactory bean definition based on the given script definition,
* extracting only the definition data that is relevant for the ScriptFactory * extracting only the definition data that is relevant for the ScriptFactory
@ -425,13 +452,12 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
* @param resourceLoader the ResourceLoader to use (if necessary) * @param resourceLoader the ResourceLoader to use (if necessary)
* @return the ScriptSource instance * @return the ScriptSource instance
*/ */
protected ScriptSource convertToScriptSource( protected ScriptSource convertToScriptSource(String beanName, String scriptSourceLocator,
String beanName, String scriptSourceLocator, ResourceLoader resourceLoader) { ResourceLoader resourceLoader) {
if (scriptSourceLocator.startsWith(INLINE_SCRIPT_PREFIX)) { if (scriptSourceLocator.startsWith(INLINE_SCRIPT_PREFIX)) {
return new StaticScriptSource(scriptSourceLocator.substring(INLINE_SCRIPT_PREFIX.length()), beanName); return new StaticScriptSource(scriptSourceLocator.substring(INLINE_SCRIPT_PREFIX.length()), beanName);
} } else {
else {
return new ResourceScriptSource(resourceLoader.getResource(scriptSourceLocator)); return new ResourceScriptSource(resourceLoader.getResource(scriptSourceLocator));
} }
} }
@ -457,7 +483,7 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
String propertyName = pv.getName(); String propertyName = pv.getName();
Class propertyType = BeanUtils.findPropertyType(propertyName, interfaces); Class propertyType = BeanUtils.findPropertyType(propertyName, interfaces);
String setterName = "set" + StringUtils.capitalize(propertyName); String setterName = "set" + StringUtils.capitalize(propertyName);
Signature signature = new Signature(setterName, Type.VOID_TYPE, new Type[] {Type.getType(propertyType)}); Signature signature = new Signature(setterName, Type.VOID_TYPE, new Type[] { Type.getType(propertyType) });
maker.add(signature, new Type[0]); maker.add(signature, new Type[0]);
} }
if (bd instanceof AbstractBeanDefinition) { if (bd instanceof AbstractBeanDefinition) {
@ -498,8 +524,8 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
* @return the extracted ScriptFactory bean definition * @return the extracted ScriptFactory bean definition
* @see org.springframework.scripting.ScriptFactory#getScriptedObject * @see org.springframework.scripting.ScriptFactory#getScriptedObject
*/ */
protected BeanDefinition createScriptedObjectBeanDefinition( protected BeanDefinition createScriptedObjectBeanDefinition(BeanDefinition bd, String scriptFactoryBeanName,
BeanDefinition bd, String scriptFactoryBeanName, ScriptSource scriptSource, Class[] interfaces) { ScriptSource scriptSource, Class[] interfaces) {
GenericBeanDefinition objectBd = new GenericBeanDefinition(bd); GenericBeanDefinition objectBd = new GenericBeanDefinition(bd);
objectBd.setFactoryBeanName(scriptFactoryBeanName); objectBd.setFactoryBeanName(scriptFactoryBeanName);
@ -518,23 +544,27 @@ public class ScriptFactoryPostProcessor extends InstantiationAwareBeanPostProces
* @return the generated proxy * @return the generated proxy
* @see RefreshableScriptTargetSource * @see RefreshableScriptTargetSource
*/ */
protected Object createRefreshableProxy(TargetSource ts, Class[] interfaces) { protected Object createRefreshableProxy(TargetSource ts, Class[] interfaces, boolean proxyTargetClass) {
ProxyFactory proxyFactory = new ProxyFactory(); ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTargetSource(ts); proxyFactory.setTargetSource(ts);
ClassLoader classLoader = this.beanClassLoader;
if (interfaces == null) { if (interfaces == null) {
interfaces = ClassUtils.getAllInterfacesForClass(ts.getTargetClass(), this.beanClassLoader); interfaces = ClassUtils.getAllInterfacesForClass(ts.getTargetClass(), this.beanClassLoader);
} }
proxyFactory.setInterfaces(interfaces); proxyFactory.setInterfaces(interfaces);
if (proxyTargetClass) {
classLoader = null; // Force use of Class.getClassLoader()
proxyFactory.setProxyTargetClass(proxyTargetClass);
}
DelegatingIntroductionInterceptor introduction = new DelegatingIntroductionInterceptor(ts); DelegatingIntroductionInterceptor introduction = new DelegatingIntroductionInterceptor(ts);
introduction.suppressInterface(TargetSource.class); introduction.suppressInterface(TargetSource.class);
proxyFactory.addAdvice(introduction); proxyFactory.addAdvice(introduction);
return proxyFactory.getProxy(this.beanClassLoader); return proxyFactory.getProxy(classLoader);
} }
/** /**
* Destroy the inner bean factory (used for scripts) on shutdown. * Destroy the inner bean factory (used for scripts) on shutdown.
*/ */

View File

@ -30,28 +30,49 @@
</xsd:complexType> </xsd:complexType>
</xsd:element> </xsd:element>
<xsd:element name="groovy" type="customizableScriptType"> <xsd:element name="groovy">
<xsd:annotation> <xsd:annotation>
<xsd:documentation><![CDATA[ <xsd:documentation><![CDATA[
A Spring bean backed by a Groovy class definition. A Spring bean backed by a Groovy class definition.
]]></xsd:documentation> ]]></xsd:documentation>
</xsd:annotation> </xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="customizableScriptType">
<xsd:attributeGroup ref="defaultableAttributes"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element> </xsd:element>
<xsd:element name="jruby" type="dynamicScriptType"> <xsd:element name="jruby">
<xsd:annotation> <xsd:annotation>
<xsd:documentation><![CDATA[ <xsd:documentation><![CDATA[
A Spring bean backed by a JRuby class definition. A Spring bean backed by a JRuby class definition.
]]></xsd:documentation> ]]></xsd:documentation>
</xsd:annotation> </xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="dynamicScriptType">
<xsd:attributeGroup ref="vanillaScriptAttributes"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element> </xsd:element>
<xsd:element name="bsh" type="dynamicScriptType"> <xsd:element name="bsh">
<xsd:annotation> <xsd:annotation>
<xsd:documentation><![CDATA[ <xsd:documentation><![CDATA[
A Spring bean backed by a BeanShell script. A Spring bean backed by a BeanShell script.
]]></xsd:documentation> ]]></xsd:documentation>
</xsd:annotation> </xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="dynamicScriptType">
<xsd:attributeGroup ref="vanillaScriptAttributes"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element> </xsd:element>
<!-- Script Types --> <!-- Script Types -->
@ -77,7 +98,6 @@
</xsd:annotation> </xsd:annotation>
</xsd:element> </xsd:element>
</xsd:sequence> </xsd:sequence>
<xsd:attributeGroup ref="defaultableAttributes"/>
<xsd:attribute name="script-source" type="xsd:string"> <xsd:attribute name="script-source" type="xsd:string">
<xsd:annotation> <xsd:annotation>
<xsd:documentation source="java:org.springframework.core.io.Resource"><![CDATA[ <xsd:documentation source="java:org.springframework.core.io.Resource"><![CDATA[
@ -185,7 +205,7 @@
</xsd:complexContent> </xsd:complexContent>
</xsd:complexType> </xsd:complexType>
<xsd:attributeGroup name="defaultableAttributes"> <xsd:attributeGroup name="vanillaScriptAttributes">
<xsd:attribute name="refresh-check-delay" type="xsd:long"> <xsd:attribute name="refresh-check-delay" type="xsd:long">
<xsd:annotation> <xsd:annotation>
<xsd:documentation><![CDATA[ <xsd:documentation><![CDATA[
@ -196,4 +216,18 @@
</xsd:attribute> </xsd:attribute>
</xsd:attributeGroup> </xsd:attributeGroup>
<xsd:attributeGroup name="defaultableAttributes">
<xsd:attribute name="proxy-target-class" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation><![CDATA[
Flag to tell the bean factory that if this bean is proxied it should be done using the target class type,
not its interfaces. A refreshable script is normally proxied, so often this is useful in conjunction with
refresh-check-delay. Defaults to false requiring no additional library dependencies, but hiding behaviour in the
bean that is not defined in an interface.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="vanillaScriptAttributes"></xsd:attributeGroup>
</xsd:attributeGroup>
</xsd:schema> </xsd:schema>

View File

@ -21,6 +21,7 @@ import java.lang.reflect.Field;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.springframework.aop.framework.Advised; import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.target.dynamic.AbstractRefreshableTargetSource; import org.springframework.aop.target.dynamic.AbstractRefreshableTargetSource;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
@ -32,7 +33,10 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ScriptingDefaultsTests extends TestCase { public class ScriptingDefaultsTests extends TestCase {
private static final String CONFIG = private static final String CONFIG =
"org/springframework/scripting/config/scriptingDefaultsTests.xml"; "org/springframework/scripting/config/scriptingDefaultsTests.xml";
private static final String PROXY_CONFIG =
"org/springframework/scripting/config/scriptingDefaultsProxyTargetClassTests.xml";
public void testDefaultRefreshCheckDelay() throws Exception { public void testDefaultRefreshCheckDelay() throws Exception {
@ -73,4 +77,10 @@ public class ScriptingDefaultsTests extends TestCase {
assertEquals(otherBean, testBean.getOtherBean()); assertEquals(otherBean, testBean.getOtherBean());
} }
public void testDefaultProxyTargetClass() {
ApplicationContext context = new ClassPathXmlApplicationContext(PROXY_CONFIG);
Object testBean = context.getBean("testBean");
assertTrue(AopUtils.isCglibProxy(testBean));
}
} }

View File

@ -0,0 +1,37 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.scripting.groovy;
import org.springframework.scripting.ConfigurableMessenger;
/**
* @author Dave Syer
*
*/
public class ConcreteMessenger implements ConfigurableMessenger {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -16,7 +16,13 @@
package org.springframework.scripting.groovy; package org.springframework.scripting.groovy;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import groovy.lang.DelegatingMetaClass; import groovy.lang.DelegatingMetaClass;
import groovy.lang.GroovyObject; import groovy.lang.GroovyObject;
@ -178,8 +184,7 @@ public class GroovyScriptFactoryTests {
try { try {
new ClassPathXmlApplicationContext("org/springframework/scripting/groovy/groovyBrokenContext.xml"); new ClassPathXmlApplicationContext("org/springframework/scripting/groovy/groovyBrokenContext.xml");
fail("Should throw exception for broken script file"); fail("Should throw exception for broken script file");
} } catch (NestedRuntimeException ex) {
catch (NestedRuntimeException ex) {
assertTrue("Wrong root cause: " + ex, ex.contains(ScriptCompilationException.class)); assertTrue("Wrong root cause: " + ex, ex.contains(ScriptCompilationException.class));
} }
} }
@ -194,12 +199,12 @@ public class GroovyScriptFactoryTests {
script.suggestedClassName(); script.suggestedClassName();
mock.setReturnValue("someName"); mock.setReturnValue("someName");
mock.replay(); mock.replay();
GroovyScriptFactory factory = new GroovyScriptFactory(ScriptFactoryPostProcessor.INLINE_SCRIPT_PREFIX + badScript); GroovyScriptFactory factory = new GroovyScriptFactory(ScriptFactoryPostProcessor.INLINE_SCRIPT_PREFIX
+ badScript);
try { try {
factory.getScriptedObject(script, new Class[]{}); factory.getScriptedObject(script, new Class[] {});
fail("Must have thrown a ScriptCompilationException (no public no-arg ctor in scripted class)."); fail("Must have thrown a ScriptCompilationException (no public no-arg ctor in scripted class).");
} } catch (ScriptCompilationException expected) {
catch (ScriptCompilationException expected) {
assertTrue(expected.contains(InstantiationException.class)); assertTrue(expected.contains(InstantiationException.class));
} }
mock.verify(); mock.verify();
@ -215,12 +220,12 @@ public class GroovyScriptFactoryTests {
script.suggestedClassName(); script.suggestedClassName();
mock.setReturnValue("someName"); mock.setReturnValue("someName");
mock.replay(); mock.replay();
GroovyScriptFactory factory = new GroovyScriptFactory(ScriptFactoryPostProcessor.INLINE_SCRIPT_PREFIX + badScript); GroovyScriptFactory factory = new GroovyScriptFactory(ScriptFactoryPostProcessor.INLINE_SCRIPT_PREFIX
+ badScript);
try { try {
factory.getScriptedObject(script, new Class[]{}); factory.getScriptedObject(script, new Class[] {});
fail("Must have thrown a ScriptCompilationException (no oublic no-arg ctor in scripted class)."); fail("Must have thrown a ScriptCompilationException (no oublic no-arg ctor in scripted class).");
} } catch (ScriptCompilationException expected) {
catch (ScriptCompilationException expected) {
assertTrue(expected.contains(IllegalAccessException.class)); assertTrue(expected.contains(IllegalAccessException.class));
} }
mock.verify(); mock.verify();
@ -254,8 +259,7 @@ public class GroovyScriptFactoryTests {
try { try {
new GroovyScriptFactory(null); new GroovyScriptFactory(null);
fail("Must have thrown exception by this point."); fail("Must have thrown exception by this point.");
} } catch (IllegalArgumentException expected) {
catch (IllegalArgumentException expected) {
} }
} }
@ -264,8 +268,7 @@ public class GroovyScriptFactoryTests {
try { try {
new GroovyScriptFactory(""); new GroovyScriptFactory("");
fail("Must have thrown exception by this point."); fail("Must have thrown exception by this point.");
} } catch (IllegalArgumentException expected) {
catch (IllegalArgumentException expected) {
} }
} }
@ -274,8 +277,7 @@ public class GroovyScriptFactoryTests {
try { try {
new GroovyScriptFactory("\n "); new GroovyScriptFactory("\n ");
fail("Must have thrown exception by this point."); fail("Must have thrown exception by this point.");
} } catch (IllegalArgumentException expected) {
catch (IllegalArgumentException expected) {
} }
} }
@ -284,8 +286,7 @@ public class GroovyScriptFactoryTests {
try { try {
new ClassPathXmlApplicationContext("lwspBadGroovyContext.xml", getClass()); new ClassPathXmlApplicationContext("lwspBadGroovyContext.xml", getClass());
fail("Must have thrown a BeanCreationException ('inline:' prefix was preceded by whitespace"); fail("Must have thrown a BeanCreationException ('inline:' prefix was preceded by whitespace");
} } catch (BeanCreationException expected) {
catch (BeanCreationException expected) {
assertTrue(expected.contains(FileNotFoundException.class)); assertTrue(expected.contains(FileNotFoundException.class));
} }
} }
@ -312,12 +313,12 @@ public class GroovyScriptFactoryTests {
try { try {
factory.getScriptedObject(null, null); factory.getScriptedObject(null, null);
fail("Must have thrown a NullPointerException as per contract ('null' ScriptSource supplied"); fail("Must have thrown a NullPointerException as per contract ('null' ScriptSource supplied");
} } catch (NullPointerException expected) {
catch (NullPointerException expected) {
} }
} }
@Ignore // see http://build.springframework.org/browse/SPR-TRUNKQUICK-908 @Ignore
// see http://build.springframework.org/browse/SPR-TRUNKQUICK-908
@Test @Test
public void testResourceScriptFromTag() throws Exception { public void testResourceScriptFromTag() throws Exception {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("groovy-with-xsd.xml", getClass()); ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("groovy-with-xsd.xml", getClass());
@ -375,6 +376,32 @@ public class GroovyScriptFactoryTests {
assertTrue(ctx.getBeansOfType(Messenger.class).values().contains(messenger)); assertTrue(ctx.getBeansOfType(Messenger.class).values().contains(messenger));
} }
@Test
// Test for SPR-6268
public void testRefreshableFromTagProxyTargetClass() throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("groovy-with-xsd-proxy-target-class.xml",
getClass());
assertTrue(Arrays.asList(ctx.getBeanNamesForType(Messenger.class)).contains("refreshableMessenger"));
Messenger messenger = (Messenger) ctx.getBean("refreshableMessenger");
assertTrue(AopUtils.isAopProxy(messenger));
assertTrue(messenger instanceof Refreshable);
assertEquals("Hello World!", messenger.getMessage());
assertTrue(ctx.getBeansOfType(ConcreteMessenger.class).values().contains(messenger));
}
@Test
// Test for SPR-6268
public void testProxyTargetClassNotAllowedIfNotGroovy() throws Exception {
try {
new ClassPathXmlApplicationContext("jruby-with-xsd-proxy-target-class.xml", getClass());
} catch (BeanCreationException e) {
assertTrue(e.getMessage().contains("Cannot use proxyTargetClass=true"));
}
}
@Test @Test
public void testAnonymousScriptDetected() throws Exception { public void testAnonymousScriptDetected() throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("groovy-with-xsd.xml", getClass()); ApplicationContext ctx = new ClassPathXmlApplicationContext("groovy-with-xsd.xml", getClass());
@ -404,8 +431,7 @@ public class GroovyScriptFactoryTests {
try { try {
ctx.getBean("bean3"); ctx.getBean("bean3");
fail("Should have thrown BeanCreationException"); fail("Should have thrown BeanCreationException");
} } catch (BeanCreationException ex) {
catch (BeanCreationException ex) {
// expected // expected
assertTrue(ex.contains(UnsatisfiedDependencyException.class)); assertTrue(ex.contains(UnsatisfiedDependencyException.class));
} }
@ -424,8 +450,7 @@ public class GroovyScriptFactoryTests {
private void testMetaClass(final String xmlFile) { private void testMetaClass(final String xmlFile) {
// expect the exception we threw in the custom metaclass to show it got invoked // expect the exception we threw in the custom metaclass to show it got invoked
try { try {
ApplicationContext ctx = ApplicationContext ctx = new ClassPathXmlApplicationContext(xmlFile);
new ClassPathXmlApplicationContext(xmlFile);
Calculator calc = (Calculator) ctx.getBean("delegatingCalculator"); Calculator calc = (Calculator) ctx.getBean("delegatingCalculator");
calc.add(1, 2); calc.add(1, 2);
fail("expected IllegalStateException"); fail("expected IllegalStateException");
@ -454,7 +479,6 @@ public class GroovyScriptFactoryTests {
assertEquals("test", result); assertEquals("test", result);
} }
public static class TestCustomizer implements GroovyObjectCustomizer { public static class TestCustomizer implements GroovyObjectCustomizer {
public void customize(GroovyObject goo) { public void customize(GroovyObject goo) {
@ -462,8 +486,7 @@ public class GroovyScriptFactoryTests {
public Object invokeMethod(Object arg0, String mName, Object[] arg2) { public Object invokeMethod(Object arg0, String mName, Object[] arg2) {
if (mName.indexOf("Missing") != -1) { if (mName.indexOf("Missing") != -1) {
throw new IllegalStateException("Gotcha"); throw new IllegalStateException("Gotcha");
} } else {
else {
return super.invokeMethod(arg0, mName, arg2); return super.invokeMethod(arg0, mName, arg2);
} }
} }

View File

@ -2,7 +2,5 @@ package org.springframework.scripting.groovy;
import org.springframework.scripting.ConfigurableMessenger import org.springframework.scripting.ConfigurableMessenger
class GroovyMessenger implements ConfigurableMessenger { class GroovyMessenger extends ConcreteMessenger {
def String message;
} }

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.1.xsd">
<lang:groovy id="refreshableMessenger" refresh-check-delay="5000" proxy-target-class="true"
script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
<lang:property name="message" value="Hello World!" />
</lang:groovy>
</beans>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.1.xsd">
<lang:defaults proxy-target-class="true"/>
<lang:jruby id="refreshableMessenger" refresh-check-delay="1000"
script-source="classpath:org/springframework/scripting/jruby/Messenger.rb"
script-interfaces="org.springframework.scripting.Messenger">
<lang:property name="message" value="Hello World!"/>
</lang:jruby>
</beans>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang
http://www.springframework.org/schema/lang/spring-lang-3.1.xsd"
default-autowire="byName"
default-init-method="startup"
default-destroy-method="shutdown">
<lang:defaults refresh-check-delay="5000" proxy-target-class="true"/>
<lang:groovy id="testBean" name="/url" script-source="classpath:org/springframework/scripting/config/TestBean.groovy"/>
</beans>