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 = null;
205: if (options.getCompilerClassName() != null) {
206: jspCompiler = createCompiler(options.getCompilerClassName());
207: } else {
208: if (options.getCompiler() == null) {
209: jspCompiler = createCompiler("org.apache.jasper.compiler.JDTCompiler");
210: if (jspCompiler == null) {
211: jspCompiler = createCompiler("org.apache.jasper.compiler.AntCompiler");
212: }
213: } else {
214: jspCompiler = createCompiler("org.apache.jasper.compiler.AntCompiler");
215: if (jspCompiler == null) {
216: jspCompiler = createCompiler("org.apache.jasper.compiler.JDTCompiler");
217: }
218: }
219: }
220: if (jspCompiler == null) {
221: throw new IllegalStateException(Localizer
222: .getMessage("jsp.error.compiler"));
223: }
224: jspCompiler.init(this , jsw);
225: return jspCompiler;
226: }
227:
228: protected Compiler createCompiler(String className) {
229: Compiler compiler = null;
230: try {
231: compiler = (Compiler) Class.forName(className)
232: .newInstance();
233: } catch (InstantiationException e) {
234: log.warn(Localizer.getMessage("jsp.error.compiler"), e);
235: } catch (IllegalAccessException e) {
236: log.warn(Localizer.getMessage("jsp.error.compiler"), e);
237: } catch (NoClassDefFoundError e) {
238: if (log.isDebugEnabled()) {
239: log
240: .debug(Localizer
241: .getMessage("jsp.error.compiler"), e);
242: }
243: } catch (ClassNotFoundException e) {
244: if (log.isDebugEnabled()) {
245: log
246: .debug(Localizer
247: .getMessage("jsp.error.compiler"), e);
248: }
249: }
250: return compiler;
251: }
252:
253: public Compiler getCompiler() {
254: return jspCompiler;
255: }
256:
257: /** ---------- Access resources in the webapp ---------- */
258:
259: /**
260: * Get the full value of a URI relative to this compilations context
261: * uses current file as the base.
262: */
263: public String resolveRelativeUri(String uri) {
264: // sometimes we get uri's massaged from File(String), so check for
265: // a root directory deperator char
266: if (uri.startsWith("/") || uri.startsWith(File.separator)) {
267: return uri;
268: } else {
269: return baseURI + uri;
270: }
271: }
272:
273: /**
274: * Gets a resource as a stream, relative to the meanings of this
275: * context's implementation.
276: * @return a null if the resource cannot be found or represented
277: * as an InputStream.
278: */
279: public java.io.InputStream getResourceAsStream(String res) {
280: return context.getResourceAsStream(canonicalURI(res));
281: }
282:
283: public URL getResource(String res) throws MalformedURLException {
284: return context.getResource(canonicalURI(res));
285: }
286:
287: public Set getResourcePaths(String path) {
288: return context.getResourcePaths(canonicalURI(path));
289: }
290:
291: /**
292: * Gets the actual path of a URI relative to the context of
293: * the compilation.
294: */
295: public String getRealPath(String path) {
296: if (context != null) {
297: return context.getRealPath(path);
298: }
299: return path;
300: }
301:
302: /**
303: * Returns the tag-file-name-to-JAR-file map of this compilation unit,
304: * which maps tag file names to the JAR files in which the tag files are
305: * packaged.
306: *
307: * The map is populated when parsing the tag-file elements of the TLDs
308: * of any imported taglibs.
309: */
310: public URL getTagFileJarUrl(String tagFile) {
311: return this .tagFileJarUrls.get(tagFile);
312: }
313:
314: public void setTagFileJarUrl(String tagFile, URL tagFileURL) {
315: this .tagFileJarUrls.put(tagFile, tagFileURL);
316: }
317:
318: /**
319: * Returns the JAR file in which the tag file for which this
320: * JspCompilationContext was created is packaged, or null if this
321: * JspCompilationContext does not correspond to a tag file, or if the
322: * corresponding tag file is not packaged in a JAR.
323: */
324: public URL getTagFileJarUrl() {
325: return this .tagFileJarUrl;
326: }
327:
328: /* ==================== Common implementation ==================== */
329:
330: /**
331: * Just the class name (does not include package name) of the
332: * generated class.
333: */
334: public String getServletClassName() {
335:
336: if (className != null) {
337: return className;
338: }
339:
340: if (isTagFile) {
341: className = tagInfo.getTagClassName();
342: int lastIndex = className.lastIndexOf('.');
343: if (lastIndex != -1) {
344: className = className.substring(lastIndex + 1);
345: }
346: } else {
347: int iSep = jspUri.lastIndexOf('/') + 1;
348: className = JspUtil.makeJavaIdentifier(jspUri
349: .substring(iSep));
350: }
351: return className;
352: }
353:
354: public void setServletClassName(String className) {
355: this .className = className;
356: }
357:
358: /**
359: * Path of the JSP URI. Note that this is not a file name. This is
360: * the context rooted URI of the JSP file.
361: */
362: public String getJspFile() {
363: return jspUri;
364: }
365:
366: /**
367: * Are we processing something that has been declared as an
368: * errorpage?
369: */
370: public boolean isErrorPage() {
371: return isErrPage;
372: }
373:
374: public void setErrorPage(boolean isErrPage) {
375: this .isErrPage = isErrPage;
376: }
377:
378: public boolean isTagFile() {
379: return isTagFile;
380: }
381:
382: public TagInfo getTagInfo() {
383: return tagInfo;
384: }
385:
386: public void setTagInfo(TagInfo tagi) {
387: tagInfo = tagi;
388: }
389:
390: /**
391: * True if we are compiling a tag file in prototype mode.
392: * ie we only generate codes with class for the tag handler with empty
393: * method bodies.
394: */
395: public boolean isPrototypeMode() {
396: return protoTypeMode;
397: }
398:
399: public void setPrototypeMode(boolean pm) {
400: protoTypeMode = pm;
401: }
402:
403: /**
404: * Package name for the generated class is make up of the base package
405: * name, which is user settable, and the derived package name. The
406: * derived package name directly mirrors the file heirachy of the JSP page.
407: */
408: public String getServletPackageName() {
409: if (isTagFile()) {
410: String className = tagInfo.getTagClassName();
411: int lastIndex = className.lastIndexOf('.');
412: String pkgName = "";
413: if (lastIndex != -1) {
414: pkgName = className.substring(0, lastIndex);
415: }
416: return pkgName;
417: } else {
418: String dPackageName = getDerivedPackageName();
419: if (dPackageName.length() == 0) {
420: return basePackageName;
421: }
422: return basePackageName + '.' + getDerivedPackageName();
423: }
424: }
425:
426: protected String getDerivedPackageName() {
427: if (derivedPackageName == null) {
428: int iSep = jspUri.lastIndexOf('/');
429: derivedPackageName = (iSep > 0) ? JspUtil
430: .makeJavaPackage(jspUri.substring(1, iSep)) : "";
431: }
432: return derivedPackageName;
433: }
434:
435: /**
436: * The package name into which the servlet class is generated.
437: */
438: public void setServletPackageName(String servletPackageName) {
439: this .basePackageName = servletPackageName;
440: }
441:
442: /**
443: * Full path name of the Java file into which the servlet is being
444: * generated.
445: */
446: public String getServletJavaFileName() {
447: if (servletJavaFileName == null) {
448: servletJavaFileName = getOutputDir()
449: + getServletClassName() + ".java";
450: }
451: return servletJavaFileName;
452: }
453:
454: /**
455: * Get hold of the Options object for this context.
456: */
457: public Options getOptions() {
458: return options;
459: }
460:
461: public ServletContext getServletContext() {
462: return context;
463: }
464:
465: public JspRuntimeContext getRuntimeContext() {
466: return rctxt;
467: }
468:
469: /**
470: * Path of the Java file relative to the work directory.
471: */
472: public String getJavaPath() {
473:
474: if (javaPath != null) {
475: return javaPath;
476: }
477:
478: if (isTagFile()) {
479: String tagName = tagInfo.getTagClassName();
480: javaPath = tagName.replace('.', '/') + ".java";
481: } else {
482: javaPath = getServletPackageName().replace('.', '/') + '/'
483: + getServletClassName() + ".java";
484: }
485: return javaPath;
486: }
487:
488: public String getClassFileName() {
489: if (classFileName == null) {
490: classFileName = getOutputDir() + getServletClassName()
491: + ".class";
492: }
493: return classFileName;
494: }
495:
496: /**
497: * Get the content type of this JSP.
498: *
499: * Content type includes content type and encoding.
500: */
501: public String getContentType() {
502: return contentType;
503: }
504:
505: public void setContentType(String contentType) {
506: this .contentType = contentType;
507: }
508:
509: /**
510: * Where is the servlet being generated?
511: */
512: public ServletWriter getWriter() {
513: return writer;
514: }
515:
516: public void setWriter(ServletWriter writer) {
517: this .writer = writer;
518: }
519:
520: /**
521: * Gets the 'location' of the TLD associated with the given taglib 'uri'.
522: *
523: * @return An array of two Strings: The first element denotes the real
524: * path to the TLD. If the path to the TLD points to a jar file, then the
525: * second element denotes the name of the TLD entry in the jar file.
526: * Returns null if the given uri is not associated with any tag library
527: * 'exposed' in the web application.
528: */
529: public String[] getTldLocation(String uri) throws JasperException {
530: String[] location = getOptions().getTldLocationsCache()
531: .getLocation(uri);
532: return location;
533: }
534:
535: /**
536: * Are we keeping generated code around?
537: */
538: public boolean keepGenerated() {
539: return getOptions().getKeepGenerated();
540: }
541:
542: // ==================== Removal ====================
543:
544: public void incrementRemoved() {
545: if (removed == 0 && rctxt != null) {
546: rctxt.removeWrapper(jspUri);
547: }
548: removed++;
549: }
550:
551: public boolean isRemoved() {
552: if (removed > 1) {
553: return true;
554: }
555: return false;
556: }
557:
558: // ==================== Compile and reload ====================
559:
560: public void compile() throws JasperException, FileNotFoundException {
561: createCompiler();
562: if (isPackagedTagFile || jspCompiler.isOutDated()) {
563: try {
564: jspCompiler.removeGeneratedFiles();
565: jspLoader = null;
566: jspCompiler.compile();
567: jsw.setReload(true);
568: jsw.setCompilationException(null);
569: } catch (JasperException ex) {
570: // Cache compilation exception
571: jsw.setCompilationException(ex);
572: throw ex;
573: } catch (Exception ex) {
574: JasperException je = new JasperException(Localizer
575: .getMessage("jsp.error.unable.compile"), ex);
576: // Cache compilation exception
577: jsw.setCompilationException(je);
578: throw je;
579: }
580: }
581: }
582:
583: // ==================== Manipulating the class ====================
584:
585: public Class load() throws JasperException, FileNotFoundException {
586: try {
587: getJspLoader();
588:
589: String name;
590: if (isTagFile()) {
591: name = tagInfo.getTagClassName();
592: } else {
593: name = getServletPackageName() + "."
594: + getServletClassName();
595: }
596: servletClass = jspLoader.loadClass(name);
597: } catch (ClassNotFoundException cex) {
598: throw new JasperException(Localizer
599: .getMessage("jsp.error.unable.load"), cex);
600: } catch (Exception ex) {
601: throw new JasperException(Localizer
602: .getMessage("jsp.error.unable.compile"), ex);
603: }
604: removed = 0;
605: return servletClass;
606: }
607:
608: // ==================== protected methods ====================
609:
610: static Object outputDirLock = new Object();
611:
612: public void checkOutputDir() {
613: if (outputDir != null) {
614: if (!(new File(outputDir)).exists()) {
615: makeOutputDir();
616: }
617: } else {
618: createOutputDir();
619: }
620: }
621:
622: protected boolean makeOutputDir() {
623: synchronized (outputDirLock) {
624: File outDirFile = new File(outputDir);
625: return (outDirFile.exists() || outDirFile.mkdirs());
626: }
627: }
628:
629: protected void createOutputDir() {
630: String path = null;
631: if (isTagFile()) {
632: String tagName = tagInfo.getTagClassName();
633: path = tagName.replace('.', '/');
634: path = path.substring(0, path.lastIndexOf('/'));
635: } else {
636: path = getServletPackageName().replace('.', '/');
637: }
638:
639: // Append servlet or tag handler path to scratch dir
640: try {
641: baseUrl = options.getScratchDir().toURL();
642: String outUrlString = baseUrl.toString() + '/' + path;
643: URL outUrl = new URL(outUrlString);
644: outputDir = outUrl.getFile() + File.separator;
645: if (!makeOutputDir()) {
646: throw new IllegalStateException(Localizer
647: .getMessage("jsp.error.outputfolder"));
648: }
649: } catch (MalformedURLException e) {
650: throw new IllegalStateException(Localizer
651: .getMessage("jsp.error.outputfolder"), e);
652: }
653: }
654:
655: protected static final boolean isPathSeparator(char c) {
656: return (c == '/' || c == '\\');
657: }
658:
659: protected static final String canonicalURI(String s) {
660: if (s == null)
661: return null;
662: StringBuffer result = new StringBuffer();
663: final int len = s.length();
664: int pos = 0;
665: while (pos < len) {
666: char c = s.charAt(pos);
667: if (isPathSeparator(c)) {
668: /*
669: * multiple path separators.
670: * 'foo///bar' -> 'foo/bar'
671: */
672: while (pos + 1 < len
673: && isPathSeparator(s.charAt(pos + 1))) {
674: ++pos;
675: }
676:
677: if (pos + 1 < len && s.charAt(pos + 1) == '.') {
678: /*
679: * a single dot at the end of the path - we are done.
680: */
681: if (pos + 2 >= len)
682: break;
683:
684: switch (s.charAt(pos + 2)) {
685: /*
686: * self directory in path
687: * foo/./bar -> foo/bar
688: */
689: case '/':
690: case '\\':
691: pos += 2;
692: continue;
693:
694: /*
695: * two dots in a path: go back one hierarchy.
696: * foo/bar/../baz -> foo/baz
697: */
698: case '.':
699: // only if we have exactly _two_ dots.
700: if (pos + 3 < len
701: && isPathSeparator(s.charAt(pos + 3))) {
702: pos += 3;
703: int separatorPos = result.length() - 1;
704: while (separatorPos >= 0
705: && !isPathSeparator(result
706: .charAt(separatorPos))) {
707: --separatorPos;
708: }
709: if (separatorPos >= 0)
710: result.setLength(separatorPos);
711: continue;
712: }
713: }
714: }
715: }
716: result.append(c);
717: ++pos;
718: }
719: return result.toString();
720: }
721: }
|