-
Notifications
You must be signed in to change notification settings - Fork 682
/
TemplateLoader.java
297 lines (280 loc) · 11.6 KB
/
TemplateLoader.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
package play.templates;
import java.nio.file.InvalidPathException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import play.Logger;
import play.Play;
import play.exceptions.TemplateCompilationException;
import play.exceptions.TemplateNotFoundException;
import play.vfs.VirtualFile;
public class TemplateLoader {
protected static final Map<String, BaseTemplate> templates = new HashMap<>();
/**
* See getUniqueNumberForTemplateFile() for more info
*/
private static final AtomicLong nextUniqueNumber = new AtomicLong(1000);// we start on 1000
private static final Map<String, String> templateFile2UniqueNumber = Collections.synchronizedMap(new HashMap<String, String>());
/**
* All loaded templates is cached in the templates-list using a key. This key is included as part of the classname
* for the generated class for a specific template. The key is included in the classname to make it possible to
* resolve the original template-file from the classname, when creating cleanStackTrace
*
* This method returns a unique representation of the path which is usable as part of a classname
*
* @param path
* Path of the template file
* @return a unique representation of the path which is usable as part of a classname
*/
public static String getUniqueNumberForTemplateFile(String path) {
// a path cannot be a valid classname so we have to convert it somehow.
// If we did some encoding on the path, the result would be at least as long as the path.
// Therefor we assign a unique number to each path the first time we see it, and store it..
// This way, all seen paths gets a unique number. This number is our UniqueValidClassnamePart..
String uniqueNumber = templateFile2UniqueNumber.get(path);
if (uniqueNumber == null) {
// this is the first time we see this path - must assign a unique number to it.
uniqueNumber = Long.toString(nextUniqueNumber.getAndIncrement());
templateFile2UniqueNumber.put(path, uniqueNumber);
}
return uniqueNumber;
}
/**
* Load a template from a virtual file
*
* @param file
* A VirtualFile
* @return The executable template
*/
public static Template load(VirtualFile file) {
// Try with plugin
Template pluginProvided = Play.pluginCollection.loadTemplate(file);
if (pluginProvided != null) {
return pluginProvided;
}
// Use default engine
String fileRelativePath = file.relativePath();
String key = getUniqueNumberForTemplateFile(fileRelativePath);
if (!templates.containsKey(key) || templates.get(key).compiledTemplate == null) {
if (Play.usePrecompiled) {
BaseTemplate template = new GroovyTemplate(
fileRelativePath.replaceAll("\\{(.*)\\}", "from_$1").replace(':', '_').replace("..", "parent"), "");
try {
template.loadPrecompiled();
templates.put(key, template);
return template;
} catch (Exception e) {
Logger.warn(e, "Precompiled template %s not found, trying to load it dynamically...", file.relativePath());
}
}
BaseTemplate template = new GroovyTemplate(fileRelativePath, file.contentAsString());
if (template.loadFromCache()) {
templates.put(key, template);
} else {
templates.put(key, new GroovyTemplateCompiler().compile(file));
}
} else {
BaseTemplate template = templates.get(key);
if (Play.mode == Play.Mode.DEV && template.timestamp < file.lastModified()) {
templates.put(key, new GroovyTemplateCompiler().compile(file));
}
}
if (templates.get(key) == null) {
throw new TemplateNotFoundException(fileRelativePath);
}
return templates.get(key);
}
/**
* Load a template from a String
*
* @param key
* A unique identifier for the template, used for retrieving a cached template
* @param source
* The template source
* @return A Template
*/
public static BaseTemplate load(String key, String source) {
if (!templates.containsKey(key) || templates.get(key).compiledTemplate == null) {
BaseTemplate template = new GroovyTemplate(key, source);
if (template.loadFromCache()) {
templates.put(key, template);
} else {
templates.put(key, new GroovyTemplateCompiler().compile(template));
}
} else {
BaseTemplate template = new GroovyTemplate(key, source);
if (Play.mode == Play.Mode.DEV) {
templates.put(key, new GroovyTemplateCompiler().compile(template));
}
}
if (templates.get(key) == null) {
throw new TemplateNotFoundException(key);
}
return templates.get(key);
}
/**
* Clean the cache for that key Then load a template from a String
*
* @param key
* A unique identifier for the template, used for retrieving a cached template
* @param source
* The template source
* @param reload
* : Indicate if we must clean the cache
* @return A Template
*/
public static BaseTemplate load(String key, String source, boolean reload) {
cleanCompiledCache(key);
return load(key, source);
}
/**
* Load template from a String, but don't cache it
*
* @param source
* The template source
* @return A Template
*/
public static BaseTemplate loadString(String source) {
BaseTemplate template = new GroovyTemplate(source);
return new GroovyTemplateCompiler().compile(template);
}
/**
* Cleans the cache for all templates
*/
public static void cleanCompiledCache() {
templates.clear();
}
/**
* Cleans the specified key from the cache
*
* @param key
* The template key
*/
public static void cleanCompiledCache(String key) {
templates.remove(key);
}
/**
* Load a template
*
* @param path
* The path of the template (ex: Application/index.html)
* @return The executable template
*/
public static Template load(String path) {
Template template = null;
for (VirtualFile vf : Play.templatesPath) {
if (vf == null) {
continue;
}
VirtualFile tf = vf.child(path);
boolean templateExists = tf.exists();
if (!templateExists && Play.usePrecompiled) {
String name = tf.relativePath().replaceAll("\\{(.*)\\}", "from_$1").replace(':', '_').replace("..", "parent");
templateExists = Play.getFile("precompiled/templates/" + name).exists();
}
if (templateExists) {
template = TemplateLoader.load(tf);
break;
}
}
/*
* if (template == null) { //When using the old 'key = (file.relativePath().hashCode() + "").replace("-",
* "M");', //the next line never return anything, since all values written to templates is using the //above
* key. //when using just file.relativePath() as key, the next line start returning stuff.. //therefor I have
* commented it out. template = templates.get(path); }
*/
// TODO: remove ?
if (template == null) {
VirtualFile tf = Play.getVirtualFile(path);
if (tf != null && tf.exists()) {
template = TemplateLoader.load(tf);
} else {
throw new TemplateNotFoundException(path);
}
}
return template;
}
/**
* List all found templates
*
* @return A list of executable templates
*/
public static List<Template> getAllTemplate() {
List<Template> res = new ArrayList<>();
for (VirtualFile virtualFile : Play.templatesPath) {
scan(res, virtualFile);
}
for (VirtualFile root : Play.roots) {
VirtualFile vf = root.child("conf/routes");
if (vf != null && vf.exists()) {
Template template = load(vf);
if (template != null) {
template.compile();
}
}
}
String play_templates_compile = Play.configuration.getProperty("play.templates.compile",
System.getProperty("play.templates.compile", System.getenv("PLAY_TEMPLATES_COMPILE")));
String play_templates_compile_path_separator = Play.configuration.getProperty("play.templates.compile.path.separator",
System.getProperty("play.templates.compile.path.separator", System.getProperty("path.separator")));
if (play_templates_compile != null) {
for (String yamlTemplate : play_templates_compile.split(play_templates_compile_path_separator)) {
VirtualFile vf = null;
for (int retry = 0;; retry++) {
if (retry == 0) {
try {
vf = VirtualFile.open(Play.applicationPath.toPath().resolve(Paths.get(yamlTemplate)).toFile());
} catch (InvalidPathException invalidPathException) {
/* ignored */}
} else if (retry == 1) {
vf = VirtualFile.fromRelativePath(yamlTemplate);
} else {
vf = null;
break;
}
if (vf != null && vf.exists()) {
Template template = load(vf);
if (template != null) {
template.compile();
break;
}
} else {
vf = null;
}
}
if (vf == null) {
Logger.warn(
"A template specified by system environment 'PLAY_YAML_TEMPLATES' does not exist or path is wrong. template: '%s'",
yamlTemplate);
}
}
}
return res;
}
private static void scan(List<Template> templates, VirtualFile current) {
if (!current.isDirectory() && !current.getName().startsWith(".") && !current.getName().endsWith(".scala.html")) {
long start = System.currentTimeMillis();
Template template = load(current);
if (template != null) {
try {
template.compile();
if (Logger.isTraceEnabled()) {
Logger.trace("%sms to load %s", System.currentTimeMillis() - start, current.getName());
}
} catch (TemplateCompilationException e) {
Logger.error("Template %s does not compile at line %d", e.getTemplate().name, e.getLineNumber());
throw e;
}
templates.add(template);
}
} else if (current.isDirectory() && !current.getName().startsWith(".")) {
for (VirtualFile virtualFile : current.list()) {
scan(templates, virtualFile);
}
}
}
}