Java tutorial
/* * Copyright 1999,2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.jasper; import java.io.File; import java.io.FileNotFoundException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.Hashtable; import java.util.Set; import javax.servlet.ServletContext; import javax.servlet.jsp.tagext.TagInfo; import org.apache.jasper.compiler.Compiler; import org.apache.jasper.compiler.JspRuntimeContext; import org.apache.jasper.compiler.JspUtil; import org.apache.jasper.compiler.Localizer; import org.apache.jasper.compiler.ServletWriter; import org.apache.jasper.servlet.JasperLoader; import org.apache.jasper.servlet.JspServletWrapper; /** * A place holder for various things that are used through out the JSP * engine. This is a per-request/per-context data structure. Some of * the instance variables are set at different points. * * Most of the path-related stuff is here - mangling names, versions, dirs, * loading resources and dealing with uris. * * @author Anil K. Vijendran * @author Harish Prabandham * @author Pierre Delisle * @author Costin Manolache * @author Kin-man Chung */ public class JspCompilationContext { protected org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory .getLog(JspCompilationContext.class); private Hashtable tagFileJarUrls; private boolean isPackagedTagFile; private String className; private String jspUri; private boolean isErrPage; private String basePackageName; private String derivedPackageName; private String servletJavaFileName; private String javaPath; private String classFileName; private String contentType; private ServletWriter writer; private Options options; private JspServletWrapper jsw; private Compiler jspCompiler; private String classPath; private String baseURI; private String baseOutputDir; private String outputDir; private ServletContext context; private URLClassLoader loader; private JspRuntimeContext rctxt; private int removed = 0; private URLClassLoader jspLoader; private URL baseUrl; private Class servletClass; private boolean isTagFile; private boolean protoTypeMode; private TagInfo tagInfo; private URL tagFileJarUrl; // jspURI _must_ be relative to the context public JspCompilationContext(String jspUri, boolean isErrPage, Options options, ServletContext context, JspServletWrapper jsw, JspRuntimeContext rctxt) { this.jspUri = canonicalURI(jspUri); this.isErrPage = isErrPage; this.options = options; this.jsw = jsw; this.context = context; this.baseURI = jspUri.substring(0, jspUri.lastIndexOf('/') + 1); // hack fix for resolveRelativeURI if (baseURI == null) { baseURI = "/"; } else if (baseURI.charAt(0) != '/') { // strip the basde slash since it will be combined with the // uriBase to generate a file baseURI = "/" + baseURI; } if (baseURI.charAt(baseURI.length() - 1) != '/') { baseURI += '/'; } this.rctxt = rctxt; this.tagFileJarUrls = new Hashtable(); this.basePackageName = Constants.JSP_PACKAGE_NAME; } public JspCompilationContext(String tagfile, TagInfo tagInfo, Options options, ServletContext context, JspServletWrapper jsw, JspRuntimeContext rctxt, URL tagFileJarUrl) { this(tagfile, false, options, context, jsw, rctxt); this.isTagFile = true; this.tagInfo = tagInfo; this.tagFileJarUrl = tagFileJarUrl; if (tagFileJarUrl != null) { isPackagedTagFile = true; } } /* ==================== Methods to override ==================== */ /** ---------- Class path and loader ---------- */ /** * The classpath that is passed off to the Java compiler. */ public String getClassPath() { if (classPath != null) return classPath; return rctxt.getClassPath(); } /** * The classpath that is passed off to the Java compiler. */ public void setClassPath(String classPath) { this.classPath = classPath; } /** * What class loader to use for loading classes while compiling * this JSP? */ public ClassLoader getClassLoader() { if (loader != null) return loader; return rctxt.getParentClassLoader(); } public void setClassLoader(URLClassLoader loader) { this.loader = loader; } public ClassLoader getJspLoader() { if (jspLoader == null) { jspLoader = new JasperLoader(new URL[] { baseUrl }, getClassLoader(), rctxt.getPermissionCollection(), rctxt.getCodeSource()); } return jspLoader; } /** ---------- Input/Output ---------- */ /** * The output directory to generate code into. The output directory * is make up of the scratch directory, which is provide in Options, * plus the directory derived from the package name. */ public String getOutputDir() { if (outputDir == null) { createOutputDir(); } return outputDir; } /** * Create a "Compiler" object based on some init param data. This * is not done yet. Right now we're just hardcoding the actual * compilers that are created. */ public Compiler createCompiler() throws JasperException { if (jspCompiler != null) { return jspCompiler; } jspCompiler = createCompiler("org.apache.jasper.compiler.BeeCompiler"); if (jspCompiler == null) { throw new IllegalStateException(Localizer.getMessage("jsp.error.compiler")); } jspCompiler.init(this, jsw); return jspCompiler; } private Compiler createCompiler(String className) { Compiler compiler = null; try { compiler = (Compiler) Class.forName(className).newInstance(); } catch (Throwable t) { if (log.isDebugEnabled()) { log.debug(Localizer.getMessage("jsp.error.compiler"), t); } } return compiler; } public Compiler getCompiler() { return jspCompiler; } /** ---------- Access resources in the webapp ---------- */ /** * Get the full value of a URI relative to this compilations context * uses current file as the base. */ public String resolveRelativeUri(String uri) { // sometimes we get uri's massaged from File(String), so check for // a root directory deperator char if (uri.startsWith("/") || uri.startsWith(File.separator)) { return uri; } else { return baseURI + uri; } } /** * Gets a resource as a stream, relative to the meanings of this * context's implementation. * @return a null if the resource cannot be found or represented * as an InputStream. */ public java.io.InputStream getResourceAsStream(String res) { return context.getResourceAsStream(canonicalURI(res)); } public URL getResource(String res) throws MalformedURLException { return context.getResource(canonicalURI(res)); } public Set getResourcePaths(String path) { return context.getResourcePaths(canonicalURI(path)); } /** * Gets the actual path of a URI relative to the context of * the compilation. */ public String getRealPath(String path) { if (context != null) { return context.getRealPath(path); } return path; } /** * Returns the tag-file-name-to-JAR-file map of this compilation unit, * which maps tag file names to the JAR files in which the tag files are * packaged. * * The map is populated when parsing the tag-file elements of the TLDs * of any imported taglibs. */ public Hashtable getTagFileJarUrls() { return this.tagFileJarUrls; } /** * Returns the JAR file in which the tag file for which this * JspCompilationContext was created is packaged, or null if this * JspCompilationContext does not correspond to a tag file, or if the * corresponding tag file is not packaged in a JAR. */ public URL getTagFileJarUrl() { return this.tagFileJarUrl; } /* ==================== Common implementation ==================== */ /** * Just the class name (does not include package name) of the * generated class. */ public String getServletClassName() { if (className != null) { return className; } if (isTagFile) { className = tagInfo.getTagClassName(); int lastIndex = className.lastIndexOf('.'); if (lastIndex != -1) { className = className.substring(lastIndex + 1); } } else { int iSep = jspUri.lastIndexOf('/') + 1; className = JspUtil.makeJavaIdentifier(jspUri.substring(iSep)); } return className; } public void setServletClassName(String className) { this.className = className; } /** * Path of the JSP URI. Note that this is not a file name. This is * the context rooted URI of the JSP file. */ public String getJspFile() { return jspUri; } /** * Are we processing something that has been declared as an * errorpage? */ public boolean isErrorPage() { return isErrPage; } public void setErrorPage(boolean isErrPage) { this.isErrPage = isErrPage; } public boolean isTagFile() { return isTagFile; } public TagInfo getTagInfo() { return tagInfo; } public void setTagInfo(TagInfo tagi) { tagInfo = tagi; } /** * True if we are compiling a tag file in prototype mode. * ie we only generate codes with class for the tag handler with empty * method bodies. */ public boolean isPrototypeMode() { return protoTypeMode; } public void setPrototypeMode(boolean pm) { protoTypeMode = pm; } /** * Package name for the generated class is make up of the base package * name, which is user settable, and the derived package name. The * derived package name directly mirrors the file heirachy of the JSP page. */ public String getServletPackageName() { if (isTagFile()) { String className = tagInfo.getTagClassName(); int lastIndex = className.lastIndexOf('.'); String pkgName = ""; if (lastIndex != -1) { pkgName = className.substring(0, lastIndex); } return pkgName; } else { String dPackageName = getDerivedPackageName(); if (dPackageName.length() == 0) { return basePackageName; } return basePackageName + '.' + getDerivedPackageName(); } } private String getDerivedPackageName() { if (derivedPackageName == null) { int iSep = jspUri.lastIndexOf('/'); derivedPackageName = (iSep > 0) ? JspUtil.makeJavaPackage(jspUri.substring(1, iSep)) : ""; } return derivedPackageName; } /** * The package name into which the servlet class is generated. */ public void setServletPackageName(String servletPackageName) { this.basePackageName = servletPackageName; } /** * Full path name of the Java file into which the servlet is being * generated. */ public String getServletJavaFileName() { if (servletJavaFileName == null) { servletJavaFileName = getOutputDir() + getServletClassName() + ".java"; } else { // Make sure output dir exists makeOutputDir(); } return servletJavaFileName; } public void setServletJavaFileName(String servletJavaFileName) { this.servletJavaFileName = servletJavaFileName; } /** * Get hold of the Options object for this context. */ public Options getOptions() { return options; } public ServletContext getServletContext() { return context; } public JspRuntimeContext getRuntimeContext() { return rctxt; } /** * Path of the Java file relative to the work directory. */ public String getJavaPath() { if (javaPath != null) { return javaPath; } if (isTagFile()) { String tagName = tagInfo.getTagClassName(); javaPath = tagName.replace('.', '/') + ".java"; } else { javaPath = getServletPackageName().replace('.', '/') + '/' + getServletClassName() + ".java"; } return javaPath; } public String getClassFileName() { if (classFileName == null) { classFileName = getOutputDir() + getServletClassName() + ".class"; } else { // Make sure output dir exists makeOutputDir(); } return classFileName; } /** * Get the content type of this JSP. * * Content type includes content type and encoding. */ public String getContentType() { return contentType; } public void setContentType(String contentType) { this.contentType = contentType; } /** * Where is the servlet being generated? */ public ServletWriter getWriter() { return writer; } public void setWriter(ServletWriter writer) { this.writer = writer; } /** * Gets the 'location' of the TLD associated with the given taglib 'uri'. * * @return An array of two Strings: The first element denotes the real * path to the TLD. If the path to the TLD points to a jar file, then the * second element denotes the name of the TLD entry in the jar file. * Returns null if the given uri is not associated with any tag library * 'exposed' in the web application. */ public String[] getTldLocation(String uri) throws JasperException { String[] location = getOptions().getTldLocationsCache().getLocation(uri); return location; } /** * Are we keeping generated code around? */ public boolean keepGenerated() { return getOptions().getKeepGenerated(); } // ==================== Removal ==================== public void incrementRemoved() { if (removed > 1) { jspCompiler.removeGeneratedFiles(); if (rctxt != null) rctxt.removeWrapper(jspUri); } removed++; } public boolean isRemoved() { if (removed > 1) { return true; } return false; } // ==================== Compile and reload ==================== public void compile() throws JasperException, FileNotFoundException { createCompiler(); if (isPackagedTagFile || jspCompiler.isOutDated()) { try { jspLoader = null; jspCompiler.compile(); jsw.setReload(true); jsw.setCompilationException(null); } catch (JasperException ex) { // Cache compilation exception jsw.setCompilationException(ex); throw ex; } catch (Exception ex) { ex.printStackTrace(); JasperException je = new JasperException(Localizer.getMessage("jsp.error.unable.compile"), ex); // Cache compilation exception jsw.setCompilationException(je); throw je; } } } // ==================== Manipulating the class ==================== public Class load() throws JasperException, FileNotFoundException { try { getJspLoader(); String name; if (isTagFile()) { name = tagInfo.getTagClassName(); } else { name = getServletPackageName() + "." + getServletClassName(); } servletClass = jspLoader.loadClass(name); } catch (ClassNotFoundException cex) { throw new JasperException(Localizer.getMessage("jsp.error.unable.load"), cex); } catch (Exception ex) { throw new JasperException(Localizer.getMessage("jsp.error.unable.compile"), ex); } removed = 0; return servletClass; } // ==================== Private methods ==================== static Object outputDirLock = new Object(); private void makeOutputDir() { synchronized (outputDirLock) { File outDirFile = new File(outputDir); outDirFile.mkdirs(); } } private void createOutputDir() { String path = null; if (isTagFile()) { String tagName = tagInfo.getTagClassName(); path = tagName.replace('.', '/'); path = path.substring(0, path.lastIndexOf('/')); } else { path = getServletPackageName().replace('.', '/'); } try { // Append servlet or tag handler path to scratch dir baseUrl = options.getScratchDir().toURL(); String outUrlString = baseUrl.toString() + '/' + path; URL outUrl = new URL(outUrlString); outputDir = outUrl.getFile() + File.separator; makeOutputDir(); } catch (Exception e) { throw new IllegalStateException("No output directory: " + e.getMessage()); } } private static final boolean isPathSeparator(char c) { return (c == '/' || c == '\\'); } private static final String canonicalURI(String s) { if (s == null) return null; StringBuffer result = new StringBuffer(); final int len = s.length(); int pos = 0; while (pos < len) { char c = s.charAt(pos); if (isPathSeparator(c)) { /* * multiple path separators. * 'foo///bar' -> 'foo/bar' */ while (pos + 1 < len && isPathSeparator(s.charAt(pos + 1))) { ++pos; } if (pos + 1 < len && s.charAt(pos + 1) == '.') { /* * a single dot at the end of the path - we are done. */ if (pos + 2 >= len) break; switch (s.charAt(pos + 2)) { /* * self directory in path * foo/./bar -> foo/bar */ case '/': case '\\': pos += 2; continue; /* * two dots in a path: go back one hierarchy. * foo/bar/../baz -> foo/baz */ case '.': // only if we have exactly _two_ dots. if (pos + 3 < len && isPathSeparator(s.charAt(pos + 3))) { pos += 3; int separatorPos = result.length() - 1; while (separatorPos >= 0 && !isPathSeparator(result.charAt(separatorPos))) { --separatorPos; } if (separatorPos >= 0) result.setLength(separatorPos); continue; } } } } result.append(c); ++pos; } return result.toString(); } }