16
16
17
17
package org .springframework .web .servlet .view .script ;
18
18
19
- import java .io .IOException ;
20
- import java .io .InputStreamReader ;
21
- import java .io .Reader ;
22
- import java .net .URL ;
23
- import java .net .URLClassLoader ;
24
19
import java .nio .charset .Charset ;
25
- import java .util .ArrayList ;
26
- import java .util .List ;
27
20
28
- import javax .script .Invocable ;
29
21
import javax .script .ScriptEngine ;
30
- import javax .script .ScriptEngineManager ;
31
- import javax .script .ScriptException ;
32
-
33
- import org .springframework .beans .factory .InitializingBean ;
34
- import org .springframework .context .ApplicationContext ;
35
- import org .springframework .context .ApplicationContextAware ;
36
- import org .springframework .core .io .DefaultResourceLoader ;
37
- import org .springframework .core .io .Resource ;
38
- import org .springframework .core .io .ResourceLoader ;
39
- import org .springframework .util .Assert ;
40
- import org .springframework .util .StringUtils ;
41
22
42
23
/**
43
24
* An implementation of Spring MVC's {@link ScriptTemplateConfig} for creating
59
40
* }
60
41
* </pre>
61
42
*
43
+ * <p>It is possible to use non thread-safe script engines and templating libraries, like
44
+ * Handlebars or React running on Nashorn, by setting the
45
+ * {@link #setSharedEngine(Boolean) sharedEngine} property to {@code false}.
46
+ *
62
47
* @author Sebastien Deleuze
63
48
* @since 4.2
64
49
* @see ScriptTemplateView
65
50
*/
66
- public class ScriptTemplateConfigurer implements ScriptTemplateConfig , ApplicationContextAware , InitializingBean {
51
+ public class ScriptTemplateConfigurer implements ScriptTemplateConfig {
67
52
68
53
private ScriptEngine engine ;
69
54
70
55
private String engineName ;
71
56
72
- private ApplicationContext applicationContext ;
73
-
74
57
private String [] scripts ;
75
58
76
59
private String renderObject ;
77
60
78
61
private String renderFunction ;
79
62
80
- private Charset charset = Charset . forName ( "UTF-8" ) ;
63
+ private Charset charset ;
81
64
82
- private ResourceLoader resourceLoader ;
65
+ private String resourceLoaderPath ;
83
66
84
- private String resourceLoaderPath = "classpath:" ;
67
+ private Boolean sharedEngine ;
85
68
86
69
/**
87
70
* Set the {@link ScriptEngine} to use by the view.
88
71
* The script engine must implement {@code Invocable}.
89
72
* You must define {@code engine} or {@code engineName}, not both.
73
+ *
74
+ * <p>When the {@code sharedEngine} flag is set to {@code false}, you should not specify
75
+ * the script engine with this setter, but with the {@link #setEngineName(String)}
76
+ * one (since it implies multiple lazy instanciations of the script engine).
77
+ *
78
+ * @see #setEngineName(String)
90
79
*/
91
80
public void setEngine (ScriptEngine engine ) {
92
- Assert .isInstanceOf (Invocable .class , engine );
93
81
this .engine = engine ;
94
82
}
95
83
@@ -102,18 +90,15 @@ public ScriptEngine getEngine() {
102
90
* Set the engine name that will be used to instantiate the {@link ScriptEngine}.
103
91
* The script engine must implement {@code Invocable}.
104
92
* You must define {@code engine} or {@code engineName}, not both.
93
+ * @see #setEngine(ScriptEngine)
105
94
*/
106
95
public void setEngineName (String engineName ) {
107
96
this .engineName = engineName ;
108
97
}
109
98
110
99
@ Override
111
- public void setApplicationContext (ApplicationContext applicationContext ) {
112
- this .applicationContext = applicationContext ;
113
- }
114
-
115
- protected ApplicationContext getApplicationContext () {
116
- return this .applicationContext ;
100
+ public String getEngineName () {
101
+ return this .engineName ;
117
102
}
118
103
119
104
/**
@@ -134,8 +119,8 @@ public void setScripts(String... scriptNames) {
134
119
}
135
120
136
121
@ Override
137
- public String getRenderObject () {
138
- return renderObject ;
122
+ public String [] getScripts () {
123
+ return this . scripts ;
139
124
}
140
125
141
126
/**
@@ -148,8 +133,8 @@ public void setRenderObject(String renderObject) {
148
133
}
149
134
150
135
@ Override
151
- public String getRenderFunction () {
152
- return renderFunction ;
136
+ public String getRenderObject () {
137
+ return this . renderObject ;
153
138
}
154
139
155
140
/**
@@ -164,6 +149,11 @@ public void setRenderFunction(String renderFunction) {
164
149
this .renderFunction = renderFunction ;
165
150
}
166
151
152
+ @ Override
153
+ public String getRenderFunction () {
154
+ return this .renderFunction ;
155
+ }
156
+
167
157
/**
168
158
* Set the charset used to read script and template files.
169
159
* ({@code UTF-8} by default).
@@ -189,69 +179,30 @@ public void setResourceLoaderPath(String resourceLoaderPath) {
189
179
this .resourceLoaderPath = resourceLoaderPath ;
190
180
}
191
181
182
+ @ Override
192
183
public String getResourceLoaderPath () {
193
- return resourceLoaderPath ;
184
+ return this . resourceLoaderPath ;
194
185
}
195
186
196
- @ Override
197
- public ResourceLoader getResourceLoader () {
198
- return resourceLoader ;
187
+ /**
188
+ * When set to {@code false}, use thread-local {@link ScriptEngine} instances instead
189
+ * of one single shared instance. This flag should be set to {@code false} for those
190
+ * using non thread-safe script engines and templating libraries, like Handlebars or
191
+ * React running on Nashorn for example.
192
+ *
193
+ * <p>When this flag is set to {@code false}, the script engine must be specified using
194
+ * {@link #setEngineName(String)}. Using {@link #setEngine(ScriptEngine)} is not
195
+ * possible because multiple instances of the script engine need to be created lazily
196
+ * (one per thread).
197
+ * @see <a href="http://docs.oracle.com/javase/8/docs/api/javax/script/ScriptEngineFactory.html#getParameter-java.lang.String-">THREADING ScriptEngine parameter<a/>
198
+ */
199
+ public void setSharedEngine (Boolean sharedEngine ) {
200
+ this .sharedEngine = sharedEngine ;
199
201
}
200
202
201
203
@ Override
202
- public void afterPropertiesSet () throws Exception {
203
- if (this .engine == null ) {
204
- this .engine = createScriptEngine ();
205
- }
206
- Assert .state (this .renderFunction != null , "renderFunction property must be defined." );
207
- this .resourceLoader = new DefaultResourceLoader (createClassLoader ());
208
- if (this .scripts != null ) {
209
- try {
210
- for (String script : this .scripts ) {
211
- this .engine .eval (read (script ));
212
- }
213
- }
214
- catch (ScriptException e ) {
215
- throw new IllegalStateException ("could not load script" , e );
216
- }
217
- }
218
- }
219
-
220
- protected ClassLoader createClassLoader () throws IOException {
221
- String [] paths = StringUtils .commaDelimitedListToStringArray (this .resourceLoaderPath );
222
- List <URL > urls = new ArrayList <URL >();
223
- for (String path : paths ) {
224
- Resource [] resources = getApplicationContext ().getResources (path );
225
- if (resources .length > 0 ) {
226
- for (Resource resource : resources ) {
227
- if (resource .exists ()) {
228
- urls .add (resource .getURL ());
229
- }
230
- }
231
- }
232
- }
233
- ClassLoader classLoader = getApplicationContext ().getClassLoader ();
234
- return (urls .size () > 0 ? new URLClassLoader (urls .toArray (new URL [urls .size ()]), classLoader ) : classLoader );
235
- }
236
-
237
- private Reader read (String path ) throws IOException {
238
- Resource resource = this .resourceLoader .getResource (path );
239
- Assert .state (resource .exists (), "Resource " + path + " not found." );
240
- return new InputStreamReader (resource .getInputStream ());
241
- }
242
-
243
- protected ScriptEngine createScriptEngine () throws IOException {
244
- if (this .engine != null && this .engineName != null ) {
245
- throw new IllegalStateException ("You should define engine or engineName properties, not both." );
246
- }
247
- if (this .engineName != null ) {
248
- ScriptEngine scriptEngine = new ScriptEngineManager ().getEngineByName (this .engineName );
249
- Assert .state (scriptEngine != null , "No engine \" " + this .engineName + "\" found." );
250
- Assert .state (scriptEngine instanceof Invocable , "Script engine should be instance of Invocable" );
251
- this .engine = scriptEngine ;
252
- }
253
- Assert .state (this .engine != null , "No script engine found, please specify valid engine or engineName properties." );
254
- return this .engine ;
204
+ public Boolean isShareEngine () {
205
+ return this .sharedEngine ;
255
206
}
256
207
257
208
}
0 commit comments