001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
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: * http://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:
017: package org.apache.jasper;
018:
019: import java.io.File;
020: import java.io.FileNotFoundException;
021: import java.net.MalformedURLException;
022: import java.net.URL;
023: import java.net.URLClassLoader;
024: import java.util.Hashtable;
025: import java.util.Set;
026:
027: import javax.servlet.ServletContext;
028: import javax.servlet.jsp.tagext.TagInfo;
029:
030: import org.apache.jasper.compiler.Compiler;
031: import org.apache.jasper.compiler.JspRuntimeContext;
032: import org.apache.jasper.compiler.JspUtil;
033: import org.apache.jasper.compiler.Localizer;
034: import org.apache.jasper.compiler.ServletWriter;
035: import org.apache.jasper.servlet.JasperLoader;
036: import org.apache.jasper.servlet.JspServletWrapper;
037:
038: /**
039: * A place holder for various things that are used through out the JSP
040: * engine. This is a per-request/per-context data structure. Some of
041: * the instance variables are set at different points.
042: *
043: * Most of the path-related stuff is here - mangling names, versions, dirs,
044: * loading resources and dealing with uris.
045: *
046: * @author Anil K. Vijendran
047: * @author Harish Prabandham
048: * @author Pierre Delisle
049: * @author Costin Manolache
050: * @author Kin-man Chung
051: */
052: public class JspCompilationContext {
053:
054: private Hashtable tagFileJarUrls;
055: private boolean isPackagedTagFile;
056:
057: private String className;
058: private String jspUri;
059: private boolean isErrPage;
060: private String basePackageName;
061: private String derivedPackageName;
062: private String servletJavaFileName;
063: private String javaPath;
064: private String classFileName;
065: private String contentType;
066: private ServletWriter writer;
067: private Options options;
068: private JspServletWrapper jsw;
069: private Compiler jspCompiler;
070: private String classPath;
071:
072: private String baseURI;
073: private String baseOutputDir;
074: private String outputDir;
075: private ServletContext context;
076: private URLClassLoader loader;
077:
078: private JspRuntimeContext rctxt;
079:
080: private int removed = 0;
081:
082: private URLClassLoader jspLoader;
083: private URL baseUrl;
084: private Class servletClass;
085:
086: private boolean isTagFile;
087: private boolean protoTypeMode;
088: private TagInfo tagInfo;
089: private URL tagFileJarUrl;
090:
091: // jspURI _must_ be relative to the context
092: public JspCompilationContext(String jspUri, boolean isErrPage,
093: Options options, ServletContext context,
094: JspServletWrapper jsw, JspRuntimeContext rctxt) {
095:
096: this .jspUri = canonicalURI(jspUri);
097: this .isErrPage = isErrPage;
098: this .options = options;
099: this .jsw = jsw;
100: this .context = context;
101:
102: this .baseURI = jspUri.substring(0, jspUri.lastIndexOf('/') + 1);
103: // hack fix for resolveRelativeURI
104: if (baseURI == null) {
105: baseURI = "/";
106: } else if (baseURI.charAt(0) != '/') {
107: // strip the basde slash since it will be combined with the
108: // uriBase to generate a file
109: baseURI = "/" + baseURI;
110: }
111: if (baseURI.charAt(baseURI.length() - 1) != '/') {
112: baseURI += '/';
113: }
114:
115: this .rctxt = rctxt;
116: this .tagFileJarUrls = new Hashtable();
117: this .basePackageName = Constants.JSP_PACKAGE_NAME;
118: }
119:
120: public JspCompilationContext(String tagfile, TagInfo tagInfo,
121: Options options, ServletContext context,
122: JspServletWrapper jsw, JspRuntimeContext rctxt,
123: URL tagFileJarUrl) {
124: this (tagfile, false, options, context, jsw, rctxt);
125: this .isTagFile = true;
126: this .tagInfo = tagInfo;
127: this .tagFileJarUrl = tagFileJarUrl;
128: if (tagFileJarUrl != null) {
129: isPackagedTagFile = true;
130: }
131: }
132:
133: /* ==================== Methods to override ==================== */
134:
135: /** ---------- Class path and loader ---------- */
136:
137: /**
138: * The classpath that is passed off to the Java compiler.
139: */
140: public String getClassPath() {
141: if (classPath != null)
142: return classPath;
143: return rctxt.getClassPath();
144: }
145:
146: /**
147: * The classpath that is passed off to the Java compiler.
148: */
149: public void setClassPath(String classPath) {
150: this .classPath = classPath;
151: }
152:
153: /**
154: * What class loader to use for loading classes while compiling
155: * this JSP?
156: */
157: public ClassLoader getClassLoader() {
158: if (loader != null)
159: return loader;
160: return rctxt.getParentClassLoader();
161: }
162:
163: public void setClassLoader(URLClassLoader loader) {
164: this .loader = loader;
165: }
166:
167: /** ---------- Input/Output ---------- */
168:
169: /**
170: * The output directory to generate code into. The output directory
171: * is make up of the scratch directory, which is provide in Options,
172: * plus the directory derived from the package name.
173: */
174: public String getOutputDir() {
175: if (outputDir == null) {
176: createOutputDir();
177: }
178:
179: return outputDir;
180: }
181:
182: /**
183: * Create a "Compiler" object based on some init param data. This
184: * is not done yet. Right now we're just hardcoding the actual
185: * compilers that are created.
186: */
187: public Compiler createCompiler() throws JasperException {
188: if (jspCompiler != null) {
189: return jspCompiler;
190: }
191: jspCompiler = new Compiler(this , jsw);
192: return jspCompiler;
193: }
194:
195: public Compiler getCompiler() {
196: return jspCompiler;
197: }
198:
199: /** ---------- Access resources in the webapp ---------- */
200:
201: /**
202: * Get the full value of a URI relative to this compilations context
203: * uses current file as the base.
204: */
205: public String resolveRelativeUri(String uri) {
206: // sometimes we get uri's massaged from File(String), so check for
207: // a root directory deperator char
208: if (uri.startsWith("/") || uri.startsWith(File.separator)) {
209: return uri;
210: } else {
211: return baseURI + uri;
212: }
213: }
214:
215: /**
216: * Gets a resource as a stream, relative to the meanings of this
217: * context's implementation.
218: * @return a null if the resource cannot be found or represented
219: * as an InputStream.
220: */
221: public java.io.InputStream getResourceAsStream(String res) {
222: return context.getResourceAsStream(canonicalURI(res));
223: }
224:
225: public URL getResource(String res) throws MalformedURLException {
226: return context.getResource(canonicalURI(res));
227: }
228:
229: public Set getResourcePaths(String path) {
230: return context.getResourcePaths(canonicalURI(path));
231: }
232:
233: /**
234: * Gets the actual path of a URI relative to the context of
235: * the compilation.
236: */
237: public String getRealPath(String path) {
238: if (context != null) {
239: return context.getRealPath(path);
240: }
241: return path;
242: }
243:
244: /**
245: * Returns the tag-file-name-to-JAR-file map of this compilation unit,
246: * which maps tag file names to the JAR files in which the tag files are
247: * packaged.
248: *
249: * The map is populated when parsing the tag-file elements of the TLDs
250: * of any imported taglibs.
251: */
252: public Hashtable getTagFileJarUrls() {
253: return this .tagFileJarUrls;
254: }
255:
256: /**
257: * Returns the JAR file in which the tag file for which this
258: * JspCompilationContext was created is packaged, or null if this
259: * JspCompilationContext does not correspond to a tag file, or if the
260: * corresponding tag file is not packaged in a JAR.
261: */
262: public URL getTagFileJarUrl() {
263: return this .tagFileJarUrl;
264: }
265:
266: /* ==================== Common implementation ==================== */
267:
268: /**
269: * Just the class name (does not include package name) of the
270: * generated class.
271: */
272: public String getServletClassName() {
273:
274: if (className != null) {
275: return className;
276: }
277:
278: if (isTagFile) {
279: className = tagInfo.getTagClassName();
280: int lastIndex = className.lastIndexOf('.');
281: if (lastIndex != -1) {
282: className = className.substring(lastIndex + 1);
283: }
284: } else {
285: int iSep = jspUri.lastIndexOf('/') + 1;
286: className = JspUtil.makeJavaIdentifier(jspUri
287: .substring(iSep));
288: }
289: return className;
290: }
291:
292: public void setServletClassName(String className) {
293: this .className = className;
294: }
295:
296: /**
297: * Path of the JSP URI. Note that this is not a file name. This is
298: * the context rooted URI of the JSP file.
299: */
300: public String getJspFile() {
301: return jspUri;
302: }
303:
304: /**
305: * Are we processing something that has been declared as an
306: * errorpage?
307: */
308: public boolean isErrorPage() {
309: return isErrPage;
310: }
311:
312: public void setErrorPage(boolean isErrPage) {
313: this .isErrPage = isErrPage;
314: }
315:
316: public boolean isTagFile() {
317: return isTagFile;
318: }
319:
320: public TagInfo getTagInfo() {
321: return tagInfo;
322: }
323:
324: public void setTagInfo(TagInfo tagi) {
325: tagInfo = tagi;
326: }
327:
328: /**
329: * True if we are compiling a tag file in prototype mode.
330: * ie we only generate codes with class for the tag handler with empty
331: * method bodies.
332: */
333: public boolean isPrototypeMode() {
334: return protoTypeMode;
335: }
336:
337: public void setPrototypeMode(boolean pm) {
338: protoTypeMode = pm;
339: }
340:
341: /**
342: * Package name for the generated class is make up of the base package
343: * name, which is user settable, and the derived package name. The
344: * derived package name directly mirrors the file heirachy of the JSP page.
345: */
346: public String getServletPackageName() {
347: String dPackageName = getDerivedPackageName();
348: if (dPackageName.length() == 0) {
349: return basePackageName;
350: }
351: return basePackageName + '.' + getDerivedPackageName();
352: }
353:
354: private String getDerivedPackageName() {
355: if (derivedPackageName == null) {
356: int iSep = jspUri.lastIndexOf('/');
357: derivedPackageName = (iSep > 0) ? JspUtil
358: .makeJavaPackage(jspUri.substring(1, iSep)) : "";
359: }
360: return derivedPackageName;
361: }
362:
363: /**
364: * The package name into which the servlet class is generated.
365: */
366: public void setServletPackageName(String servletPackageName) {
367: this .basePackageName = servletPackageName;
368: }
369:
370: /**
371: * Full path name of the Java file into which the servlet is being
372: * generated.
373: */
374: public String getServletJavaFileName() {
375:
376: if (servletJavaFileName == null) {
377: servletJavaFileName = getOutputDir()
378: + getServletClassName() + ".java";
379: } else {
380: // Make sure output dir exists
381: makeOutputDir();
382: }
383: return servletJavaFileName;
384: }
385:
386: public void setServletJavaFileName(String servletJavaFileName) {
387: this .servletJavaFileName = servletJavaFileName;
388: }
389:
390: /**
391: * Get hold of the Options object for this context.
392: */
393: public Options getOptions() {
394: return options;
395: }
396:
397: public ServletContext getServletContext() {
398: return context;
399: }
400:
401: public JspRuntimeContext getRuntimeContext() {
402: return rctxt;
403: }
404:
405: /**
406: * Path of the Java file relative to the work directory.
407: */
408: public String getJavaPath() {
409:
410: if (javaPath != null) {
411: return javaPath;
412: }
413:
414: if (isTagFile()) {
415: String tagName = tagInfo.getTagClassName();
416: javaPath = tagName.replace('.', '/') + ".java";
417: } else {
418: javaPath = getServletPackageName().replace('.', '/') + '/'
419: + getServletClassName() + ".java";
420: }
421: return javaPath;
422: }
423:
424: public String getClassFileName() {
425:
426: if (classFileName == null) {
427: classFileName = getOutputDir() + getServletClassName()
428: + ".class";
429: } else {
430: // Make sure output dir exists
431: makeOutputDir();
432: }
433: return classFileName;
434: }
435:
436: /**
437: * Get the content type of this JSP.
438: *
439: * Content type includes content type and encoding.
440: */
441: public String getContentType() {
442: return contentType;
443: }
444:
445: public void setContentType(String contentType) {
446: this .contentType = contentType;
447: }
448:
449: /**
450: * Where is the servlet being generated?
451: */
452: public ServletWriter getWriter() {
453: return writer;
454: }
455:
456: public void setWriter(ServletWriter writer) {
457: this .writer = writer;
458: }
459:
460: /**
461: * Gets the 'location' of the TLD associated with the given taglib 'uri'.
462: *
463: * @return An array of two Strings: The first element denotes the real
464: * path to the TLD. If the path to the TLD points to a jar file, then the
465: * second element denotes the name of the TLD entry in the jar file.
466: * Returns null if the given uri is not associated with any tag library
467: * 'exposed' in the web application.
468: */
469: public String[] getTldLocation(String uri) throws JasperException {
470: String[] location = getOptions().getTldLocationsCache()
471: .getLocation(uri);
472: return location;
473: }
474:
475: /**
476: * Are we keeping generated code around?
477: */
478: public boolean keepGenerated() {
479: return getOptions().getKeepGenerated();
480: }
481:
482: // ==================== Removal ====================
483:
484: public void incrementRemoved() {
485: if (removed > 1) {
486: jspCompiler.removeGeneratedFiles();
487: if (rctxt != null)
488: rctxt.removeWrapper(jspUri);
489: }
490: removed++;
491: }
492:
493: public boolean isRemoved() {
494: if (removed > 1) {
495: return true;
496: }
497: return false;
498: }
499:
500: // ==================== Compile and reload ====================
501:
502: public void compile() throws JasperException, FileNotFoundException {
503: createCompiler();
504: if (isPackagedTagFile || jspCompiler.isOutDated()) {
505: try {
506: jspCompiler.compile();
507: jsw.setReload(true);
508: jsw.setCompilationException(null);
509: } catch (JasperException ex) {
510: // Cache compilation exception
511: jsw.setCompilationException(ex);
512: throw ex;
513: } catch (Exception ex) {
514: ex.printStackTrace();
515: JasperException je = new JasperException(Localizer
516: .getMessage("jsp.error.unable.compile"), ex);
517: // Cache compilation exception
518: jsw.setCompilationException(je);
519: throw je;
520: }
521: }
522: }
523:
524: // ==================== Manipulating the class ====================
525:
526: public Class load() throws JasperException, FileNotFoundException {
527: try {
528: jspLoader = new JasperLoader(new URL[] { baseUrl },
529: getClassLoader(), rctxt.getPermissionCollection(),
530: rctxt.getCodeSource());
531:
532: String name;
533: if (isTagFile()) {
534: name = tagInfo.getTagClassName();
535: } else {
536: name = getServletPackageName() + "."
537: + getServletClassName();
538: }
539: servletClass = jspLoader.loadClass(name);
540: } catch (ClassNotFoundException cex) {
541: throw new JasperException(Localizer
542: .getMessage("jsp.error.unable.load"), cex);
543: } catch (Exception ex) {
544: throw new JasperException(Localizer
545: .getMessage("jsp.error.unable.compile"), ex);
546: }
547: removed = 0;
548: return servletClass;
549: }
550:
551: // ==================== Private methods ====================
552:
553: static Object outputDirLock = new Object();
554:
555: private void makeOutputDir() {
556: synchronized (outputDirLock) {
557: File outDirFile = new File(outputDir);
558: outDirFile.mkdirs();
559: }
560: }
561:
562: private void createOutputDir() {
563: String path = null;
564: if (isTagFile()) {
565: String tagName = tagInfo.getTagClassName();
566: path = tagName.replace('.', '/');
567: path = path.substring(0, path.lastIndexOf('/'));
568: } else {
569: path = getServletPackageName().replace('.', '/');
570: }
571:
572: try {
573: // Append servlet or tag handler path to scratch dir
574: baseUrl = options.getScratchDir().toURL();
575: String outUrlString = baseUrl.toString() + '/' + path;
576: URL outUrl = new URL(outUrlString);
577: outputDir = outUrl.getFile() + File.separator;
578: makeOutputDir();
579: } catch (Exception e) {
580: throw new IllegalStateException("No output directory: "
581: + e.getMessage());
582: }
583: }
584:
585: private static final boolean isPathSeparator(char c) {
586: return (c == '/' || c == '\\');
587: }
588:
589: private static final String canonicalURI(String s) {
590: if (s == null)
591: return null;
592: StringBuffer result = new StringBuffer();
593: final int len = s.length();
594: int pos = 0;
595: while (pos < len) {
596: char c = s.charAt(pos);
597: if (isPathSeparator(c)) {
598: /*
599: * multiple path separators.
600: * 'foo///bar' -> 'foo/bar'
601: */
602: while (pos + 1 < len
603: && isPathSeparator(s.charAt(pos + 1))) {
604: ++pos;
605: }
606:
607: if (pos + 1 < len && s.charAt(pos + 1) == '.') {
608: /*
609: * a single dot at the end of the path - we are done.
610: */
611: if (pos + 2 >= len)
612: break;
613:
614: switch (s.charAt(pos + 2)) {
615: /*
616: * self directory in path
617: * foo/./bar -> foo/bar
618: */
619: case '/':
620: case '\\':
621: pos += 2;
622: continue;
623:
624: /*
625: * two dots in a path: go back one hierarchy.
626: * foo/bar/../baz -> foo/baz
627: */
628: case '.':
629: // only if we have exactly _two_ dots.
630: if (pos + 3 < len
631: && isPathSeparator(s.charAt(pos + 3))) {
632: pos += 3;
633: int separatorPos = result.length() - 1;
634: while (separatorPos >= 0
635: && !isPathSeparator(result
636: .charAt(separatorPos))) {
637: --separatorPos;
638: }
639: if (separatorPos >= 0)
640: result.setLength(separatorPos);
641: continue;
642: }
643: }
644: }
645: }
646: result.append(c);
647: ++pos;
648: }
649: return result.toString();
650: }
651: }
|