1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts2.jasper.compiler;
19
20 import com.opensymphony.xwork2.util.logging.Logger;
21 import com.opensymphony.xwork2.util.logging.LoggerFactory;
22 import org.apache.struts2.jasper.JasperException;
23 import org.apache.struts2.jasper.JspCompilationContext;
24 import org.apache.struts2.jasper.Options;
25 import org.apache.struts2.jasper.servlet.JspServletWrapper;
26
27 import java.io.*;
28 import java.net.URL;
29 import java.net.URLConnection;
30 import java.util.Iterator;
31 import java.util.List;
32
33 /***
34 * Main JSP compiler class. This class uses Ant for compiling.
35 *
36 * @author Anil K. Vijendran
37 * @author Mandar Raje
38 * @author Pierre Delisle
39 * @author Kin-man Chung
40 * @author Remy Maucherat
41 * @author Mark Roth
42 */
43 public abstract class Compiler {
44 private Logger log = LoggerFactory.getLogger(Compiler.class);
45
46
47
48
49
50 static Object javacLock = new Object();
51
52
53
54
55
56 protected JspCompilationContext ctxt;
57
58 protected ErrorDispatcher errDispatcher;
59 protected PageInfo pageInfo;
60 protected JspServletWrapper jsw;
61 protected TagFileProcessor tfp;
62
63 protected Options options;
64
65 protected Node.Nodes pageNodes;
66
67
68 public void init(JspCompilationContext ctxt, JspServletWrapper jsw) {
69 this.jsw = jsw;
70 this.ctxt = ctxt;
71 this.options = ctxt.getOptions();
72 }
73
74
75
76 /***
77 * <p>Retrieves the parsed nodes of the JSP page, if they are available.
78 * May return null. Used in development mode for generating detailed
79 * error messages. http://issues.apache.org/bugzilla/show_bug.cgi?id=37062.
80 * </p>
81 */
82 public Node.Nodes getPageNodes() {
83 return this.pageNodes;
84 }
85
86 /***
87 * Compile the jsp file into equivalent servlet in .java file
88 *
89 * @return a smap for the current JSP page, if one is generated,
90 * null otherwise
91 */
92 protected String[] generateJava() throws Exception {
93
94 String[] smapStr = null;
95
96 long t1, t2, t3, t4;
97
98 t1 = t2 = t3 = t4 = 0;
99
100 if (log.isDebugEnabled()) {
101 t1 = System.currentTimeMillis();
102 }
103
104
105 pageInfo = new PageInfo(new BeanRepository(ctxt.getClassLoader(),
106 errDispatcher),
107 ctxt.getJspFile());
108
109 JspConfig jspConfig = options.getJspConfig();
110 JspConfig.JspProperty jspProperty =
111 jspConfig.findJspProperty(ctxt.getJspFile());
112
113
114
115
116
117
118 pageInfo.setELIgnored(JspUtil.booleanValue(
119 jspProperty.isELIgnored()));
120 pageInfo.setScriptingInvalid(JspUtil.booleanValue(
121 jspProperty.isScriptingInvalid()));
122 if (jspProperty.getIncludePrelude() != null) {
123 pageInfo.setIncludePrelude(jspProperty.getIncludePrelude());
124 }
125 if (jspProperty.getIncludeCoda() != null) {
126 pageInfo.setIncludeCoda(jspProperty.getIncludeCoda());
127 }
128
129 String javaFileName = ctxt.getServletJavaFileName();
130 ServletWriter writer = null;
131
132 try {
133
134 String javaEncoding = ctxt.getOptions().getJavaEncoding();
135 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
136 OutputStreamWriter osw = null;
137
138 try {
139 osw = new OutputStreamWriter(
140 byteArrayOutputStream, javaEncoding);
141 } catch (UnsupportedEncodingException ex) {
142 errDispatcher.jspError("jsp.error.needAlternateJavaEncoding",
143 javaEncoding);
144 }
145
146 writer = new ServletWriter(new PrintWriter(osw));
147 ctxt.setWriter(writer);
148
149
150 JspUtil.resetTemporaryVariableName();
151
152
153 ParserController parserCtl = new ParserController(ctxt, this);
154 pageNodes = parserCtl.parse(ctxt.getJspFile());
155
156 if (ctxt.isPrototypeMode()) {
157
158 Generator.generate(writer, this, pageNodes);
159 writer.close();
160 writer = null;
161 return null;
162 }
163
164
165 Validator.validate(this, pageNodes);
166
167 if (log.isDebugEnabled()) {
168 t2 = System.currentTimeMillis();
169 }
170
171
172 Collector.collect(this, pageNodes);
173
174
175
176 tfp = new TagFileProcessor();
177 tfp.loadTagFiles(this, pageNodes);
178
179 if (log.isDebugEnabled()) {
180 t3 = System.currentTimeMillis();
181 }
182
183
184 ScriptingVariabler.set(pageNodes, errDispatcher);
185
186
187 TagPluginManager tagPluginManager = options.getTagPluginManager();
188 tagPluginManager.apply(pageNodes, errDispatcher, pageInfo);
189
190
191 TextOptimizer.concatenate(this, pageNodes);
192
193
194 ELFunctionMapper.map(this, pageNodes);
195
196
197 Generator.generate(writer, this, pageNodes);
198 writer.close();
199 writer = null;
200
201
202
203
204 ctxt.setWriter(null);
205 ctxt.setSourceCode(byteArrayOutputStream.toString());
206 if (log.isDebugEnabled()) {
207 t4 = System.currentTimeMillis();
208 log.debug("Generated " + javaFileName + " total="
209 + (t4 - t1) + " generate=" + (t4 - t3)
210 + " validate=" + (t2 - t1));
211 }
212
213 } catch (Exception e) {
214 if (writer != null) {
215 try {
216 writer.close();
217 writer = null;
218 } catch (Exception e1) {
219
220 }
221 }
222
223 new File(javaFileName).delete();
224 throw e;
225 } finally {
226 if (writer != null) {
227 try {
228 writer.close();
229 } catch (Exception e2) {
230
231 }
232 }
233 }
234
235
236 if (!options.isSmapSuppressed()) {
237 smapStr = SmapUtil.generateSmap(ctxt, pageNodes);
238 }
239
240
241
242
243
244
245 tfp.removeProtoTypeFiles(ctxt.getClassFileName());
246
247 return smapStr;
248 }
249
250 /***
251 * Compile the servlet from .java file to .class file
252 */
253 protected abstract void generateClass(String[] smap)
254 throws FileNotFoundException, JasperException, Exception;
255
256
257 /***
258 * Compile the jsp file from the current engine context
259 */
260 public void compile()
261 throws FileNotFoundException, JasperException, Exception {
262 compile(true);
263 }
264
265 /***
266 * Compile the jsp file from the current engine context. As an side-
267 * effect, tag files that are referenced by this page are also compiled.
268 *
269 * @param compileClass If true, generate both .java and .class file
270 * If false, generate only .java file
271 */
272 public void compile(boolean compileClass)
273 throws FileNotFoundException, JasperException, Exception {
274 compile(compileClass, false);
275 }
276
277 /***
278 * Compile the jsp file from the current engine context. As an side-
279 * effect, tag files that are referenced by this page are also compiled.
280 *
281 * @param compileClass If true, generate both .java and .class file
282 * If false, generate only .java file
283 * @param jspcMode true if invoked from JspC, false otherwise
284 */
285 public void compile(boolean compileClass, boolean jspcMode)
286 throws FileNotFoundException, JasperException, Exception {
287 if (errDispatcher == null) {
288 this.errDispatcher = new ErrorDispatcher(jspcMode);
289 }
290
291 try {
292 String[] smap = generateJava();
293 if (compileClass) {
294 generateClass(smap);
295 }
296 } finally {
297 if (tfp != null) {
298 tfp.removeProtoTypeFiles(null);
299 }
300
301
302
303
304 tfp = null;
305 errDispatcher = null;
306 pageInfo = null;
307
308
309
310
311
312 if (!this.options.getDevelopment()) {
313 pageNodes = null;
314 }
315
316 if (ctxt.getWriter() != null) {
317 ctxt.getWriter().close();
318 ctxt.setWriter(null);
319 }
320 }
321 }
322
323 /***
324 * This is a protected method intended to be overridden by
325 * subclasses of Compiler. This is used by the compile method
326 * to do all the compilation.
327 */
328 public boolean isOutDated() {
329 return isOutDated(true);
330 }
331
332 /***
333 * Determine if a compilation is necessary by checking the time stamp
334 * of the JSP page with that of the corresponding .class or .java file.
335 * If the page has dependencies, the check is also extended to its
336 * dependeants, and so on.
337 * This method can by overidden by a subclasses of Compiler.
338 *
339 * @param checkClass If true, check against .class file,
340 * if false, check against .java file.
341 */
342 public boolean isOutDated(boolean checkClass) {
343
344 String jsp = ctxt.getJspFile();
345
346 if (jsw != null
347 && (ctxt.getOptions().getModificationTestInterval() > 0)) {
348
349 if (jsw.getLastModificationTest()
350 + (ctxt.getOptions().getModificationTestInterval() * 1000)
351 > System.currentTimeMillis()) {
352 return false;
353 } else {
354 jsw.setLastModificationTest(System.currentTimeMillis());
355 }
356 }
357
358 long jspRealLastModified = 0;
359 try {
360 URL jspUrl = ctxt.getResource(jsp);
361 if (jspUrl == null) {
362 ctxt.incrementRemoved();
363 return false;
364 }
365 URLConnection uc = jspUrl.openConnection();
366 jspRealLastModified = uc.getLastModified();
367 uc.getInputStream().close();
368 } catch (Exception e) {
369 e.printStackTrace();
370 return true;
371 }
372
373 long targetLastModified = 0;
374 File targetFile;
375
376 if (checkClass) {
377 targetFile = new File(ctxt.getClassFileName());
378 } else {
379 targetFile = new File(ctxt.getServletJavaFileName());
380 }
381
382 if (!targetFile.exists()) {
383 return true;
384 }
385
386 targetLastModified = targetFile.lastModified();
387 if (checkClass && jsw != null) {
388 jsw.setServletClassLastModifiedTime(targetLastModified);
389 }
390 if (targetLastModified < jspRealLastModified) {
391 if (log.isDebugEnabled()) {
392 log.debug("Compiler: outdated: " + targetFile + " " +
393 targetLastModified);
394 }
395 return true;
396 }
397
398
399
400 if (jsw == null) {
401 return false;
402 }
403
404 List depends = jsw.getDependants();
405 if (depends == null) {
406 return false;
407 }
408
409 Iterator it = depends.iterator();
410 while (it.hasNext()) {
411 String include = (String) it.next();
412 try {
413 URL includeUrl = ctxt.getResource(include);
414 if (includeUrl == null) {
415 return true;
416 }
417
418 URLConnection includeUconn = includeUrl.openConnection();
419 long includeLastModified = includeUconn.getLastModified();
420 includeUconn.getInputStream().close();
421
422 if (includeLastModified > targetLastModified) {
423 return true;
424 }
425 } catch (Exception e) {
426 e.printStackTrace();
427 return true;
428 }
429 }
430
431 return false;
432
433 }
434
435
436 /***
437 * Gets the error dispatcher.
438 */
439 public ErrorDispatcher getErrorDispatcher() {
440 return errDispatcher;
441 }
442
443
444 /***
445 * Gets the info about the page under compilation
446 */
447 public PageInfo getPageInfo() {
448 return pageInfo;
449 }
450
451
452 public JspCompilationContext getCompilationContext() {
453 return ctxt;
454 }
455
456
457 /***
458 * Remove generated files
459 */
460 public void removeGeneratedFiles() {
461 try {
462 String classFileName = ctxt.getClassFileName();
463 if (classFileName != null) {
464 File classFile = new File(classFileName);
465 if (log.isDebugEnabled())
466 log.debug("Deleting " + classFile);
467 classFile.delete();
468 }
469 } catch (Exception e) {
470
471 }
472 try {
473 String javaFileName = ctxt.getServletJavaFileName();
474 if (javaFileName != null) {
475 File javaFile = new File(javaFileName);
476 if (log.isDebugEnabled())
477 log.debug("Deleting " + javaFile);
478 javaFile.delete();
479 }
480 } catch (Exception e) {
481
482 }
483 }
484
485 public void removeGeneratedClassFiles() {
486 try {
487 String classFileName = ctxt.getClassFileName();
488 if (classFileName != null) {
489 File classFile = new File(classFileName);
490 if (log.isDebugEnabled())
491 log.debug("Deleting " + classFile);
492 classFile.delete();
493 }
494 } catch (Exception e) {
495
496 }
497 }
498 }