1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts2.jasper;
19
20 import com.opensymphony.xwork2.util.finder.ClassLoaderInterface;
21 import com.opensymphony.xwork2.util.logging.Logger;
22 import com.opensymphony.xwork2.util.logging.LoggerFactory;
23 import org.apache.struts2.jasper.compiler.Compiler;
24 import org.apache.struts2.jasper.compiler.*;
25 import org.apache.struts2.jasper.servlet.JasperLoader;
26 import org.apache.struts2.jasper.servlet.JspServletWrapper;
27 import org.apache.commons.lang.xwork.StringUtils;
28
29 import javax.servlet.ServletContext;
30 import javax.servlet.jsp.tagext.TagInfo;
31 import java.io.FileNotFoundException;
32 import java.io.IOException;
33 import java.net.MalformedURLException;
34 import java.net.URL;
35 import java.net.URLClassLoader;
36 import java.util.Hashtable;
37 import java.util.Set;
38
39 /***
40 * A place holder for various things that are used through out the JSP
41 * engine. This is a per-request/per-context data structure. Some of
42 * the instance variables are set at different points.
43 * <p/>
44 * Most of the path-related stuff is here - mangling names, versions, dirs,
45 * loading resources and dealing with uris.
46 *
47 * @author Anil K. Vijendran
48 * @author Harish Prabandham
49 * @author Pierre Delisle
50 * @author Costin Manolache
51 * @author Kin-man Chung
52 */
53 public class JspCompilationContext {
54
55 private Logger log = LoggerFactory.getLogger(JspCompilationContext.class);
56
57 private Hashtable tagFileJarUrls;
58 private boolean isPackagedTagFile;
59
60 private String className;
61 private String jspUri;
62 private boolean isErrPage;
63 private String basePackageName;
64 private String derivedPackageName;
65 private String servletJavaFileName;
66 private String javaPath;
67 private String classFileName;
68 private String contentType;
69 private ServletWriter writer;
70 private Options options;
71 private JspServletWrapper jsw;
72 private Compiler jspCompiler;
73 private String classPath;
74
75
76 private ServletContext context;
77 private URLClassLoader loader;
78
79 private JspRuntimeContext rctxt;
80
81 private int removed = 0;
82
83 private URLClassLoader jspLoader;
84 private URL baseUrl;
85 private Class servletClass;
86
87 private boolean isTagFile;
88 private boolean protoTypeMode;
89 private TagInfo tagInfo;
90 private URL tagFileJarUrl;
91
92 private String sourceCode;
93 private ClassLoaderInterface classLoaderInterface;
94
95 public String getSourceCode() {
96 return sourceCode;
97 }
98
99 public void setSourceCode(String sourceCode) {
100 this.sourceCode = sourceCode;
101 }
102
103 public JspCompilationContext(String jspUri,
104 boolean isErrPage,
105 Options options,
106 ServletContext context,
107 JspServletWrapper jsw,
108 JspRuntimeContext rctxt,
109 ClassLoaderInterface classLoaderInterface) {
110
111 this.jspUri = canonicalURI(jspUri);
112 this.isErrPage = isErrPage;
113 this.options = options;
114 this.jsw = jsw;
115 this.context = context;
116
117 this.rctxt = rctxt;
118 this.tagFileJarUrls = new Hashtable();
119 this.basePackageName = Constants.JSP_PACKAGE_NAME;
120 this.classLoaderInterface = classLoaderInterface;
121 }
122
123 public JspCompilationContext(String tagfile,
124 TagInfo tagInfo,
125 Options options,
126 ServletContext context,
127 JspServletWrapper jsw,
128 JspRuntimeContext rctxt,
129 URL tagFileJarUrl) {
130 this(tagfile, false, options, context, jsw, rctxt, null);
131 this.isTagFile = true;
132 this.tagInfo = tagInfo;
133 this.tagFileJarUrl = tagFileJarUrl;
134 if (tagFileJarUrl != null) {
135 isPackagedTagFile = true;
136 }
137 }
138
139
140
141 /*** ---------- Class path and loader ---------- */
142
143 /***
144 * The classpath that is passed off to the Java compiler.
145 */
146 public String getClassPath() {
147 if (classPath != null)
148 return classPath;
149 return rctxt.getClassPath();
150 }
151
152 /***
153 * The classpath that is passed off to the Java compiler.
154 */
155 public void setClassPath(String classPath) {
156 this.classPath = classPath;
157 }
158
159 /***
160 * What class loader to use for loading classes while compiling
161 * this JSP?
162 */
163 public ClassLoader getClassLoader() {
164 if (loader != null)
165 return loader;
166 return rctxt.getParentClassLoader();
167 }
168
169 public void setClassLoader(URLClassLoader loader) {
170 this.loader = loader;
171 }
172
173 public ClassLoader getJspLoader() {
174 if (jspLoader == null) {
175 jspLoader = new JasperLoader
176 (new URL[]{baseUrl},
177 getClassLoader(),
178 rctxt.getPermissionCollection(),
179 rctxt.getCodeSource());
180 }
181 return jspLoader;
182 }
183
184 /*** ---------- Input/Output ---------- */
185
186 /***
187 * Create a "Compiler" object based on some init param data. This
188 * is not done yet. Right now we're just hardcoding the actual
189 * compilers that are created.
190 */
191 public Compiler createCompiler() throws JasperException {
192 jspCompiler = new CustomCompiler();
193 jspCompiler.init(this, jsw);
194 return jspCompiler;
195 }
196
197 private Compiler createCompiler(String className) {
198 Compiler compiler = null;
199 try {
200 compiler = (Compiler) Class.forName(className).newInstance();
201 } catch (Throwable t) {
202 if (log.isDebugEnabled()) {
203 log.debug(Localizer.getMessage("jsp.error.compiler"), t);
204 }
205 }
206 return compiler;
207 }
208
209 public Compiler getCompiler() {
210 return jspCompiler;
211 }
212
213 /*** ---------- Access resources in the webapp ---------- */
214
215 /***
216 * Gets a resource as a stream, relative to the meanings of this
217 * context's implementation.
218 *
219 * @return a null if the resource cannot be found or represented
220 * as an InputStream.
221 */
222 public java.io.InputStream getResourceAsStream(String res) {
223 try {
224 return classLoaderInterface.getResourceAsStream(canonicalURI(StringUtils.removeStart(res, "/")));
225 } catch (IOException e) {
226 throw new RuntimeException(e);
227 }
228 }
229
230
231 public URL getResource(String res) throws MalformedURLException {
232 return classLoaderInterface.getResource(canonicalURI(StringUtils.removeStart(res, "/")));
233 }
234
235 public Set getResourcePaths(String path) {
236 return context.getResourcePaths(canonicalURI(path));
237 }
238
239 /***
240 * Gets the actual path of a URI relative to the context of
241 * the compilation.
242 */
243 public String getRealPath(String path) {
244 if (context != null) {
245 return context.getRealPath(path);
246 }
247 return path;
248 }
249
250 /***
251 * Returns the tag-file-name-to-JAR-file map of this compilation unit,
252 * which maps tag file names to the JAR files in which the tag files are
253 * packaged.
254 * <p/>
255 * The map is populated when parsing the tag-file elements of the TLDs
256 * of any imported taglibs.
257 */
258 public Hashtable getTagFileJarUrls() {
259 return this.tagFileJarUrls;
260 }
261
262 /***
263 * Returns the JAR file in which the tag file for which this
264 * JspCompilationContext was created is packaged, or null if this
265 * JspCompilationContext does not correspond to a tag file, or if the
266 * corresponding tag file is not packaged in a JAR.
267 */
268 public URL getTagFileJarUrl() {
269 return this.tagFileJarUrl;
270 }
271
272
273
274 /***
275 * Just the class name (does not include package name) of the
276 * generated class.
277 */
278 public String getServletClassName() {
279
280 if (className != null) {
281 return className;
282 }
283
284 if (isTagFile) {
285 className = tagInfo.getTagClassName();
286 int lastIndex = className.lastIndexOf('.');
287 if (lastIndex != -1) {
288 className = className.substring(lastIndex + 1);
289 }
290 } else {
291 int iSep = jspUri.lastIndexOf('/') + 1;
292 className = JspUtil.makeJavaIdentifier(jspUri.substring(iSep));
293 }
294 return className;
295 }
296
297 public void setServletClassName(String className) {
298 this.className = className;
299 }
300
301 /***
302 * Path of the JSP URI. Note that this is not a file name. This is
303 * the context rooted URI of the JSP file.
304 */
305 public String getJspFile() {
306 return jspUri;
307 }
308
309 /***
310 * Are we processing something that has been declared as an
311 * errorpage?
312 */
313 public boolean isErrorPage() {
314 return isErrPage;
315 }
316
317 public void setErrorPage(boolean isErrPage) {
318 this.isErrPage = isErrPage;
319 }
320
321 public boolean isTagFile() {
322 return isTagFile;
323 }
324
325 public TagInfo getTagInfo() {
326 return tagInfo;
327 }
328
329 public void setTagInfo(TagInfo tagi) {
330 tagInfo = tagi;
331 }
332
333 /***
334 * True if we are compiling a tag file in prototype mode.
335 * ie we only generate codes with class for the tag handler with empty
336 * method bodies.
337 */
338 public boolean isPrototypeMode() {
339 return protoTypeMode;
340 }
341
342 public void setPrototypeMode(boolean pm) {
343 protoTypeMode = pm;
344 }
345
346 /***
347 * Package name for the generated class is make up of the base package
348 * name, which is user settable, and the derived package name. The
349 * derived package name directly mirrors the file heirachy of the JSP page.
350 */
351 public String getServletPackageName() {
352 if (isTagFile()) {
353 String className = tagInfo.getTagClassName();
354 int lastIndex = className.lastIndexOf('.');
355 String pkgName = "";
356 if (lastIndex != -1) {
357 pkgName = className.substring(0, lastIndex);
358 }
359 return pkgName;
360 } else {
361 String dPackageName = getDerivedPackageName();
362 if (dPackageName.length() == 0) {
363 return basePackageName;
364 }
365 return basePackageName + '.' + getDerivedPackageName();
366 }
367
368 }
369
370 private String getDerivedPackageName() {
371 if (derivedPackageName == null) {
372 int iSep = jspUri.lastIndexOf('/');
373 derivedPackageName = (iSep > 0) ?
374 JspUtil.makeJavaPackage(jspUri.substring(0, iSep)) : "";
375 }
376 return derivedPackageName;
377 }
378
379 /***
380 * The package name into which the servlet class is generated.
381 */
382 public void setServletPackageName(String servletPackageName) {
383 this.basePackageName = servletPackageName;
384 }
385
386 /***
387 * Full path name of the Java file into which the servlet is being
388 * generated.
389 */
390 public String getServletJavaFileName() {
391
392 if (servletJavaFileName == null) {
393 servletJavaFileName =
394 getServletClassName() + ".java";
395 }
396 return servletJavaFileName;
397 }
398
399 public void setServletJavaFileName(String servletJavaFileName) {
400 this.servletJavaFileName = servletJavaFileName;
401 }
402
403 /***
404 * Get hold of the Options object for this context.
405 */
406 public Options getOptions() {
407 return options;
408 }
409
410 public ServletContext getServletContext() {
411 return context;
412 }
413
414 public JspRuntimeContext getRuntimeContext() {
415 return rctxt;
416 }
417
418 /***
419 * Path of the Java file relative to the work directory.
420 */
421 public String getJavaPath() {
422
423 if (javaPath != null) {
424 return javaPath;
425 }
426
427 if (isTagFile()) {
428 String tagName = tagInfo.getTagClassName();
429 javaPath = tagName.replace('.', '/') + ".java";
430 } else {
431 javaPath = getServletPackageName().replace('.', '/') + '/' +
432 getServletClassName() + ".java";
433 }
434 return javaPath;
435 }
436
437 public String getClassFileName() {
438
439 if (classFileName == null) {
440 classFileName = getServletClassName() + ".class";
441 }
442 return classFileName;
443 }
444
445 /***
446 * Get the content type of this JSP.
447 * <p/>
448 * Content type includes content type and encoding.
449 */
450 public String getContentType() {
451 return contentType;
452 }
453
454 public void setContentType(String contentType) {
455 this.contentType = contentType;
456 }
457
458 /***
459 * Where is the servlet being generated?
460 */
461 public ServletWriter getWriter() {
462 return writer;
463 }
464
465 public void setWriter(ServletWriter writer) {
466 this.writer = writer;
467 }
468
469 /***
470 * Gets the 'location' of the TLD associated with the given taglib 'uri'.
471 *
472 * @return An array of two Strings: The first element denotes the real
473 * path to the TLD. If the path to the TLD points to a jar file, then the
474 * second element denotes the name of the TLD entry in the jar file.
475 * Returns null if the given uri is not associated with any tag library
476 * 'exposed' in the web application.
477 */
478 public String[] getTldLocation(String uri) throws JasperException {
479 String[] location =
480 getOptions().getTldLocationsCache().getLocation(uri);
481 return location;
482 }
483
484 /***
485 * Are we keeping generated code around?
486 */
487 public boolean keepGenerated() {
488 return getOptions().getKeepGenerated();
489 }
490
491
492
493 public void incrementRemoved() {
494 if (removed > 1) {
495 jspCompiler.removeGeneratedFiles();
496 if (rctxt != null)
497 rctxt.removeWrapper(jspUri);
498 }
499 removed++;
500 }
501
502 public boolean isRemoved() {
503 if (removed > 1) {
504 return true;
505 }
506 return false;
507 }
508
509
510
511 public void compile() throws JasperException, FileNotFoundException {
512 createCompiler();
513 if (isPackagedTagFile || jspCompiler.isOutDated()) {
514 try {
515 jspLoader = null;
516 jspCompiler.compile();
517 jsw.setReload(true);
518 jsw.setCompilationException(null);
519 } catch (JasperException ex) {
520
521 jsw.setCompilationException(ex);
522 throw ex;
523 } catch (Exception ex) {
524 ex.printStackTrace();
525 JasperException je = new JasperException(
526 Localizer.getMessage("jsp.error.unable.compile"),
527 ex);
528
529 jsw.setCompilationException(je);
530 throw je;
531 }
532 }
533 }
534
535 public String getJspUri() {
536 return jspUri;
537 }
538
539
540
541 public Class load()
542 throws JasperException, FileNotFoundException {
543 try {
544 getJspLoader();
545
546 String name;
547 if (isTagFile()) {
548 name = tagInfo.getTagClassName();
549 } else {
550 name = getServletPackageName() + "." + getServletClassName();
551 }
552 servletClass = jspLoader.loadClass(name);
553 } catch (ClassNotFoundException cex) {
554 throw new JasperException(Localizer.getMessage("jsp.error.unable.load"),
555 cex);
556 } catch (Exception ex) {
557 throw new JasperException(Localizer.getMessage("jsp.error.unable.compile"),
558 ex);
559 }
560 removed = 0;
561 return servletClass;
562 }
563
564
565
566 static Object outputDirLock = new Object();
567
568 private static final boolean isPathSeparator(char c) {
569 return (c == '/' || c == '//');
570 }
571
572 public static final String canonicalURI(String s) {
573 if (s == null) return null;
574 StringBuffer result = new StringBuffer();
575 final int len = s.length();
576 int pos = 0;
577 while (pos < len) {
578 char c = s.charAt(pos);
579 if (isPathSeparator(c)) {
580
581
582
583
584 while (pos + 1 < len && isPathSeparator(s.charAt(pos + 1))) {
585 ++pos;
586 }
587
588 if (pos + 1 < len && s.charAt(pos + 1) == '.') {
589
590
591
592 if (pos + 2 >= len) break;
593
594 switch (s.charAt(pos + 2)) {
595
596
597
598
599 case '/':
600 case '//':
601 pos += 2;
602 continue;
603
604
605
606
607
608 case '.':
609
610 if (pos + 3 < len && isPathSeparator(s.charAt(pos + 3))) {
611 pos += 3;
612 int separatorPos = result.length() - 1;
613 while (separatorPos >= 0 &&
614 !isPathSeparator(result
615 .charAt(separatorPos))) {
616 --separatorPos;
617 }
618 if (separatorPos >= 0)
619 result.setLength(separatorPos);
620 continue;
621 }
622 }
623 }
624 }
625 result.append(c);
626 ++pos;
627 }
628 return result.toString();
629 }
630 }
631