001/*
002 * Copyright 2002-2017 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.scripting.groovy;
018
019import java.io.IOException;
020import java.util.Map;
021
022import groovy.lang.Binding;
023import groovy.lang.GroovyRuntimeException;
024import groovy.lang.GroovyShell;
025import org.codehaus.groovy.control.CompilerConfiguration;
026import org.codehaus.groovy.control.customizers.CompilationCustomizer;
027
028import org.springframework.beans.factory.BeanClassLoaderAware;
029import org.springframework.lang.Nullable;
030import org.springframework.scripting.ScriptCompilationException;
031import org.springframework.scripting.ScriptEvaluator;
032import org.springframework.scripting.ScriptSource;
033import org.springframework.scripting.support.ResourceScriptSource;
034
035/**
036 * Groovy-based implementation of Spring's {@link ScriptEvaluator} strategy interface.
037 *
038 * @author Juergen Hoeller
039 * @since 4.0
040 * @see GroovyShell#evaluate(String, String)
041 */
042public class GroovyScriptEvaluator implements ScriptEvaluator, BeanClassLoaderAware {
043
044        @Nullable
045        private ClassLoader classLoader;
046
047        private CompilerConfiguration compilerConfiguration = new CompilerConfiguration();
048
049
050        /**
051         * Construct a new GroovyScriptEvaluator.
052         */
053        public GroovyScriptEvaluator() {
054        }
055
056        /**
057         * Construct a new GroovyScriptEvaluator.
058         * @param classLoader the ClassLoader to use as a parent for the {@link GroovyShell}
059         */
060        public GroovyScriptEvaluator(@Nullable ClassLoader classLoader) {
061                this.classLoader = classLoader;
062        }
063
064
065        /**
066         * Set a custom compiler configuration for this evaluator.
067         * @since 4.3.3
068         * @see #setCompilationCustomizers
069         */
070        public void setCompilerConfiguration(@Nullable CompilerConfiguration compilerConfiguration) {
071                this.compilerConfiguration =
072                                (compilerConfiguration != null ? compilerConfiguration : new CompilerConfiguration());
073        }
074
075        /**
076         * Return this evaluator's compiler configuration (never {@code null}).
077         * @since 4.3.3
078         * @see #setCompilerConfiguration
079         */
080        public CompilerConfiguration getCompilerConfiguration() {
081                return this.compilerConfiguration;
082        }
083
084        /**
085         * Set one or more customizers to be applied to this evaluator's compiler configuration.
086         * <p>Note that this modifies the shared compiler configuration held by this evaluator.
087         * @since 4.3.3
088         * @see #setCompilerConfiguration
089         */
090        public void setCompilationCustomizers(CompilationCustomizer... compilationCustomizers) {
091                this.compilerConfiguration.addCompilationCustomizers(compilationCustomizers);
092        }
093
094        @Override
095        public void setBeanClassLoader(ClassLoader classLoader) {
096                this.classLoader = classLoader;
097        }
098
099
100        @Override
101        @Nullable
102        public Object evaluate(ScriptSource script) {
103                return evaluate(script, null);
104        }
105
106        @Override
107        @Nullable
108        public Object evaluate(ScriptSource script, @Nullable Map<String, Object> arguments) {
109                GroovyShell groovyShell = new GroovyShell(
110                                this.classLoader, new Binding(arguments), this.compilerConfiguration);
111                try {
112                        String filename = (script instanceof ResourceScriptSource ?
113                                        ((ResourceScriptSource) script).getResource().getFilename() : null);
114                        if (filename != null) {
115                                return groovyShell.evaluate(script.getScriptAsString(), filename);
116                        }
117                        else {
118                                return groovyShell.evaluate(script.getScriptAsString());
119                        }
120                }
121                catch (IOException ex) {
122                        throw new ScriptCompilationException(script, "Cannot access Groovy script", ex);
123                }
124                catch (GroovyRuntimeException ex) {
125                        throw new ScriptCompilationException(script, ex);
126                }
127        }
128
129}