Javadoc refinements, exception fine-tuning, general polishing

Issue: SPR-13034
This commit is contained in:
Juergen Hoeller 2015-07-15 15:42:17 +02:00
parent 2577ac300b
commit d183bbb667
13 changed files with 193 additions and 169 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,9 +19,9 @@ package org.springframework.web.servlet.view.groovy;
import groovy.text.markup.MarkupTemplateEngine; import groovy.text.markup.MarkupTemplateEngine;
/** /**
* Interface to be implemented by objects that configure and manage a * Interface to be implemented by objects that configure and manage a Groovy
* Groovy MarkupTemplateEngine for automatic lookup in a web environment. * {@link MarkupTemplateEngine} for automatic lookup in a web environment.
* Detected and used by GroovyMarkupView. * Detected and used by {@link GroovyMarkupView}.
* *
* @author Brian Clozel * @author Brian Clozel
* @since 4.1 * @since 4.1

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -46,7 +46,6 @@ import org.springframework.util.StringUtils;
* *
* @Bean * @Bean
* public GroovyMarkupConfig groovyMarkupConfigurer() { * public GroovyMarkupConfig groovyMarkupConfigurer() {
*
* GroovyMarkupConfigurer configurer = new GroovyMarkupConfigurer(); * GroovyMarkupConfigurer configurer = new GroovyMarkupConfigurer();
* configurer.setResourceLoaderPath("classpath:/WEB-INF/groovymarkup/"); * configurer.setResourceLoaderPath("classpath:/WEB-INF/groovymarkup/");
* return configurer; * return configurer;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -35,8 +35,7 @@ import org.springframework.web.servlet.view.AbstractTemplateView;
import org.springframework.web.util.NestedServletException; import org.springframework.web.util.NestedServletException;
/** /**
* An {@link org.springframework.web.servlet.view.AbstractTemplateView AbstractTemplateView} * An {@link AbstractTemplateView} subclass based on Groovy XML/XHTML markup templates.
* based on Groovy XML/XHTML markup templates.
* *
* <p>Spring's Groovy Markup Template support requires Groovy 2.3.1 and higher. * <p>Spring's Groovy Markup Template support requires Groovy 2.3.1 and higher.
* *

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,10 +21,9 @@ import java.util.Locale;
import org.springframework.web.servlet.view.AbstractTemplateViewResolver; import org.springframework.web.servlet.view.AbstractTemplateViewResolver;
/** /**
* Convenience subclass of * Convenience subclass of @link AbstractTemplateViewResolver} that supports
* {@link org.springframework.web.servlet.view.AbstractTemplateViewResolver} * {@link GroovyMarkupView} (i.e. Groovy XML/XHTML markup templates) and
* that supports {@link GroovyMarkupView} (i.e. Groovy XML/XHTML markup templates) * custom subclasses of it.
* and custom subclasses of it.
* *
* <p>The view class for all views created by this resolver can be specified * <p>The view class for all views created by this resolver can be specified
* via the {@link #setViewClass(Class)} property. * via the {@link #setViewClass(Class)} property.

View File

@ -1,6 +1,6 @@
/** /**
* Support classes for the integration of * Support classes for the integration of
* <a href="http://beta.groovy-lang.org/docs/groovy-2.3.0/html/documentation/markup-template-engine.html"> * <a href="http://docs.groovy-lang.org/docs/next/html/documentation/template-engines.html#_the_markuptemplateengine">
* Groovy Templates</a> as Spring web view technology. * Groovy Templates</a> as Spring web view technology.
* Contains a View implementation for Groovy templates. * Contains a View implementation for Groovy templates.
*/ */

View File

@ -21,7 +21,7 @@ import javax.script.ScriptEngine;
/** /**
* Interface to be implemented by objects that configure and manage a * Interface to be implemented by objects that configure and manage a
* {@link ScriptEngine} for automatic lookup in a web environment. * JSR-223 {@link ScriptEngine} for automatic lookup in a web environment.
* Detected and used by {@link ScriptTemplateView}. * Detected and used by {@link ScriptTemplateView}.
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
@ -29,20 +29,45 @@ import javax.script.ScriptEngine;
*/ */
public interface ScriptTemplateConfig { public interface ScriptTemplateConfig {
/**
* Return the {@link ScriptEngine} to use by the views.
*/
ScriptEngine getEngine(); ScriptEngine getEngine();
/**
* Return the engine name that will be used to instantiate the {@link ScriptEngine}.
*/
String getEngineName(); String getEngineName();
/**
* Return whether to use a shared engine for all threads or whether to create
* thread-local engine instances for each thread.
*/
Boolean isSharedEngine();
/**
* Return the scripts to be loaded by the script engine (library or user provided).
*/
String[] getScripts(); String[] getScripts();
/**
* Return the object where the render function belongs (optional).
*/
String getRenderObject(); String getRenderObject();
/**
* Return the render function name (mandatory).
*/
String getRenderFunction(); String getRenderFunction();
/**
* Return the charset used to read script and template files.
*/
Charset getCharset(); Charset getCharset();
/**
* Return the resource loader path(s) via a Spring resource location.
*/
String getResourceLoaderPath(); String getResourceLoaderPath();
Boolean isShareEngine();
} }

View File

@ -17,7 +17,6 @@
package org.springframework.web.servlet.view.script; package org.springframework.web.servlet.view.script;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import javax.script.ScriptEngine; import javax.script.ScriptEngine;
/** /**
@ -40,9 +39,9 @@ import javax.script.ScriptEngine;
* } * }
* </pre> * </pre>
* *
* <p>It is possible to use non thread-safe script engines and templating libraries, like * <p><b>NOTE:</b> It is possible to use non thread-safe script engines and
* Handlebars or React running on Nashorn, by setting the * templating libraries, like Handlebars or React running on Nashorn, by setting
* {@link #setSharedEngine(Boolean) sharedEngine} property to {@code false}. * the {@link #setSharedEngine sharedEngine} property to {@code false}.
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @since 4.2 * @since 4.2
@ -54,6 +53,8 @@ public class ScriptTemplateConfigurer implements ScriptTemplateConfig {
private String engineName; private String engineName;
private Boolean sharedEngine;
private String[] scripts; private String[] scripts;
private String renderObject; private String renderObject;
@ -64,17 +65,14 @@ public class ScriptTemplateConfigurer implements ScriptTemplateConfig {
private String resourceLoaderPath; private String resourceLoaderPath;
private Boolean sharedEngine;
/** /**
* Set the {@link ScriptEngine} to use by the view. * Set the {@link ScriptEngine} to use by the view.
* The script engine must implement {@code Invocable}. * The script engine must implement {@code Invocable}.
* You must define {@code engine} or {@code engineName}, not both. * You must define {@code engine} or {@code engineName}, not both.
*
* <p>When the {@code sharedEngine} flag is set to {@code false}, you should not specify * <p>When the {@code sharedEngine} flag is set to {@code false}, you should not specify
* the script engine with this setter, but with the {@link #setEngineName(String)} * the script engine with this setter, but with the {@link #setEngineName(String)}
* one (since it implies multiple lazy instanciations of the script engine). * one (since it implies multiple lazy instantiations of the script engine).
*
* @see #setEngineName(String) * @see #setEngineName(String)
*/ */
public void setEngine(ScriptEngine engine) { public void setEngine(ScriptEngine engine) {
@ -101,17 +99,35 @@ public class ScriptTemplateConfigurer implements ScriptTemplateConfig {
return this.engineName; return this.engineName;
} }
/**
* When set to {@code false}, use thread-local {@link ScriptEngine} instances instead
* of one single shared instance. This flag should be set to {@code false} for those
* using non thread-safe script engines and templating libraries, like Handlebars or
* React running on Nashorn for example.
* <p>When this flag is set to {@code false}, the script engine must be specified using
* {@link #setEngineName(String)}. Using {@link #setEngine(ScriptEngine)} is not
* possible because multiple instances of the script engine need to be created lazily
* (one per thread).
* @see <a href="http://docs.oracle.com/javase/8/docs/api/javax/script/ScriptEngineFactory.html#getParameter-java.lang.String-">THREADING ScriptEngine parameter<a/>
*/
public void setSharedEngine(Boolean sharedEngine) {
this.sharedEngine = sharedEngine;
}
@Override
public Boolean isSharedEngine() {
return this.sharedEngine;
}
/** /**
* Set the scripts to be loaded by the script engine (library or user provided). * Set the scripts to be loaded by the script engine (library or user provided).
* Since {@code resourceLoaderPath} default value is "classpath:", you can load easily * Since {@code resourceLoaderPath} default value is "classpath:", you can load easily
* any script available on the classpath. * any script available on the classpath.
* * <p>For example, in order to use a JavaScript library available as a WebJars dependency
* For example, in order to use a Javascript library available as a WebJars dependency
* and a custom "render.js" file, you should call * and a custom "render.js" file, you should call
* {@code configurer.setScripts("/META-INF/resources/webjars/library/version/library.js", * {@code configurer.setScripts("/META-INF/resources/webjars/library/version/library.js",
* "com/myproject/script/render.js");}. * "com/myproject/script/render.js");}.
* * @see #setResourceLoaderPath
* @see #setResourceLoaderPath(String)
* @see <a href="http://www.webjars.org">WebJars</a> * @see <a href="http://www.webjars.org">WebJars</a>
*/ */
public void setScripts(String... scriptNames) { public void setScripts(String... scriptNames) {
@ -124,7 +140,7 @@ public class ScriptTemplateConfigurer implements ScriptTemplateConfig {
} }
/** /**
* Set the object where belongs the render function (optional). * Set the object where the render function belongs (optional).
* For example, in order to call {@code Mustache.render()}, {@code renderObject} * For example, in order to call {@code Mustache.render()}, {@code renderObject}
* should be set to {@code "Mustache"} and {@code renderFunction} to {@code "render"}. * should be set to {@code "Mustache"} and {@code renderFunction} to {@code "render"}.
*/ */
@ -138,11 +154,11 @@ public class ScriptTemplateConfigurer implements ScriptTemplateConfig {
} }
/** /**
* Set the render function name (mandatory). This function will be called with the * Set the render function name (mandatory).
* following parameters: * This function will be called with the following parameters:
* <ol> * <ol>
* <li>{@code template}: the view template content (String)</li> * <li>{@code template}: the view template content (String)</li>
* <li>{@code model}: the view model (Map)</li> * <li>{@code model}: the view model (Map)</li>
* </ol> * </ol>
*/ */
public void setRenderFunction(String renderFunction) { public void setRenderFunction(String renderFunction) {
@ -173,7 +189,7 @@ public class ScriptTemplateConfigurer implements ScriptTemplateConfig {
* Standard URLs like "file:" and "classpath:" and pseudo URLs are supported * Standard URLs like "file:" and "classpath:" and pseudo URLs are supported
* as understood by Spring's {@link org.springframework.core.io.ResourceLoader}. * as understood by Spring's {@link org.springframework.core.io.ResourceLoader}.
* Relative paths are allowed when running in an ApplicationContext. * Relative paths are allowed when running in an ApplicationContext.
* Default is "classpath:". * <p>Default is "classpath:".
*/ */
public void setResourceLoaderPath(String resourceLoaderPath) { public void setResourceLoaderPath(String resourceLoaderPath) {
this.resourceLoaderPath = resourceLoaderPath; this.resourceLoaderPath = resourceLoaderPath;
@ -184,25 +200,4 @@ public class ScriptTemplateConfigurer implements ScriptTemplateConfig {
return this.resourceLoaderPath; return this.resourceLoaderPath;
} }
/**
* When set to {@code false}, use thread-local {@link ScriptEngine} instances instead
* of one single shared instance. This flag should be set to {@code false} for those
* using non thread-safe script engines and templating libraries, like Handlebars or
* React running on Nashorn for example.
*
* <p>When this flag is set to {@code false}, the script engine must be specified using
* {@link #setEngineName(String)}. Using {@link #setEngine(ScriptEngine)} is not
* possible because multiple instances of the script engine need to be created lazily
* (one per thread).
* @see <a href="http://docs.oracle.com/javase/8/docs/api/javax/script/ScriptEngineFactory.html#getParameter-java.lang.String-">THREADING ScriptEngine parameter<a/>
*/
public void setSharedEngine(Boolean sharedEngine) {
this.sharedEngine = sharedEngine;
}
@Override
public Boolean isShareEngine() {
return this.sharedEngine;
}
} }

View File

@ -27,7 +27,6 @@ import java.util.Map;
import javax.script.Invocable; import javax.script.Invocable;
import javax.script.ScriptEngine; import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager; import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@ -46,8 +45,8 @@ import org.springframework.util.StringUtils;
import org.springframework.web.servlet.view.AbstractUrlBasedView; import org.springframework.web.servlet.view.AbstractUrlBasedView;
/** /**
* An {@link org.springframework.web.servlet.view.AbstractUrlBasedView AbstractUrlBasedView} * An {@link AbstractUrlBasedView} subclass designed to run any template library
* designed to run any template library based on a JSR-223 script engine. * based on a JSR-223 script engine.
* *
* <p>If not set, each property is auto-detected by looking up up a single * <p>If not set, each property is auto-detected by looking up up a single
* {@link ScriptTemplateConfig} bean in the web application context and using * {@link ScriptTemplateConfig} bean in the web application context and using
@ -69,13 +68,14 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
private static final String DEFAULT_RESOURCE_LOADER_PATH = "classpath:"; private static final String DEFAULT_RESOURCE_LOADER_PATH = "classpath:";
private final ThreadLocal<ScriptEngine> engineHolder = new NamedThreadLocal<ScriptEngine>("ScriptTemplateView engine");
private ScriptEngine engine; private ScriptEngine engine;
private final ThreadLocal<ScriptEngine> engineHolder =
new NamedThreadLocal<ScriptEngine>("ScriptTemplateView engine");
private String engineName; private String engineName;
private Boolean sharedEngine;
private String[] scripts; private String[] scripts;
private String renderObject; private String renderObject;
@ -88,8 +88,6 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
private String resourceLoaderPath; private String resourceLoaderPath;
private Boolean sharedEngine;
/** /**
* See {@link ScriptTemplateConfigurer#setEngine(ScriptEngine)} documentation. * See {@link ScriptTemplateConfigurer#setEngine(ScriptEngine)} documentation.
@ -99,48 +97,6 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
this.engine = engine; this.engine = engine;
} }
protected ScriptEngine getEngine() {
if (Boolean.FALSE.equals(this.sharedEngine)) {
ScriptEngine engine = this.engineHolder.get();
if (engine == null) {
engine = createEngineFromName();
this.engineHolder.set(engine);
}
return engine;
}
else if (this.engine == null) {
setEngine(createEngineFromName());
}
return this.engine;
}
protected ScriptEngine createEngineFromName() {
Assert.notNull(this.engineName);
ScriptEngine engine = new ScriptEngineManager().getEngineByName(this.engineName);
Assert.state(engine != null, "No engine \"" + this.engineName + "\" found.");
loadScripts(engine);
return engine;
}
protected void loadScripts(ScriptEngine engine) {
Assert.notNull(engine);
if (this.scripts != null) {
try {
for (String script : this.scripts) {
Resource resource = this.resourceLoader.getResource(script);
Assert.state(resource.exists(), "Resource " + script + " not found.");
engine.eval(new InputStreamReader(resource.getInputStream()));
}
}
catch (ScriptException e) {
throw new IllegalStateException("could not load script", e);
}
catch (IOException e) {
throw new IllegalStateException("could not load script", e);
}
}
}
/** /**
* See {@link ScriptTemplateConfigurer#setEngineName(String)} documentation. * See {@link ScriptTemplateConfigurer#setEngineName(String)} documentation.
*/ */
@ -148,6 +104,13 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
this.engineName = engineName; this.engineName = engineName;
} }
/**
* See {@link ScriptTemplateConfigurer#setSharedEngine(Boolean)} documentation.
*/
public void setSharedEngine(Boolean sharedEngine) {
this.sharedEngine = sharedEngine;
}
/** /**
* See {@link ScriptTemplateConfigurer#setScripts(String...)} documentation. * See {@link ScriptTemplateConfigurer#setScripts(String...)} documentation.
*/ */
@ -183,12 +146,6 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
this.resourceLoaderPath = resourceLoaderPath; this.resourceLoaderPath = resourceLoaderPath;
} }
/**
* See {@link ScriptTemplateConfigurer#setSharedEngine(Boolean)} documentation.
*/
public void setSharedEngine(Boolean sharedEngine) {
this.sharedEngine = sharedEngine;
}
@Override @Override
protected void initApplicationContext(ApplicationContext context) { protected void initApplicationContext(ApplicationContext context) {
@ -211,36 +168,78 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
this.renderFunction = viewConfig.getRenderFunction(); this.renderFunction = viewConfig.getRenderFunction();
} }
if (this.charset == null) { if (this.charset == null) {
this.charset = viewConfig.getCharset() == null ? DEFAULT_CHARSET : viewConfig.getCharset(); this.charset = (viewConfig.getCharset() != null ? viewConfig.getCharset() : DEFAULT_CHARSET);
} }
if (this.resourceLoaderPath == null) { if (this.resourceLoaderPath == null) {
this.resourceLoaderPath = viewConfig.getResourceLoaderPath() == null ? this.resourceLoaderPath = (viewConfig.getResourceLoaderPath() != null ?
DEFAULT_RESOURCE_LOADER_PATH : viewConfig.getResourceLoaderPath(); viewConfig.getResourceLoaderPath() : DEFAULT_RESOURCE_LOADER_PATH);
} }
if (this.resourceLoader == null) { if (this.resourceLoader == null) {
this.resourceLoader = new DefaultResourceLoader(createClassLoader()); this.resourceLoader = new DefaultResourceLoader(createClassLoader());
} }
if (this.sharedEngine == null && viewConfig.isShareEngine() != null) { if (this.sharedEngine == null && viewConfig.isSharedEngine() != null) {
this.sharedEngine = viewConfig.isShareEngine(); this.sharedEngine = viewConfig.isSharedEngine();
} }
Assert.state(!(this.engine != null && this.engineName != null), Assert.isTrue(!(this.engine != null && this.engineName != null),
"You should define engine or engineName properties, not both."); "You should define either 'engine' or 'engineName', not both.");
Assert.state(!(this.engine == null && this.engineName == null), Assert.isTrue(!(this.engine == null && this.engineName == null),
"No script engine found, please specify valid engine or engineName properties."); "No script engine found, please specify either 'engine' or 'engineName'.");
if (Boolean.FALSE.equals(this.sharedEngine)) { if (Boolean.FALSE.equals(this.sharedEngine)) {
Assert.state(this.engineName != null, Assert.isTrue(this.engineName != null,
"When sharedEngine property is set to false, you should specify the " + "When 'sharedEngine' is set to false, you should specify the " +
"script engine using the engineName property, not the engine one."); "script engine using the 'engineName' property, not the 'engine' one.");
} }
Assert.state(this.renderFunction != null, "renderFunction property must be defined."); else if (this.engine != null) {
if (this.engine != null) {
loadScripts(this.engine); loadScripts(this.engine);
} }
else { else {
setEngine(createEngineFromName()); setEngine(createEngineFromName());
} }
Assert.isTrue(this.renderFunction != null, "The 'renderFunction' property must be defined.");
}
protected ScriptEngine getEngine() {
if (Boolean.FALSE.equals(this.sharedEngine)) {
ScriptEngine engine = this.engineHolder.get();
if (engine == null) {
engine = createEngineFromName();
this.engineHolder.set(engine);
}
return engine;
}
else {
// Simply return the configured ScriptEngine...
return this.engine;
}
}
protected ScriptEngine createEngineFromName() {
ScriptEngine engine = new ScriptEngineManager().getEngineByName(this.engineName);
if (engine == null) {
throw new IllegalStateException("No engine with name '" + this.engineName + "' found");
}
loadScripts(engine);
return engine;
}
protected void loadScripts(ScriptEngine engine) {
if (this.scripts != null) {
try {
for (String script : this.scripts) {
Resource resource = this.resourceLoader.getResource(script);
if (!resource.exists()) {
throw new IllegalStateException("Resource " + script + " not found");
}
engine.eval(new InputStreamReader(resource.getInputStream()));
}
}
catch (Exception ex) {
throw new IllegalStateException("Failed to load script", ex);
}
}
} }
protected ClassLoader createClassLoader() { protected ClassLoader createClassLoader() {
@ -257,8 +256,9 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
} }
} }
} }
} catch (IOException e) { }
throw new IllegalStateException("Cannot create class loader: " + e.getMessage()); catch (IOException ex) {
throw new IllegalStateException("Cannot create class loader: " + ex.getMessage());
} }
ClassLoader classLoader = getApplicationContext().getClassLoader(); ClassLoader classLoader = getApplicationContext().getClassLoader();
return (urls.size() > 0 ? new URLClassLoader(urls.toArray(new URL[urls.size()]), classLoader) : classLoader); return (urls.size() > 0 ? new URLClassLoader(urls.toArray(new URL[urls.size()]), classLoader) : classLoader);
@ -275,30 +275,33 @@ public class ScriptTemplateView extends AbstractUrlBasedView {
} }
} }
@Override @Override
protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { protected void renderMergedOutputModel(
Assert.notNull("Render function must not be null", this.renderFunction); Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
try { try {
ScriptEngine engine = getEngine();
Invocable invocable = (Invocable) engine;
String template = getTemplate(getUrl()); String template = getTemplate(getUrl());
Object html; Object html;
if (this.renderObject != null) { if (this.renderObject != null) {
Object thiz = engine.eval(this.renderObject); Object thiz = engine.eval(this.renderObject);
html = ((Invocable)getEngine()).invokeMethod(thiz, this.renderFunction, template, model); html = invocable.invokeMethod(thiz, this.renderFunction, template, model);
} }
else { else {
html = ((Invocable)getEngine()).invokeFunction(this.renderFunction, template, model); html = invocable.invokeFunction(this.renderFunction, template, model);
} }
response.getWriter().write(String.valueOf(html)); response.getWriter().write(String.valueOf(html));
} }
catch (Exception e) { catch (Exception ex) {
throw new IllegalStateException("failed to render template", e); throw new IllegalStateException("Failed to render script template", ex);
} }
} }
protected String getTemplate(String path) throws IOException { protected String getTemplate(String path) throws IOException {
Resource resource = this.resourceLoader.getResource(path); Resource resource = this.resourceLoader.getResource(path);
Assert.state(resource.exists(), "Resource " + path + " not found.");
return StreamUtils.copyToString(resource.getInputStream(), this.charset); return StreamUtils.copyToString(resource.getInputStream(), this.charset);
} }
} }

View File

@ -19,9 +19,8 @@ package org.springframework.web.servlet.view.script;
import org.springframework.web.servlet.view.UrlBasedViewResolver; import org.springframework.web.servlet.view.UrlBasedViewResolver;
/** /**
* Convenience subclass of * Convenience subclass of {@link UrlBasedViewResolver} that supports
* {@link org.springframework.web.servlet.view.UrlBasedViewResolver} * {@link ScriptTemplateView} and custom subclasses of it.
* that supports {@link ScriptTemplateView} and custom subclasses of it.
* *
* <p>The view class for all views created by this resolver can be specified * <p>The view class for all views created by this resolver can be specified
* via the {@link #setViewClass(Class)} property. * via the {@link #setViewClass(Class)} property.

View File

@ -0,0 +1,6 @@
/**
* Support classes for views based on the JSR-223 script engine abstraction
* (as included in Java 6+), e.g. using JavaScript via Nashorn on JDK 8.
* Contains a View implementation for scripted templates.
*/
package org.springframework.web.servlet.view.script;

View File

@ -31,14 +31,15 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.servlet.RequestDispatcher; import javax.servlet.RequestDispatcher;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.joda.time.LocalDate; import org.joda.time.LocalDate;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -56,6 +57,7 @@ import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO; import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.format.annotation.NumberFormat; import org.springframework.format.annotation.NumberFormat;
import org.springframework.format.support.FormattingConversionServiceFactoryBean; import org.springframework.format.support.FormattingConversionServiceFactoryBean;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter; import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
@ -75,7 +77,6 @@ import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.http.CacheControl;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.ServletWebRequest;
@ -141,11 +142,6 @@ import org.springframework.web.servlet.view.velocity.VelocityConfigurer;
import org.springframework.web.servlet.view.velocity.VelocityViewResolver; import org.springframework.web.servlet.view.velocity.VelocityViewResolver;
import org.springframework.web.util.UrlPathHelper; import org.springframework.web.util.UrlPathHelper;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -807,7 +803,7 @@ public class MvcNamespaceTests {
assertEquals("render", scriptTemplateConfigurer.getRenderFunction()); assertEquals("render", scriptTemplateConfigurer.getRenderFunction());
assertEquals(StandardCharsets.ISO_8859_1, scriptTemplateConfigurer.getCharset()); assertEquals(StandardCharsets.ISO_8859_1, scriptTemplateConfigurer.getCharset());
assertEquals("classpath:", scriptTemplateConfigurer.getResourceLoaderPath()); assertEquals("classpath:", scriptTemplateConfigurer.getResourceLoaderPath());
assertFalse(scriptTemplateConfigurer.isShareEngine()); assertFalse(scriptTemplateConfigurer.isSharedEngine());
String[] scripts = { "org/springframework/web/servlet/view/script/nashorn/render.js" }; String[] scripts = { "org/springframework/web/servlet/view/script/nashorn/render.js" };
accessor = new DirectFieldAccessor(scriptTemplateConfigurer); accessor = new DirectFieldAccessor(scriptTemplateConfigurer);
assertArrayEquals(scripts, (String[]) accessor.getPropertyValue("scripts")); assertArrayEquals(scripts, (String[]) accessor.getPropertyValue("scripts"));

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2014 the original author or authors. * Copyright 2002-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.web.servlet.view.script; package org.springframework.web.servlet.view.script;
import org.junit.Assert; import org.junit.Assert;

View File

@ -28,19 +28,18 @@ import javax.script.Invocable;
import javax.script.ScriptEngine; import javax.script.ScriptEngine;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.*;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.DirectFieldAccessor;
import org.springframework.context.ApplicationContextException; import org.springframework.context.ApplicationContextException;
import org.springframework.context.support.StaticApplicationContext; import org.springframework.context.support.StaticApplicationContext;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
import static org.mockito.Mockito.mock;
/** /**
* Unit tests for {@link ScriptTemplateView}. * Unit tests for {@link ScriptTemplateView}.
* *
@ -149,11 +148,12 @@ public class ScriptTemplateViewTests {
public void nonInvocableScriptEngine() throws Exception { public void nonInvocableScriptEngine() throws Exception {
try { try {
this.view.setEngine(mock(ScriptEngine.class)); this.view.setEngine(mock(ScriptEngine.class));
} catch(IllegalArgumentException ex) { fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), containsString("instance")); assertThat(ex.getMessage(), containsString("instance"));
return; return;
} }
fail();
} }
@Test @Test
@ -161,11 +161,11 @@ public class ScriptTemplateViewTests {
this.view.setEngine(mock(InvocableScriptEngine.class)); this.view.setEngine(mock(InvocableScriptEngine.class));
try { try {
this.view.setApplicationContext(this.applicationContext); this.view.setApplicationContext(this.applicationContext);
} catch(IllegalStateException ex) { fail("Should have thrown IllegalArgumentException");
assertThat(ex.getMessage(), containsString("renderFunction")); }
return; catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), containsString("renderFunction"));
} }
fail();
} }
@Test @Test
@ -175,11 +175,11 @@ public class ScriptTemplateViewTests {
this.view.setRenderFunction("render"); this.view.setRenderFunction("render");
try { try {
this.view.setApplicationContext(this.applicationContext); this.view.setApplicationContext(this.applicationContext);
} catch(IllegalStateException ex) { fail("Should have thrown IllegalArgumentException");
assertThat(ex.getMessage(), containsString("engine or engineName")); }
return; catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), containsString("'engine' or 'engineName'"));
} }
fail();
} }
@Test @Test
@ -189,7 +189,9 @@ public class ScriptTemplateViewTests {
this.view.setSharedEngine(false); this.view.setSharedEngine(false);
try { try {
this.view.setApplicationContext(this.applicationContext); this.view.setApplicationContext(this.applicationContext);
} catch(IllegalStateException ex) { fail("Should have thrown IllegalArgumentException");
}
catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), containsString("sharedEngine")); assertThat(ex.getMessage(), containsString("sharedEngine"));
return; return;
} }