From 6a0d9d3d97820c47d13cc296b08b6c06a51cfbcd Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 17 Aug 2016 11:29:49 +0200 Subject: [PATCH] Consistent support for CompilationCustomizers as well as custom CompilerConfiguration Issue: SPR-14585 --- .../groovy/GroovyScriptEvaluator.java | 38 ++++++++++++++++++- .../scripting/groovy/GroovyScriptFactory.java | 27 ++++++++++--- .../groovy/GroovyScriptEvaluatorTests.java | 21 ++++++++++ .../groovy/GroovyScriptFactoryTests.java | 2 + .../scripting/groovy/MyBytecodeProcessor.java | 37 ++++++++++++++++++ .../scripting/groovy/groovy-with-xsd.xml | 13 +++++-- 6 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 spring-context/src/test/java/org/springframework/scripting/groovy/MyBytecodeProcessor.java diff --git a/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptEvaluator.java b/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptEvaluator.java index 3bcf53f08bd..e94b9d400ad 100644 --- a/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptEvaluator.java +++ b/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -22,6 +22,8 @@ import java.util.Map; import groovy.lang.Binding; import groovy.lang.GroovyRuntimeException; import groovy.lang.GroovyShell; +import org.codehaus.groovy.control.CompilerConfiguration; +import org.codehaus.groovy.control.customizers.CompilationCustomizer; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.scripting.ScriptCompilationException; @@ -40,6 +42,8 @@ public class GroovyScriptEvaluator implements ScriptEvaluator, BeanClassLoaderAw private ClassLoader classLoader; + private CompilerConfiguration compilerConfiguration = new CompilerConfiguration(); + /** * Construct a new GroovyScriptEvaluator. @@ -56,6 +60,35 @@ public class GroovyScriptEvaluator implements ScriptEvaluator, BeanClassLoaderAw } + /** + * Set a custom compiler configuration for this evaluator. + * @since 4.3.3 + * @see #setCompilationCustomizers + */ + public void setCompilerConfiguration(CompilerConfiguration compilerConfiguration) { + this.compilerConfiguration = + (compilerConfiguration != null ? compilerConfiguration : new CompilerConfiguration()); + } + + /** + * Return this evaluator's compiler configuration (never {@code null}). + * @since 4.3.3 + * @see #setCompilerConfiguration + */ + public CompilerConfiguration getCompilerConfiguration() { + return this.compilerConfiguration; + } + + /** + * Set one or more customizers to be applied to this evaluator's compiler configuration. + *

Note that this modifies the shared compiler configuration held by this evaluator. + * @since 4.3.3 + * @see #setCompilerConfiguration + */ + public void setCompilationCustomizers(CompilationCustomizer... compilationCustomizers) { + this.compilerConfiguration.addCompilationCustomizers(compilationCustomizers); + } + @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; @@ -69,7 +102,8 @@ public class GroovyScriptEvaluator implements ScriptEvaluator, BeanClassLoaderAw @Override public Object evaluate(ScriptSource script, Map arguments) { - GroovyShell groovyShell = new GroovyShell(this.classLoader, new Binding(arguments)); + GroovyShell groovyShell = new GroovyShell( + this.classLoader, new Binding(arguments), this.compilerConfiguration); try { String filename = (script instanceof ResourceScriptSource ? ((ResourceScriptSource) script).getResource().getFilename() : null); diff --git a/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java b/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java index cbade53fbbe..4155b3f986b 100644 --- a/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java +++ b/spring-context/src/main/java/org/springframework/scripting/groovy/GroovyScriptFactory.java @@ -36,6 +36,7 @@ import org.springframework.scripting.ScriptFactory; import org.springframework.scripting.ScriptSource; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; /** @@ -104,23 +105,39 @@ public class GroovyScriptFactory implements ScriptFactory, BeanFactoryAware, Bea this.groovyObjectCustomizer = groovyObjectCustomizer; } + /** + * Create a new GroovyScriptFactory for the given script source, + * specifying a strategy interface that can create a custom MetaClass + * to supply missing methods and otherwise change the behavior of the object. + * @param scriptSourceLocator a locator that points to the source of the script. + * Interpreted by the post-processor that actually creates the script. + * @param compilerConfiguration a custom compiler configuration to be applied + * to the GroovyClassLoader (may be {@code null}) + * @since 4.3.3 + * @see GroovyClassLoader#GroovyClassLoader(ClassLoader, CompilerConfiguration) + */ + public GroovyScriptFactory(String scriptSourceLocator, CompilerConfiguration compilerConfiguration) { + this(scriptSourceLocator); + this.compilerConfiguration = compilerConfiguration; + } + /** * Create a new GroovyScriptFactory for the given script source, * specifying a strategy interface that can customize Groovy's compilation * process within the underlying GroovyClassLoader. * @param scriptSourceLocator a locator that points to the source of the script. * Interpreted by the post-processor that actually creates the script. - * @param compilationCustomizer a customizer to be applied to the GroovyClassLoader - * compiler configuration (may be {@code null}) + * @param compilationCustomizers one or more customizers to be applied to the + * GroovyClassLoader compiler configuration * @since 4.3.3 * @see CompilerConfiguration#addCompilationCustomizers * @see org.codehaus.groovy.control.customizers.ImportCustomizer */ - public GroovyScriptFactory(String scriptSourceLocator, CompilationCustomizer compilationCustomizer) { + public GroovyScriptFactory(String scriptSourceLocator, CompilationCustomizer... compilationCustomizers) { this(scriptSourceLocator); - if (compilationCustomizer != null) { + if (!ObjectUtils.isEmpty(compilationCustomizers)) { this.compilerConfiguration = new CompilerConfiguration(); - this.compilerConfiguration.addCompilationCustomizers(compilationCustomizer); + this.compilerConfiguration.addCompilationCustomizers(compilationCustomizers); } } diff --git a/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptEvaluatorTests.java b/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptEvaluatorTests.java index 615458c7702..6e6b6511ce2 100644 --- a/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptEvaluatorTests.java +++ b/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptEvaluatorTests.java @@ -19,6 +19,7 @@ package org.springframework.scripting.groovy; import java.util.HashMap; import java.util.Map; +import org.codehaus.groovy.control.customizers.ImportCustomizer; import org.junit.Test; import org.springframework.core.io.ClassPathResource; @@ -58,6 +59,26 @@ public class GroovyScriptEvaluatorTests { assertEquals(6, result); } + @Test + public void testGroovyScriptWithCompilerConfiguration() { + GroovyScriptEvaluator evaluator = new GroovyScriptEvaluator(); + MyBytecodeProcessor processor = new MyBytecodeProcessor(); + evaluator.getCompilerConfiguration().setBytecodePostprocessor(processor); + Object result = evaluator.evaluate(new StaticScriptSource("return 3 * 2")); + assertEquals(6, result); + assertTrue(processor.processed.contains("Script1")); + } + + @Test + public void testGroovyScriptWithImportCustomizer() { + GroovyScriptEvaluator evaluator = new GroovyScriptEvaluator(); + ImportCustomizer importCustomizer = new ImportCustomizer(); + importCustomizer.addStarImports("org.springframework.util"); + evaluator.setCompilationCustomizers(importCustomizer); + Object result = evaluator.evaluate(new StaticScriptSource("return ResourceUtils.CLASSPATH_URL_PREFIX")); + assertEquals("classpath:", result); + } + @Test public void testGroovyScriptFromStringUsingJsr223() { StandardScriptEvaluator evaluator = new StandardScriptEvaluator(); diff --git a/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java b/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java index ec2214629e8..24ef4c75378 100644 --- a/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java +++ b/spring-context/src/test/java/org/springframework/scripting/groovy/GroovyScriptFactoryTests.java @@ -470,6 +470,8 @@ public class GroovyScriptFactoryTests { ApplicationContext ctx = new ClassPathXmlApplicationContext("groovy-with-xsd.xml", getClass()); Map beans = ctx.getBeansOfType(Messenger.class); assertEquals(4, beans.size()); + assertTrue(ctx.getBean(MyBytecodeProcessor.class).processed.contains( + "org.springframework.scripting.groovy.GroovyMessenger2")); } @Test diff --git a/spring-context/src/test/java/org/springframework/scripting/groovy/MyBytecodeProcessor.java b/spring-context/src/test/java/org/springframework/scripting/groovy/MyBytecodeProcessor.java new file mode 100644 index 00000000000..12a99139a37 --- /dev/null +++ b/spring-context/src/test/java/org/springframework/scripting/groovy/MyBytecodeProcessor.java @@ -0,0 +1,37 @@ +/* + * Copyright 2002-2016 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 java.util.HashSet; +import java.util.Set; + +import org.codehaus.groovy.control.BytecodeProcessor; + +/** + * @author Juergen Hoeller + */ +public class MyBytecodeProcessor implements BytecodeProcessor { + + public final Set processed = new HashSet(); + + @Override + public byte[] processBytecode(String name, byte[] original) { + this.processed.add(name); + return original; + } + +} diff --git a/spring-context/src/test/resources/org/springframework/scripting/groovy/groovy-with-xsd.xml b/spring-context/src/test/resources/org/springframework/scripting/groovy/groovy-with-xsd.xml index 648464cceca..070a64d03fe 100644 --- a/spring-context/src/test/resources/org/springframework/scripting/groovy/groovy-with-xsd.xml +++ b/spring-context/src/test/resources/org/springframework/scripting/groovy/groovy-with-xsd.xml @@ -39,8 +39,8 @@ class GroovyCalculator implements Calculator { @@ -53,8 +53,15 @@ public class TestCustomizer implements GroovyObjectCustomizer { - + + + + + + +