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