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