Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Introduce (and Review) ScriptEvaluator abstraction #14

Closed
wants to merge 2 commits into from

4 participants

@cbeams

@costin, this PR supersedes your original one at #11 (I don't think it's possible for me to add commits to someone else's PR). I'll close it right after sending this one. Take a look at the "Review" commit 319d856 to see what I've already updated.

A few open questions:

  1. Is the 'arguments' parameter to Jsr233ScriptEvaluator#determineScriptEngine actually necessary? It's not currently used in the implementation, and it's difficult to imagine a use case where a subclass would use arguments to determine which ScriptEngine to use. Perhaps this is an oversight?
  2. Can you explain the intention / necessity of the 'runAtStartup' setting in Jsr223ScriptEvaluatorFactoryBean? getObject will be invoked when singletons are initialized anyway -- is it actually important have such an explicit setting to do this in the @PostConstruct method as well?
  3. Can you explain a use case where EvaluationPolicy (formerly EvaluationType) would be anything other than ALWAYS? A unit test here would be good. I'm simply not familiar with the "script modification" semantics that are mentioned throughout. It makes sense when imagining someone using a ScriptEvaluator directly, but against the FactoryBean, it doesn't (yet) make sense to me, unless perhaps the FactoryBean is non-singleton scoped.
  4. Curiosity mostly, but what was the impetus for this change? Why now?
  5. Could you also add and assign to yourself an issue to add reference documentation for this feature?

@jhoeller, could you take a look at the cumulative set of changes for a sanity check, e.g. that this is appropriate for 3.1.1, etc? (You can click on the "Diff" tab above to see the net result of both my and Costin's commits).

Issue: SPR-8999

Costin Leau and others added some commits
Costin Leau Introduce ScriptEvaluator abstraction
Previously Spring's scripting support allowed for the registration,
management and use of non-Java objects as Spring beans.

This change allows users a convenient and portable option for executing
any script through the new ScriptEvaluator abstraction.

The initial implementation is based on JSR-223 (javax.script.*) and its
ScriptEngine support. See unit tests for usage examples with JRuby and
Rhino.

Issue: SPR-8999
194f006
@cbeams cbeams Review "Introduce ScriptEvaluator abstraction"
Content

 - Rename discoverEngine => determineScriptEngine

   'determine' naming is commonly used throughout the framework,
   particularly in protected methods such as the one in question;
   'discover' naming has little precedent by comparison.

 - Rename EvaluationType => EvaluationPolicy

   This enumeration indicates when a script should be evaluated as
   opposed to which strategy or approach should be used when
   evaluating it.

 - Add Javadoc where missing; correct and improve where appropriate

 - Eliminate printing to standard out and add meaningful assertions to
   Jsr223ScriptEvaluatorTests

Style

 - Eliminate trailing whitespace

 - Indent case blocks in switch statements

 - Wrap code and documentation at 90 characters where feasible

 - Use imperative statements in Javadoc, e.g.

   "Set the extension" instead of "Sets the extension"
   "Determine a name" instead of "Determines a name"

 - Add @since tags on all new members of the public API

 - Use @link tags where appropriate

 - Convert use of <code/> to {@code}

 - Use "may be null" vs "can be null"

 - JUnit test classes should end in "Tests" not "Test"

Issue: SPR-8999
319d856
@cbeams cbeams referenced this pull request
Closed

Scripting #11

@dturanski

@garyrussell @olegz It looks like we can refactor spring-integration-scripting to use this. To @cbeams question 1. I have a possible UC for the arguments:
To instantiate a Ruby engine:
...java
System.setProperty("org.jruby.embed.localvariable.behavior", "transient");
System.setProperty("org.jruby.embed.localcontext.scope", "threadsafe");
...
Also, for Python engine.eval() doesn't work the same way. I need to invoke engine.get() to get the result. In SI, I have
...java
abstract postProcess(Object result, ScriptEngine engine, Script script)
...
I think it would be a good add to ScriptEvaluator

@jhoeller jhoeller was assigned
@cbeams

@jhoeller, @costin -- what shall we do with this after all? It's a shame to see these changes sit here, but it's not quite clear whether they should be committed as yet.

@costin
@cbeams cbeams closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 7, 2012
  1. @cbeams

    Introduce ScriptEvaluator abstraction

    Costin Leau authored cbeams committed
    Previously Spring's scripting support allowed for the registration,
    management and use of non-Java objects as Spring beans.
    
    This change allows users a convenient and portable option for executing
    any script through the new ScriptEvaluator abstraction.
    
    The initial implementation is based on JSR-223 (javax.script.*) and its
    ScriptEngine support. See unit tests for usage examples with JRuby and
    Rhino.
    
    Issue: SPR-8999
  2. @cbeams

    Review "Introduce ScriptEvaluator abstraction"

    cbeams authored
    Content
    
     - Rename discoverEngine => determineScriptEngine
    
       'determine' naming is commonly used throughout the framework,
       particularly in protected methods such as the one in question;
       'discover' naming has little precedent by comparison.
    
     - Rename EvaluationType => EvaluationPolicy
    
       This enumeration indicates when a script should be evaluated as
       opposed to which strategy or approach should be used when
       evaluating it.
    
     - Add Javadoc where missing; correct and improve where appropriate
    
     - Eliminate printing to standard out and add meaningful assertions to
       Jsr223ScriptEvaluatorTests
    
    Style
    
     - Eliminate trailing whitespace
    
     - Indent case blocks in switch statements
    
     - Wrap code and documentation at 90 characters where feasible
    
     - Use imperative statements in Javadoc, e.g.
    
       "Set the extension" instead of "Sets the extension"
       "Determine a name" instead of "Determines a name"
    
     - Add @since tags on all new members of the public API
    
     - Use @link tags where appropriate
    
     - Convert use of <code/> to {@code}
    
     - Use "may be null" vs "can be null"
    
     - JUnit test classes should end in "Tests" not "Test"
    
    Issue: SPR-8999
This page is out of date. Refresh to see the latest.
View
55 org.springframework.context/src/main/java/org/springframework/scripting/ReadableScriptSource.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2002-2012 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;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * Extension to {@link ScriptSource} that provides a {@link Reader} implementation for the
+ * target source (suitable for streaming).
+ *
+ * @author Costin Leau
+ * @since 3.1.1
+ */
+public interface ReadableScriptSource extends ScriptSource {
+
+ /**
+ * Retrieve the script source text as {@link Reader}.
+ *
+ * @return reader for the underlying script
+ * @throws IOException if script retrieval failed
+ */
+ Reader getScriptAsReader() throws IOException;
+
+ /**
+ * Determine a name for the underlying script.
+ *
+ * @return the suggested script name, or {@code null} if none available
+ */
+ String suggestedScriptName();
+
+ /**
+ * Indicate whether the underlying script data has been modified since the last time
+ * {@link #getScriptAsString()} or {@link #getScriptAsReader()} was called. Return
+ * {@code true} if the script has not been read yet.
+ *
+ * @return whether the script data has been modified
+ */
+ boolean isModified();
+
+}
View
46 org.springframework.context/src/main/java/org/springframework/scripting/ScriptEvaluator.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002-2012 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;
+
+import java.util.Map;
+
+/**
+ * Script evaluator.
+ *
+ * @author Costin Leau
+ * @since 3.1.1
+ */
+public interface ScriptEvaluator {
+
+ /**
+ * Evaluate the given script (without any arguments) and returns the result (if any).
+ *
+ * @param script script to evaluate
+ * @return script result (may be {@code null})
+ */
+ Object evaluate(ScriptSource script);
+
+ /**
+ * Evaluates the given script and returns the result (if any).
+ *
+ * @param script script to evaluate
+ * @param arguments script arguments
+ * @return script result (may be {@code null})
+ */
+ Object evaluate(ScriptSource script, Map<String, Object> arguments);
+
+}
View
179 org.springframework.context/src/main/java/org/springframework/scripting/jsr223/Jsr223ScriptEvaluator.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2002-2012 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.jsr223;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Collections;
+import java.util.Map;
+
+import javax.script.Bindings;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import javax.script.SimpleBindings;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.scripting.ReadableScriptSource;
+import org.springframework.scripting.ScriptCompilationException;
+import org.springframework.scripting.ScriptEvaluator;
+import org.springframework.scripting.ScriptSource;
+import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link ScriptEvaluator} implementation that delegates to an underlying
+ * JSR-233/javax.scripting {@link ScriptEngine} to evaluate scripts. The particular
+ * {@code ScriptEngine} to use is {@linkplain #determineScriptEngine determined} based
+ * based on the language/extension of the specified script.
+ *
+ * @author Costin Leau
+ * @author Chris Beams
+ * @since 3.1.1
+ */
+public class Jsr223ScriptEvaluator implements ScriptEvaluator {
+
+ private final Log log = LogFactory.getLog(getClass());
+
+ private String language;
+ private String extension;
+ private ClassLoader classLoader;
+
+
+ /**
+ * Construct a new {@code Jsr223ScriptEvaluator} instance, allowing the underlying
+ * JSR-223 {@link ScriptEngineManager} to determine the class loader to use.
+ * @see ScriptEngineManager#ScriptEngineManager()
+ */
+ public Jsr223ScriptEvaluator() {
+ this(null);
+ }
+
+ /**
+ * Construct a new {@code Jsr223ScriptEvaluator} instance.
+ *
+ * @param classLoader class loader to use when constructing underlying JSR-223
+ * {@link ScriptEngineManager} (may be {@code null}).
+ * @see #determineScriptEngine(ScriptSource, Map)
+ * @see ScriptEngineManager#ScriptEngineManager(ClassLoader)
+ */
+ public Jsr223ScriptEvaluator(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ public Object evaluate(ScriptSource script) {
+ return evaluate(script, Collections.<String, Object> emptyMap());
+ }
+
+ public Object evaluate(ScriptSource script, Map<String, Object> arguments) {
+ ScriptEngine engine = determineScriptEngine(script, arguments);
+
+ Bindings bindings = (!CollectionUtils.isEmpty(arguments) ?
+ new SimpleBindings(arguments) : null);
+
+ try {
+ Reader scriptAsReader = (script instanceof ReadableScriptSource ?
+ ((ReadableScriptSource) script).getScriptAsReader() : null);
+
+ if (bindings == null) {
+ return (scriptAsReader == null ?
+ engine.eval(script.getScriptAsString()) : engine.eval(scriptAsReader));
+ }
+ else {
+ return (scriptAsReader == null ?
+ engine.eval(script.getScriptAsString(), bindings) :
+ engine.eval(scriptAsReader, bindings));
+ }
+ } catch (IOException ex) {
+ throw new ScriptCompilationException(script, "Cannot access script", ex);
+ } catch (ScriptException ex) {
+ throw new ScriptCompilationException(script, ex);
+ }
+ }
+
+ /**
+ * Determine the JSR-223 {@link ScriptEngine} to be used for evaluating the given
+ * script based on the {@linkplain #setExtension extension} or
+ * {@linkplain #setLanguage language} of the script. If the script is a
+ * {@link ReadableScriptSource}, the extension will be discovered automatically.
+ * @param script the script to be evaluated
+ * @param arguments arguments provided to the script (ignored in this implementation)
+ * @throws IllegalArgumentException if language/extension have not been specified and
+ * no extension can be automatically discovered
+ * @throws IllegalArgumentException if no {@code ScriptEngine} associated with the
+ * given language/extension can be found
+ * @return the {@code ScriptEngine} to be used for the given language
+ */
+ protected ScriptEngine determineScriptEngine(ScriptSource script, Map<String, Object> arguments) {
+ ScriptEngineManager engineManager = new ScriptEngineManager(this.classLoader);
+ ScriptEngine engine = null;
+
+ if (StringUtils.hasText(language)) {
+ engine = engineManager.getEngineByName(language);
+ }
+ else {
+ if (!StringUtils.hasText(extension) && script instanceof ReadableScriptSource) {
+ extension = StringUtils.getFilenameExtension(((ReadableScriptSource) script).suggestedScriptName());
+ }
+ Assert.hasText(extension, "no language or extension specified or detected");
+ engine = engineManager.getEngineByExtension(extension);
+ }
+
+ Assert.notNull(engine, String.format("No suitable engine found for %s",
+ (StringUtils.hasText(language) ? "language " + language : "extension " + extension)));
+
+ if (log.isDebugEnabled()) {
+ ScriptEngineFactory factory = engine.getFactory();
+ log.debug(String.format("Using ScriptEngine %s (%s), language %s (%s)",
+ factory.getEngineName(), factory.getEngineVersion(),
+ factory.getLanguageName(), factory.getLanguageVersion()));
+ }
+
+ return engine;
+ }
+
+ /**
+ * Set the script file extension, to be used when determining the JSR-223
+ * {@link ScriptEngine} to be used in evaluating the script. This property is not
+ * required if {@link #setLanguage} has been called or if the script provided is an
+ * implementation of {@link ReadableScriptSource}.
+ *
+ * @param extension the extension of the script, e.g. ".rb" for Ruby, ".js" for
+ * JavaScript, etc
+ * @see #determineScriptEngine
+ * @see ScriptEngineManager#getEngineByExtension
+ */
+ public void setExtension(String extension) {
+ this.extension = extension;
+ }
+
+ /**
+ * Set the script file extension, to be used when determining the JSR-223
+ * {@link ScriptEngine} to be used in evaluating the script. This property is not
+ * required if {@link #setExtension} has been called or if the script provided is an
+ * implementation of {@link ReadableScriptSource}.
+ *
+ * @param language the language to set
+ * @see #determineScriptEngine
+ */
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+}
View
178 ...amework.context/src/main/java/org/springframework/scripting/jsr223/Jsr223ScriptEvaluatorFactoryBean.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2002-2012 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.jsr223;
+
+import java.util.Map;
+
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.scripting.ScriptSource;
+
+/**
+ * {@link FactoryBean} implementation allowing for use of JSR-223 based
+ * {@link ScriptEvaluator} within Spring XML configuration files.
+ *
+ * @author Costin Leau
+ * @author Chris Beams
+ * @since 3.1.1
+ */
+class Jsr223ScriptEvaluatorFactoryBean implements InitializingBean, BeanClassLoaderAware, FactoryBean<Object> {
+
+ /**
+ * Enumeration specifying the policy for how often the script should be evaluated
+ * during the lifetime of this factory bean.
+ */
+ public enum EvaluationPolicy {
+ /**
+ * Evaluate the script once and only once, regardless of whether the script has
+ * been modified.
+ */
+ ONCE,
+
+ /**
+ * Evaluate the script if it has been modified since the last time it was evaluated.
+ */
+ IF_MODIFIED,
+
+ /**
+ * Evaluate the script every time {@code getObject} is invoked.
+ */
+ ALWAYS;
+ }
+
+ private ClassLoader classLoader;
+ private ScriptSource script;
+ private Jsr223ScriptEvaluator evaluator;
+ private String language, extension;
+ private Map<String, Object> arguments;
+ private EvaluationPolicy evaluationPolicy = EvaluationPolicy.ALWAYS;
+ private final Object monitor = new Object();
+ private volatile boolean evaluated;
+ private Object result = null;
+ private boolean runAtStartup = false;
+
+ public Object getObject() {
+ switch (evaluationPolicy) {
+ case ONCE:
+ if (!evaluated) {
+ synchronized (monitor) {
+ if (!evaluated) {
+ evaluated = true;
+ result = evaluator.evaluate(script, arguments);
+ }
+ }
+ }
+ return result;
+ case IF_MODIFIED:
+ // isModified is synchronized so only one thread will see the update
+ if (script.isModified()) {
+ result = evaluator.evaluate(script, arguments);
+ }
+ return result;
+ default:
+ return evaluator.evaluate(script, arguments);
+ }
+ }
+
+ public void afterPropertiesSet() {
+ evaluator = new Jsr223ScriptEvaluator(classLoader);
+ evaluator.setLanguage(language);
+ evaluator.setExtension(extension);
+
+ postProcess(arguments);
+
+ if (runAtStartup) {
+ getObject();
+ }
+ }
+
+ /**
+ * Method for post-processing script arguments. Useful for enhancing (adding) new
+ * arguments to scripts being executed. The default implementation is a no-op.
+ *
+ * @param arguments arguments provided to the script
+ */
+ protected void postProcess(Map<String, Object> arguments) {
+
+ }
+
+ public Class<Object> getObjectType() {
+ return Object.class;
+ }
+
+ public boolean isSingleton() {
+ return EvaluationPolicy.ONCE.equals(evaluationPolicy);
+ }
+
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * @param script the script to be evaluated
+ */
+ public void setScriptSource(ScriptSource script) {
+ this.script = script;
+ }
+
+ /**
+ * @param language the language of the script to be evaluated.
+ * @see Jsr223ScriptEvaluator#setLanguage
+ */
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ /**
+ * @param extension the extension of the script to be evaluated.
+ * @see Jsr223ScriptEvaluator#setExtension
+ */
+ public void setExtension(String extension) {
+ this.extension = extension;
+ }
+
+ /**
+ * Set the {@link EvaluationPolicy} for the script.
+ */
+ public void setEvaluationPolicy(EvaluationPolicy evaluationPolicy) {
+ this.evaluationPolicy = evaluationPolicy;
+ }
+
+ /**
+ * @param arguments the arguments to provide to the script
+ */
+ public void setArguments(Map<String, Object> arguments) {
+ this.arguments = arguments;
+ }
+
+ /**
+ * Return whether the script should be evaluated immediately upon container startup.
+ */
+ public boolean isRunAtStartup() {
+ return runAtStartup;
+ }
+
+ /**
+ * Indicate whether the {@linkplain #setScriptSource script} should be evaluated
+ * immediately upon container startup. If false, evaluation will occur only when
+ * {@link #getObject()} is called. The default is {@code false}.
+ */
+ public void setRunAtStartup(boolean runAtStartup) {
+ this.runAtStartup = runAtStartup;
+ }
+}
View
44 org.springframework.context/src/main/java/org/springframework/scripting/support/ResourceScriptSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2012 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.
@@ -24,27 +24,27 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
+import org.springframework.scripting.ReadableScriptSource;
import org.springframework.scripting.ScriptSource;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
/**
- * {@link org.springframework.scripting.ScriptSource} implementation
- * based on Spring's {@link org.springframework.core.io.Resource}
- * abstraction. Loads the script text from the underlying Resource's
- * {@link org.springframework.core.io.Resource#getFile() File} or
- * {@link org.springframework.core.io.Resource#getInputStream() InputStream},
- * and tracks the last-modified timestamp of the file (if possible).
+ * {@link ScriptSource} implementation based on Spring's {@link Resource} abstraction.
+ * Loads the script text from the underlying {@code Resource}'s
+ * {@link Resource#getFile() File} or {@link Resource#getInputStream() InputStream}, and
+ * tracks the last-modified timestamp of the file (if possible).
*
* @author Rob Harrop
* @author Juergen Hoeller
+ * @author Costin Leau
* @since 2.0
* @see org.springframework.core.io.Resource#getInputStream()
* @see org.springframework.core.io.Resource#getFile()
* @see org.springframework.core.io.ResourceLoader
*/
-public class ResourceScriptSource implements ScriptSource {
+public class ResourceScriptSource implements ScriptSource, ReadableScriptSource {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
@@ -75,15 +75,7 @@ public final Resource getResource() {
}
public String getScriptAsString() throws IOException {
- synchronized (this.lastModifiedMonitor) {
- this.lastModified = retrieveLastModifiedTime();
- }
-
- InputStream stream = this.resource.getInputStream();
- Reader reader = (StringUtils.hasText(encoding) ? new InputStreamReader(stream, encoding)
- : new InputStreamReader(stream));
-
- return FileCopyUtils.copyToString(reader);
+ return FileCopyUtils.copyToString(getScriptAsReader());
}
public boolean isModified() {
@@ -115,15 +107,29 @@ public String suggestedClassName() {
/**
* Sets the encoding used for reading the script resource. The default value is "UTF-8".
* A null value, implies the platform default.
- *
+ *
* @param encoding charset encoding used for reading the script.
*/
public void setEncoding(String encoding) {
this.encoding = encoding;
}
+ public Reader getScriptAsReader() throws IOException {
+ synchronized (this.lastModifiedMonitor) {
+ this.lastModified = retrieveLastModifiedTime();
+ }
+
+ InputStream stream = this.resource.getInputStream();
+ return (StringUtils.hasText(encoding) ?
+ new InputStreamReader(stream, encoding) : new InputStreamReader(stream));
+ }
+
+ public String suggestedScriptName() {
+ return getResource().getFilename();
+ }
+
@Override
public String toString() {
return this.resource.toString();
}
-}
+}
View
40 org.springframework.context/src/main/java/org/springframework/scripting/support/StaticScriptSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2008 the original author or authors.
+ * Copyright 2002-2012 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.
@@ -16,20 +16,24 @@
package org.springframework.scripting.support;
+import java.io.Reader;
+import java.io.StringReader;
+
+import org.springframework.scripting.ReadableScriptSource;
import org.springframework.scripting.ScriptSource;
import org.springframework.util.Assert;
/**
- * Static implementation of the
- * {@link org.springframework.scripting.ScriptSource} interface,
- * encapsulating a given String that contains the script source text.
- * Supports programmatic updates of the script String.
+ * Static implementation of the {@link ScriptSource} interface, encapsulating a given
+ * String that contains the script source text. Supports programmatic updates of the
+ * script String.
*
* @author Rob Harrop
* @author Juergen Hoeller
+ * @author Costin Leau
* @since 2.0
*/
-public class StaticScriptSource implements ScriptSource {
+public class StaticScriptSource implements ScriptSource, ReadableScriptSource {
private String script;
@@ -37,24 +41,35 @@
private String className;
+ private String scriptName;
/**
* Create a new StaticScriptSource for the given script.
* @param script the script String
*/
public StaticScriptSource(String script) {
- setScript(script);
+ this(script, null);
}
/**
* Create a new StaticScriptSource for the given script.
* @param script the script String
- * @param className the suggested class name for the script
- * (may be <code>null</code>)
+ * @param className the suggested class name for the script (may be {@code null})
*/
public StaticScriptSource(String script, String className) {
+ this(script, className, className);
+ }
+
+ /**
+ * Create a new StaticScriptSource for the given script.
+ * @param script the script String
+ * @param className the suggested class name for the script (may be {@code null})
+ * @param scriptName the suggested name for the script (may be {@code null})
+ */
+ public StaticScriptSource(String script, String className, String scriptName) {
setScript(script);
this.className = className;
+ this.scriptName = scriptName;
}
/**
@@ -87,4 +102,11 @@ public String toString() {
return "static script" + (this.className != null ? " [" + this.className + "]" : "");
}
+ public Reader getScriptAsReader() {
+ return new StringReader(getScriptAsString());
+ }
+
+ public String suggestedScriptName() {
+ return scriptName;
+ }
}
View
83 ...ringframework.context/src/test/java/org/springframework/scripting/jsr223/Jsr223ScriptEvaluatorTests.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2002-2012 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.jsr223;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.junit.Test;
+import org.springframework.core.io.UrlResource;
+import org.springframework.scripting.ScriptSource;
+import org.springframework.scripting.support.ResourceScriptSource;
+import org.springframework.scripting.support.StaticScriptSource;
+
+/**
+ * Unit tests for {@link Jsr223ScriptEvaluator}.
+ *
+ * @author Costin Leau
+ * @author Chris Beams
+ * @since 3.1.1
+ */
+public class Jsr223ScriptEvaluatorTests {
+
+ @Test
+ public void testRhinoScript() throws Exception {
+ ScriptSource script = new StaticScriptSource("'Hello, js!' // return a greeting");
+
+ Jsr223ScriptEvaluator eval = new Jsr223ScriptEvaluator();
+ eval.setLanguage("javascript");
+ Object result = eval.evaluate(script);
+ assertEquals("Hello, js!", result);
+ }
+
+ @Test
+ public void testRhinoEvalScript() throws Exception {
+ ScriptSource script = new ResourceScriptSource(new UrlResource(getClass().getResource("basic-script.js")));
+
+ Jsr223ScriptEvaluator eval = new Jsr223ScriptEvaluator();
+ String arg1 = "testArg";
+
+ Map<String, Object> args = new LinkedHashMap<String, Object>();
+ args.put("arg", arg1);
+ assertEquals(arg1, eval.evaluate(script, args));
+ }
+
+ @Test
+ public void testRubyScript() throws Exception {
+ ScriptSource script = new StaticScriptSource("'Hello, ruby!' # return a greeting");
+
+ Jsr223ScriptEvaluator eval = new Jsr223ScriptEvaluator(getClass().getClassLoader());
+ eval.setLanguage("ruby");
+ Object result = eval.evaluate(script);
+ assertEquals("Hello, ruby!", result);
+ }
+
+ @Test
+ public void testRubyEvalScript() throws Exception {
+ ScriptSource script = new ResourceScriptSource(new UrlResource(getClass().getResource("basic-script.rb")));
+
+ Jsr223ScriptEvaluator eval = new Jsr223ScriptEvaluator(getClass().getClassLoader());
+ String arg1 = "testArg";
+
+ Map<String, Object> args = new LinkedHashMap<String, Object>();
+ args.put("arg", arg1);
+
+ assertEquals(arg1, eval.evaluate(script, args));
+ }
+}
View
7 org.springframework.context/src/test/java/org/springframework/scripting/jsr223/basic-script.js
@@ -0,0 +1,7 @@
+importPackage(java.util);
+
+// perform some computation for good measure
+var uuid = UUID.randomUUID();
+
+// return the argument passed in
+arg
View
7 org.springframework.context/src/test/java/org/springframework/scripting/jsr223/basic-script.rb
@@ -0,0 +1,7 @@
+require 'java'
+
+# perform some computation for good measure
+uuid = java.util.UUID.randomUUID()
+
+# return the argument passed in
+$arg
Something went wrong with that request. Please try again.