001/* 002 * Copyright 2002-2019 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.web.servlet.view.script; 018 019import java.nio.charset.Charset; 020import java.util.function.Supplier; 021 022import javax.script.Bindings; 023import javax.script.ScriptEngine; 024 025import org.springframework.lang.Nullable; 026 027/** 028 * An implementation of Spring MVC's {@link ScriptTemplateConfig} for creating 029 * a {@code ScriptEngine} for use in a web application. 030 * 031 * <pre class="code"> 032 * // Add the following to an @Configuration class 033 * @Bean 034 * public ScriptTemplateConfigurer mustacheConfigurer() { 035 * ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer(); 036 * configurer.setEngineName("nashorn"); 037 * configurer.setScripts("mustache.js"); 038 * configurer.setRenderObject("Mustache"); 039 * configurer.setRenderFunction("render"); 040 * return configurer; 041 * } 042 * </pre> 043 * 044 * <p><b>NOTE:</b> It is possible to use non thread-safe script engines with 045 * templating libraries not designed for concurrency, like Handlebars or React running on 046 * Nashorn, by setting the {@link #setSharedEngine sharedEngine} property to {@code false}. 047 * 048 * @author Sebastien Deleuze 049 * @since 4.2 050 * @see ScriptTemplateView 051 */ 052public class ScriptTemplateConfigurer implements ScriptTemplateConfig { 053 054 @Nullable 055 private ScriptEngine engine; 056 057 @Nullable 058 private Supplier<ScriptEngine> engineSupplier; 059 060 @Nullable 061 private String engineName; 062 063 @Nullable 064 private Boolean sharedEngine; 065 066 @Nullable 067 private String[] scripts; 068 069 @Nullable 070 private String renderObject; 071 072 @Nullable 073 private String renderFunction; 074 075 @Nullable 076 private String contentType; 077 078 @Nullable 079 private Charset charset; 080 081 @Nullable 082 private String resourceLoaderPath; 083 084 085 /** 086 * Default constructor. 087 */ 088 public ScriptTemplateConfigurer() { 089 } 090 091 /** 092 * Create a new ScriptTemplateConfigurer using the given engine name. 093 */ 094 public ScriptTemplateConfigurer(String engineName) { 095 this.engineName = engineName; 096 } 097 098 099 /** 100 * Set the {@link ScriptEngine} to use by the view. 101 * If {@code renderFunction} is specified, the script engine must implement {@code Invocable}. 102 * You must define {@code engine} or {@code engineName}, not both. 103 * <p>When the {@code sharedEngine} flag is set to {@code false}, you should not specify 104 * the script engine with this setter, but with {@link #setEngineName(String)} 105 * or {@link #setEngineSupplier(Supplier)} since it implies multiple lazy 106 * instantiations of the script engine. 107 * @see #setEngineName(String) 108 * @see #setEngineSupplier(Supplier) 109 */ 110 public void setEngine(@Nullable ScriptEngine engine) { 111 this.engine = engine; 112 } 113 114 @Override 115 @Nullable 116 public ScriptEngine getEngine() { 117 return this.engine; 118 } 119 120 /** 121 * Set the {@link ScriptEngine} supplier to use by the view, usually used with 122 * {@link #setSharedEngine(Boolean)} set to {@code false}. 123 * If {@code renderFunction} is specified, the script engine must implement {@code Invocable}. 124 * You must either define {@code engineSupplier}, {@code engine} or {@code engineName}. 125 * @since 5.2 126 * @see #setEngine(ScriptEngine) 127 * @see #setEngineName(String) 128 */ 129 public void setEngineSupplier(@Nullable Supplier<ScriptEngine> engineSupplier) { 130 this.engineSupplier = engineSupplier; 131 } 132 133 @Override 134 @Nullable 135 public Supplier<ScriptEngine> getEngineSupplier() { 136 return this.engineSupplier; 137 } 138 139 /** 140 * Set the engine name that will be used to instantiate the {@link ScriptEngine}. 141 * If {@code renderFunction} is specified, the script engine must implement {@code Invocable}. 142 * You must define {@code engine} or {@code engineName}, not both. 143 * @see #setEngine(ScriptEngine) 144 * @see #setEngineSupplier(Supplier) 145 */ 146 public void setEngineName(@Nullable String engineName) { 147 this.engineName = engineName; 148 } 149 150 @Override 151 @Nullable 152 public String getEngineName() { 153 return this.engineName; 154 } 155 156 /** 157 * When set to {@code false}, use thread-local {@link ScriptEngine} instances instead 158 * of one single shared instance. This flag should be set to {@code false} for those 159 * using non thread-safe script engines with templating libraries not designed for 160 * concurrency, like Handlebars or React running on Nashorn for example. 161 * <p>When this flag is set to {@code false}, the script engine must be specified using 162 * {@link #setEngineName(String)} or {@link #setEngineSupplier(Supplier)}. 163 * Using {@link #setEngine(ScriptEngine)} is not possible because multiple instances 164 * of the script engine need to be created lazily (one per thread). 165 * @see <a href="https://docs.oracle.com/javase/8/docs/api/javax/script/ScriptEngineFactory.html#getParameter-java.lang.String-">THREADING ScriptEngine parameter</a> 166 */ 167 public void setSharedEngine(@Nullable Boolean sharedEngine) { 168 this.sharedEngine = sharedEngine; 169 } 170 171 @Override 172 @Nullable 173 public Boolean isSharedEngine() { 174 return this.sharedEngine; 175 } 176 177 /** 178 * Set the scripts to be loaded by the script engine (library or user provided). 179 * Since {@code resourceLoaderPath} default value is "classpath:", you can load easily 180 * any script available on the classpath. 181 * <p>For example, in order to use a JavaScript library available as a WebJars dependency 182 * and a custom "render.js" file, you should call 183 * {@code configurer.setScripts("/META-INF/resources/webjars/library/version/library.js", 184 * "com/myproject/script/render.js");}. 185 * @see #setResourceLoaderPath 186 * @see <a href="https://www.webjars.org">WebJars</a> 187 */ 188 public void setScripts(@Nullable String... scriptNames) { 189 this.scripts = scriptNames; 190 } 191 192 @Override 193 @Nullable 194 public String[] getScripts() { 195 return this.scripts; 196 } 197 198 /** 199 * Set the object where the render function belongs (optional). 200 * For example, in order to call {@code Mustache.render()}, {@code renderObject} 201 * should be set to {@code "Mustache"} and {@code renderFunction} to {@code "render"}. 202 */ 203 public void setRenderObject(@Nullable String renderObject) { 204 this.renderObject = renderObject; 205 } 206 207 @Override 208 @Nullable 209 public String getRenderObject() { 210 return this.renderObject; 211 } 212 213 /** 214 * Set the render function name (optional). If not specified, the script templates 215 * will be evaluated with {@link ScriptEngine#eval(String, Bindings)}. 216 * <p>This function will be called with the following parameters: 217 * <ol> 218 * <li>{@code String template}: the template content</li> 219 * <li>{@code Map model}: the view model</li> 220 * <li>{@code RenderingContext context}: the rendering context (since 5.0)</li> 221 * </ol> 222 * @see RenderingContext 223 */ 224 public void setRenderFunction(@Nullable String renderFunction) { 225 this.renderFunction = renderFunction; 226 } 227 228 @Override 229 @Nullable 230 public String getRenderFunction() { 231 return this.renderFunction; 232 } 233 234 /** 235 * Set the content type to use for the response. 236 * ({@code text/html} by default). 237 * @since 4.2.1 238 */ 239 public void setContentType(@Nullable String contentType) { 240 this.contentType = contentType; 241 } 242 243 /** 244 * Return the content type to use for the response. 245 * @since 4.2.1 246 */ 247 @Override 248 @Nullable 249 public String getContentType() { 250 return this.contentType; 251 } 252 253 /** 254 * Set the charset used to read script and template files. 255 * ({@code UTF-8} by default). 256 */ 257 public void setCharset(@Nullable Charset charset) { 258 this.charset = charset; 259 } 260 261 @Override 262 @Nullable 263 public Charset getCharset() { 264 return this.charset; 265 } 266 267 /** 268 * Set the resource loader path(s) via a Spring resource location. 269 * Accepts multiple locations as a comma-separated list of paths. 270 * Standard URLs like "file:" and "classpath:" and pseudo URLs are supported 271 * as understood by Spring's {@link org.springframework.core.io.ResourceLoader}. 272 * Relative paths are allowed when running in an ApplicationContext. 273 * <p>Default is "classpath:". 274 */ 275 public void setResourceLoaderPath(@Nullable String resourceLoaderPath) { 276 this.resourceLoaderPath = resourceLoaderPath; 277 } 278 279 @Override 280 @Nullable 281 public String getResourceLoaderPath() { 282 return this.resourceLoaderPath; 283 } 284 285}