costin 01/05/27 19:18:59 Added: jasper34/liaison/org/apache/jasper34/tomcat33 JasperEngineContext.java JasperOptionsImpl.java JspInterceptor.java Log: Added tomcat33 specific interface ( will be compiled only if 33 is detected). This is few times faster than the servlet and the default for 3.3. Revision Changes Path 1.1 jakarta-tomcat-jasper/jasper34/liaison/org/apache/jasper34/tomcat33/JasperEngineContext.java Index: JasperEngineContext.java =================================================================== /* * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.jasper34.tomcat33; import javax.servlet.*; import javax.servlet.http.*; import org.apache.jasper34.generator.*; import org.apache.jasper34.generator.Compiler; import org.apache.jasper34.core.*; import org.apache.jasper34.runtime.*; import java.util.*; import java.io.*; import java.net.*; //import org.apache.jasper.JspCompilationContext; /** Alternative implementation of JspCompilationContext ( in addition to the servlet and standalone ). Used by JspInterceptor - but it's in no way specific to tomcat. */ public class JasperEngineContext implements JspCompilationContext { JspReader reader; ServletWriter writer; ServletContext context; ClassLoader loader; boolean isErrPage; String jspFile; String servletClassName; String servletPackageName; String servletJavaFileName; String contentType; Options options; String cpath; // for compiling JSPs. ServletContext sctx; String outputDir; public JasperEngineContext() { } public void setClassPath( String s ) { cpath=s; } /** * The classpath that is passed off to the Java compiler. */ public String getClassPath() { return cpath; } /** * Get the input reader for the JSP text. */ public JspReader getReader() { if( debug>0 ) log("getReader " + reader ); return reader; } /** * Where is the servlet being generated? */ public ServletWriter getWriter() { if( debug>0 ) log("getWriter " + writer ); return writer; } public void setServletContext( Object o ) { sctx=(ServletContext)o; } /** * Get the ServletContext for the JSP we're processing now. */ public ServletContext getServletContext() { if( debug>0 ) log("getCtx " + sctx ); return sctx; } /** * What class loader to use for loading classes while compiling * this JSP? I don't think this is used right now -- akv. */ public ClassLoader getClassLoader() { if( debug>0 ) log("getLoader " + loader ); return loader; } public void setClassLoader( ClassLoader cl ) { loader=cl; } public void addJar( String jar ) throws IOException { if( debug>0 ) log("Add jar " + jar); //loader.addJar( jar ); } /** * Are we processing something that has been declared as an * errorpage? */ public boolean isErrorPage() { if( debug>0 ) log("isErrorPage " + isErrPage ); return isErrPage; } /** * What is the scratch directory we are generating code into? * FIXME: In some places this is called scratchDir and in some * other places it is called outputDir. */ public String getOutputDir() { if( debug>0 ) log("getOutputDir " + outputDir ); return outputDir; } public void setOutputDir(String s ) { outputDir=s; } /** * 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() { if( debug>0 ) log("getJspFile " + jspFile); return jspFile; } public void setJspFile( String s ) { jspFile=s; } /** * Just the class name (does not include package name) of the * generated class. */ public String getServletClassName() { if( debug>0 ) log("getServletClassName " + servletClassName); return servletClassName; } public void setServletClassName( String s ) { servletClassName=s; } /** * The package name into which the servlet class is generated. */ public String getServletPackageName() { if( debug>0 ) log("getServletPackageName " + servletPackageName ); return servletPackageName; } /** * Utility method to get the full class name from the package and * class name. */ public final String getFullClassName() { if( debug>0 ) log("getServletPackageName " + servletPackageName + "." + servletClassName); if (servletPackageName == null) return servletClassName; return servletPackageName + "." + servletClassName; } /** * Full path name of the Java file into which the servlet is being * generated. */ public String getServletJavaFileName() { if( debug>0 ) log("getServletPackageName " + servletPackageName + "." + servletClassName); return servletJavaFileName; } /** * Are we keeping generated code around? */ public boolean keepGenerated() { return options.getKeepGenerated(); } /** * What's the content type of this JSP? Content type includes * content type and encoding. */ public String getContentType() { return contentType; } /** * Get hold of the Options object for this context. */ public Options getOptions() { return options; } public void setOptions(Options options) { this.options=options; } public void setContentType(String contentType) { this.contentType = contentType; } public void setReader(JspReader reader) { this.reader = reader; } public void setWriter(ServletWriter writer) { this.writer = writer; } public void setServletPackageName(String servletPackageName) { this.servletPackageName = servletPackageName; } public void setServletJavaFileName(String servletJavaFileName) { this.servletJavaFileName = servletJavaFileName; } public void setErrorPage(boolean isErrPage) { this.isErrPage = isErrPage; } public Compiler createCompiler() throws JasperException { if( debug>0 ) log("createCompiler "); return null; } public String resolveRelativeUri(String uri) { if( debug>0 ) log("resolveRelativeUri " + uri); if (uri.charAt(0) == '/') { return uri; } else { String baseURI = jspFile.substring(0, jspFile.lastIndexOf('/')); return baseURI + '/' + uri; } } public java.io.InputStream getResourceAsStream(String res) { if( debug>0 ) log("getResourceAsStream " + res); return sctx.getResourceAsStream(res); } /** * Gets the actual path of a URI relative to the context of * the compilation. */ public String getRealPath(String path) { if( debug>0 ) log("getRealPath " + path + " = " + sctx.getRealPath( path )); return sctx.getRealPath( path ); } // development tracing private static int debug=0; private void log( String s ) { System.out.println("JasperEngineContext: "+ s); } } 1.1 jakarta-tomcat-jasper/jasper34/liaison/org/apache/jasper34/tomcat33/JasperOptionsImpl.java Index: JasperOptionsImpl.java =================================================================== /* * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.jasper34.tomcat33; import java.util.*; import java.io.*; import java.net.*; import org.apache.jasper34.core.*; //import org.apache.jasper.Options; /** Another implementation of Options, backed by a Properties file * and with no external dependencies. */ public class JasperOptionsImpl implements Options { static final String ieClassId = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"; // cache private Class jspCompilerPlugin = null; // special property. private Object protectionDomain; Properties args; public JasperOptionsImpl( Properties args ) { this.args=args; } // -------------------- Options implementation -------------------- public boolean getKeepGenerated() { return s2b( args.getProperty("keepgenerated", "true") ); } public String getJavaEncoding() { return args.getProperty("javaEncoding", "UTF8"); } public boolean getLargeFile() { return s2b( args.getProperty("largefile", "false")); } public boolean getMappedFile() { return s2b( args.getProperty("mappedfile")); } public boolean getSendErrorToClient() { return s2b( args.getProperty( "sendErrToClient" )); } public boolean getClassDebugInfo() { return s2b( args.getProperty( "classDebugInfo" )); } public String getIeClassId() { return args.getProperty( "ieClassId" , ieClassId); } public File getScratchDir() { if( debug>0 ) log("Options: getScratchDir " ); return new File( args.getProperty( "scratchdir" )); } public final Object getProtectionDomain() { if( debug>0 ) log("Options: GetPD" ); return protectionDomain; } public String getClassPath() { if( debug>0 ) log("Options: GetCP " ); return args.getProperty( "classpath" ); } public Class getJspCompilerPlugin() { if( debug>0 ) log("Options: getJspCompilerPlugin " ); if( jspCompilerPlugin!= null ) return jspCompilerPlugin; String type=args.getProperty( "jspCompilerPlugin" ); if( type != null ) { try { jspCompilerPlugin=Class.forName(type); } catch(Exception ex ) { ex.printStackTrace(); } } return jspCompilerPlugin; } public String getJspCompilerPath() { return args.getProperty( "jspCompilerPath" ); } // -------------------- Setters -------------------- public void setProtectionDomain( Object pd ) { protectionDomain=pd; } // -------------------- private boolean s2b( String s ) { return new Boolean( s ).booleanValue(); } // trace for development purpose -------------------- private static int debug=0; private void log(String s) { System.err.println(s); } } 1.1 jakarta-tomcat-jasper/jasper34/liaison/org/apache/jasper34/tomcat33/JspInterceptor.java Index: JspInterceptor.java =================================================================== /* * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.jasper34.tomcat33; import javax.servlet.*; import javax.servlet.http.*; import javax.servlet.jsp.HttpJspPage; import javax.servlet.jsp.JspFactory; import java.util.*; import java.io.*; import java.net.*; import org.apache.tomcat.util.log.Log; import org.apache.tomcat.util.res.StringManager; import org.apache.tomcat.util.depend.*; import org.apache.tomcat.util.compat.*; import org.apache.jasper34.core.*; import org.apache.jasper34.runtime.*; import org.apache.jasper34.generator.*; import org.apache.jasper34.generator.Compiler; import org.apache.tomcat.core.*; import org.apache.tomcat.facade.*; /** * Plug in the JSP engine (a.k.a Jasper)! * Tomcat uses a "built-in" mapping for jsps ( *.jsp -> jsp ). "jsp" * can be either a real servlet (JspServlet) that compiles the jsp * and include the resource, or we can "intercept" and do the * compilation and mapping in requestMap stage. * * JspInterceptor will be invoked once per jsp, and will add an exact * mapping - all further invocation are identical with servlet invocations * with direct maps, with no extra overhead. * * Future - better abstraction for jsp->java converter ( jasper ), better * abstraction for java->class, plugin other jsp implementations, * better scalability. * * @author Anil K. Vijendran * @author Harish Prabandham * @author Costin Manolache */ public class JspInterceptor extends BaseInterceptor { static final String JIKES="org.apache.jasper.compiler.JikesJavaCompiler"; static final String JSP_SERVLET="org.apache.jasper34.servlet.JspServlet"; Properties args=new Properties(); // args for jasper boolean useJspServlet=false; String jspServletCN=JSP_SERVLET; String runtimePackage; // -------------------- Jasper options -------------------- // Options that affect jasper functionality. Will be set on // JspServlet ( if useJspServlet="true" ) or TomcatOptions. // IMPORTANT: periodically test for new jasper options /** * Are we keeping generated code around? */ public void setKeepGenerated( String s ) { args.put( "keepgenerated", s ); } /** * Are we supporting large files? */ public void setLargeFile( String s ) { args.put( "largefile", s ); } /** * Are we supporting HTML mapped servlets? */ public void setMappedFile( String s ) { args.put( "mappedfile", s ); } /** * Should errors be sent to client or thrown into stderr? */ public void setSendErrToClient( String s ) { args.put( "sendErrToClient", s ); } /** * Class ID for use in the plugin tag when the browser is IE. */ public void setIEClassId( String s ) { args.put( "ieClassId", s ); } /** * What classpath should I use while compiling the servlets * generated from JSP files? */ public void setClassPath( String s ) { args.put( "classpath", s ); } /** * What is my scratch dir? */ public void setScratchdir( String s ) { args.put( "scratchdir", s ); } /** * Path of the compiler to use for compiling JSP pages. */ public void setJspCompilerPath( String s ) { args.put( "jspCompilerPath", s ); } /** * What compiler plugin should I use to compile the servlets * generated from JSP files? * @deprecated Use setJavaCompiler instead */ public void setJspCompilerPlugin( String s ) { args.put( "jspCompilerPlugin", s ); } /** Include debug information in generated classes */ public void setClassDebugInfo( String s ) { args.put("classDebugInfo", s ); } public void setProperty( String n, String v ) { args.put( n, v ); } // -------------------- JspInterceptor properties -------------------- /** Use the old JspServlet to execute Jsps, instead of the new code. Note that init() never worked (AFAIK) and it'll be slower - but given the stability of JspServlet it may be a safe option. This will significantly slow down jsps. Default is false. */ public void setUseJspServlet( boolean b ) { useJspServlet=b; } /** Specify the implementation class of the jsp servlet. */ public void setJspServlet( String s ) { jspServletCN=s; } /** * What compiler should I use to compile the servlets * generated from JSP files? Default is "javac" ( you can use * "jikes" as a shortcut ). */ public void setJavaCompiler( String type ) { if( "jikes".equals( type ) ) type=JIKES; if( "javac".equals( type ) ) type="org.apache.jasper.compiler.SunJavaCompiler"; args.put( "jspCompilerPlugin", type ); } int pageContextPoolSize=JspFactoryImpl.DEFAULT_POOL_SIZE; /** Set the PageContext pool size for jasper factory. 0 will disable pooling of PageContexts. */ public void setPageContextPoolSize(int i) { pageContextPoolSize=i; } /** The generator will produce code using a different runtime ( default is org.apache.jasper.runtime ). The runtime must use the same names for classes as the default one, so the code will compile. */ public void setRuntimePackage(String rp ) { runtimePackage=rp; } // -------------------- Hooks -------------------- /** * Jasper-specific initializations, add work dir to classpath, */ public void addContext(ContextManager cm, Context ctx) throws TomcatException { if( runtimePackage!=null ) { Constants.JSP_RUNTIME_PACKAGE=runtimePackage; Constants.JSP_SERVLET_BASE=runtimePackage+".HttpJspBase"; } JspFactoryImpl factory=new JspFactoryImpl(pageContextPoolSize); JspFactory.setDefaultFactory(factory); // jspServlet uses it's own loader. We need to add workdir // to the context classpath to use URLLoader and normal // operation // XXX alternative: use WEB-INF/classes for generated files if( ! useJspServlet ) { try { // Note: URLClassLoader in JDK1.2.2 doesn't work with file URLs // that contain '\' characters. Insure only '/' is used. // jspServlet uses it's own mechanism URL url=new URL( "file", null, ctx.getWorkDir().getAbsolutePath().replace('\\','/') + "/"); ctx.addClassPath( url ); if( debug > 9 ) log( "Added to classpath: " + url ); } catch( MalformedURLException ex ) { } } } /** Do the needed initialization if jspServlet is used. * It must be called after Web.xml is read ( WebXmlReader ). */ public void contextInit(Context ctx) throws TomcatException { if( useJspServlet ) { // prepare jsp servlet. Handler jasper=ctx.getServletByName( "jsp" ); if ( debug>10) log( "Got jasper servlet " + jasper ); ServletHandler jspServlet=(ServletHandler)jasper; if( jspServlet.getServletClassName() == null ) { log( "Jsp already defined in web.xml " + jspServlet.getServletClassName() ); return; } if( debug>-1) log( "jspServlet=" + jspServlet.getServletClassName()); Enumeration enum=args.keys(); while( enum.hasMoreElements() ) { String s=(String)enum.nextElement(); String v=(String)args.get(s); if( debug>0 ) log( "Setting " + s + "=" + v ); jspServlet.getServletInfo().addInitParam(s, v ); } if( debug > 0 ) { //enable jasperServlet logging log( "Seetting debug on jsp servlet"); Constants.jasperLog= loghelper; // org.apache.jasper.Constants.jasperLog. // setVerbosityLevel("debug"); } jspServlet.setServletClassName(jspServletCN); } else { ctx.addServlet( new JspPrecompileH()); } } /** Set the HttpJspBase classloader before init, * as required by Jasper */ public void preServletInit( Context ctx, Handler sw ) throws TomcatException { if( ! (sw instanceof ServletHandler) ) return; try { // requires that everything is compiled Servlet theServlet = ((ServletHandler)sw).getServlet(); if (theServlet instanceof HttpJspBase) { if( debug > 9 ) log( "PreServletInit: HttpJspBase.setParentClassLoader" + sw ); HttpJspBase h = (HttpJspBase) theServlet; h.setClassLoader(ctx.getClassLoader()); } } catch(Exception ex ) { throw new TomcatException( ex ); } } //-------------------- Main hook - compile the jsp file if needed /** Detect if the request is for a JSP page and if it is find the associated servlet name and compile if needed. That insures that init() will take place on the equivalent servlet - and behave exactly like a servlet. A request is for a JSP if: - the handler is a ServletHandler ( i.e. defined in web.xml or dynamically loaded servlet ) and it has a "path" instead of class name - the handler has a special name "jsp". That means a *.jsp -> jsp needs to be defined. This is a tomcat-specific mechanism ( not part of the standard ) and allow users to associate other extensions with JSP by using the "fictious" jsp handler. An (cleaner?) alternative for mapping other extensions would be to set them on JspInterceptor. */ public int requestMap( Request req ) { if( useJspServlet ) { // no further processing - jspServlet will take care // of the processing as before ( all processing // will happen in the handle() pipeline. return 0; } Handler wrapper=req.getHandler(); if( wrapper==null ) return 0; // It's not a jsp if it's not "*.jsp" mapped or a servlet if( (! "jsp".equals( wrapper.getName())) && (! (wrapper instanceof ServletHandler)) ) { return 0; } ServletHandler handler=null; String jspFile=null; // There are 2 cases: extension mapped and exact map with // a <servlet> with file-name declaration // note that this code is called only the first time // the jsp page is called - all other calls will treat the jsp // as a regular servlet, nothing is special except the initial // processing. // XXX deal with jsp_compile if( "jsp".equals( wrapper.getName())) { // if it's an extension mapped file, construct and map a handler jspFile=req.servletPath().toString(); // extension mapped jsp - define a new handler, // add the exact mapping to avoid future overhead handler= mapJspPage( req.getContext(), jspFile ); req.setHandler( handler ); } else if( wrapper instanceof ServletHandler) { // if it's a simple servlet, we don't care about it handler=(ServletHandler)wrapper; jspFile=handler.getServletInfo().getJspFile(); if( jspFile==null ) return 0; // not a jsp } // if it's a jsp_precompile request, don't execute - just // compile ( if needed ). Since we'll compile the jsp on // the first request the only special thing is to not // execute the jsp if jsp_precompile param is in parameters. String qString=req.queryString().toString(); // look for ?jsp_precompile or &jsp_precompile // quick test to see if we need to worry about params // ( preserve lazy eval for parameters ) boolean pre_compile=false; int i=(qString==null) ? -1: qString.indexOf( "jsp_precompile" ); if( i>= 0 ) { // Probably we are in the problem case. req.parameters().handleQueryParameters(); String p=req.parameters().getParameter( "jsp_precompile"); if( p==null || p.equalsIgnoreCase("true")) { pre_compile=true; } } // Each .jsp file is compiled to a servlet, and will // have a dependency to check if it's expired Dependency dep= handler.getServletInfo().getDependency(); if( dep!=null && ! dep.isExpired() ) { // if the jspfile is older than the class - we're ok // this happens if the .jsp file was compiled in a previous // run of tomcat. return 0; } // we need to compile... ( or find previous .class ) JasperLiaison liasion=new JasperLiaison(getLog(), debug); liasion.processJspFile(req, jspFile, handler, args); if( pre_compile ) { // we may have compiled the page ( if needed ), but // we can't execute it. The handler will just // report that we detected the trick. // Future: detail information about compile results // and if indeed we had to do something or not req.setHandler( ctx. getServletByName( "tomcat.jspPrecompileHandler")); } return 0; } // -------------------- Utils -------------------- private static final String SERVLET_NAME_PREFIX="TOMCAT/JSP"; /** Add an exact map that will avoid *.jsp mapping and intermediate * steps. It's equivalent with declaring * <servlet-name>tomcat.jsp.[uri]</> * <servlet-mapping><servlet-name>tomcat.jsp.[uri]</> * <url-pattern>[uri]</></> */ private ServletHandler mapJspPage( Context ctx, String uri) { String servletName= SERVLET_NAME_PREFIX + uri; if( debug>0) log( "mapJspPage " + ctx + " " + " " + servletName + " " + uri ); Handler h=ctx.getServletByName( servletName ); if( h!= null ) { log( "Name already exists " + servletName + " while mapping " + uri); return (ServletHandler)h; // exception ? } ServletHandler wrapper=new ServletHandler(); wrapper.setModule( this ); wrapper.setContext(ctx); wrapper.setName(servletName); wrapper.getServletInfo().setJspFile( uri ); // add the mapping - it's a "invoker" map ( i.e. it // can be removed to keep memory under control. // The memory usage is smaller than JspSerlvet anyway, but // can be further improved. try { ctx.addServlet( wrapper ); ctx.addServletMapping( uri , servletName ); if( debug > 0 ) log( "Added mapping " + uri + " path=" + servletName ); } catch( TomcatException ex ) { log("mapJspPage: ctx=" + ctx + ", servletName=" + servletName, ex); return null; } return wrapper; } } // -------------------- Jsp_precompile handler -------------------- /** What to do for jsp precompile */ class JspPrecompileH extends Handler { static StringManager sm=StringManager. getManager("org.apache.tomcat.resources"); JspPrecompileH() { name="tomcat.jspPrecompileHandler"; } public void doService(Request req, Response res) throws Exception { res.setContentType("text/html"); String msg="<h1>Jsp Precompile Done</h1>"; res.setContentLength(msg.length()); res.getBuffer().write( msg ); } } // -------------------- The main Jasper Liaison -------------------- final class JasperLiaison { Log log; final int debug; JasperLiaison( Log log, int debug ) { this.log=log; this.debug=debug; } /** Generate mangled names, check for previous versions, * generate the .java file, compile it - all the expensive * operations. This happens only once ( or when the jsp file * changes ). */ int processJspFile(Request req, String jspFile, ServletHandler handler, Properties args) { // ---------- Expensive part - compile and load // If dep==null, the handler was never used - we need // to either compile it or find the previous compiled version // If dep.isExpired() we need to recompile. if( debug > 10 ) log.log( "Before compile sync " + jspFile ); synchronized( handler ) { // double check - maybe another thread did that for us Dependency dep= handler.getServletInfo().getDependency(); if( dep!=null && ! dep.isExpired() ) { // if the jspfile is older than the class - we're ok return 0; } Context ctx=req.getContext(); // Mangle the names - expensive operation, but nothing // compared with a compilation :-) JasperMangler mangler= new JasperMangler(ctx.getWorkDir().getAbsolutePath(), ctx.getAbsolutePath(), jspFile ); // register the handler as dependend of the jspfile if( dep==null ) { dep=setDependency( ctx, mangler, handler ); // update the servlet class name handler.setServletClassName( mangler.getServletClassName() ); // check again - maybe we just found a compiled class from // a previous run if( ! dep.isExpired() ) return 0; } // if( debug > 3) ctx.log( "Compiling: " + jspFile + " to " + mangler.getServletClassName()); //XXX old servlet - destroy(); // jump version number - the file needs to be recompiled // reset the handler error, un-initialize the servlet handler.setErrorException( null ); handler.setState( Handler.STATE_ADDED ); // Move to the next class name mangler.nextVersion(); // record time of attempted translate-and-compile // if the compilation fails, we'll not try again // until the jsp file changes dep.setLastModified( System.currentTimeMillis() ); // Update the class name in wrapper if( debug> 1 ) log.log( "Update class Name " + mangler.getServletClassName()); handler.setServletClassName( mangler.getServletClassName() ); try { Options options=new JasperOptionsImpl(args); JspCompilationContext ctxt=createCompilationContext(req, jspFile, options, mangler); jsp2java( mangler, ctxt ); javac( options, ctxt, mangler ); if(debug>0)log.log( "Generated " + mangler.getClassFileName() ); } catch( Exception ex ) { if( ctx!=null ) ctx.log("compile error: req="+req, ex); else log.log("compile error: req="+req, ex); handler.setErrorException(ex); handler.setState(Handler.STATE_DISABLED); // until the jsp cahnges, when it'll be enabled again return 500; } dep.setExpired( false ); } return 0; } /** Convert the .jsp file to a java file, then compile it to class */ void jsp2java(JasperMangler mangler, JspCompilationContext ctxt) throws Exception { if( debug > 0 ) log.log( "Generating " + mangler.getJavaFileName()); // make sure we have the directories String javaFileName=mangler.getJavaFileName(); File javaFile=new File(javaFileName); // make sure the directory is created new File( javaFile.getParent()).mkdirs(); Compiler compiler=new Compiler(ctxt); compiler.setMangler( mangler ); // we will compile ourself compiler.setJavaCompiler( null ); synchronized ( mangler ) { compiler.compile(); } if( debug > 0 ) { File f = new File( mangler.getJavaFileName()); log.log( "Created file : " + f + " " + f.lastModified()); } } String javaEncoding = "UTF8"; // perhaps debatable? static String sep = System.getProperty("path.separator"); private void prepareCompiler( JavaCompiler javac, Options options, JspCompilationContext ctxt ) throws JasperException { String compilerPath = options.getJspCompilerPath(); if (compilerPath != null) javac.setCompilerPath(compilerPath); javac.setClassDebugInfo(options.getClassDebugInfo()); javac.setEncoding(javaEncoding); String cp=System.getProperty("java.class.path")+ sep + ctxt.getClassPath() + sep + ctxt.getOutputDir(); javac.setClasspath( cp ); javac.setOutputDir(ctxt.getOutputDir()); if( debug>5) log.log( "ClassPath " + cp); } static boolean tryJikes=true; static Class jspCompilerPlugin = null; /** Compile a java to class. This should be moved to util, togheter with JavaCompiler - it's a general purpose code, no need to keep it part of jasper */ void javac(Options options, JspCompilationContext ctxt, Mangler mangler) throws JasperException { String javaFileName = mangler.getJavaFileName(); if( debug>0 ) log.log( "Compiling java file " + javaFileName); boolean status=true; if( jspCompilerPlugin == null ) { jspCompilerPlugin=options.getJspCompilerPlugin(); } // If no explicit compiler, and we never tried before if( jspCompilerPlugin==null && tryJikes ) { ByteArrayOutputStream out = new ByteArrayOutputStream (256); try { jspCompilerPlugin=Class. forName("org.apache.jasper.compiler.JikesJavaCompiler"); JavaCompiler javaC=createJavaCompiler( jspCompilerPlugin ); prepareCompiler( javaC, options, ctxt ); javaC.setMsgOutput(out); status = javaC.compile(javaFileName); } catch( Exception ex ) { log.log("Guess java compiler: no jikes " + ex.toString()); status=false; } if( status==false ) { log.log("Guess java compiler: no jikes "); log.log("Guess java compiler: OUT " + out.toString()); jspCompilerPlugin=null; tryJikes=false; } else { log.log("Guess java compiler: using jikes "); } } JavaCompiler javaC=createJavaCompiler( jspCompilerPlugin ); prepareCompiler( javaC, options, ctxt ); ByteArrayOutputStream out = new ByteArrayOutputStream (256); javaC.setMsgOutput(out); status = javaC.compile(javaFileName); if (!ctxt.keepGenerated()) { File javaFile = new File(javaFileName); javaFile.delete(); } if (status == false) { String msg = out.toString (); throw new JasperException("Unable to compile " + msg); } if( debug > 0 ) log.log("Compiled ok"); } /** tool for customizing javac. */ public JavaCompiler createJavaCompiler(Class jspCompilerPlugin ) throws JasperException { JavaCompiler javac; if (jspCompilerPlugin != null) { try { javac = (JavaCompiler) jspCompilerPlugin.newInstance(); } catch (Exception ex) { Constants.message("jsp.warning.compiler.class.cantcreate", new Object[] { jspCompilerPlugin, ex }, Log.FATAL); javac = new SunJavaCompiler(); } } else { javac = new SunJavaCompiler(); } return javac; } private String computeClassPath(Context ctx) { String separator = System.getProperty("path.separator", ":"); URL classP[]=ctx.getClassPath(); String cpath = ""; cpath+=extractClassPath(classP); Jdk11Compat jdkProxy=Jdk11Compat.getJdkCompat(); URL appsCP[]; URL commonCP[]; ClassLoader parentLoader=ctx.getContextManager().getParentLoader(); appsCP=jdkProxy.getParentURLs(parentLoader); commonCP=jdkProxy.getURLs(parentLoader); if( appsCP!=null ) cpath+=separator+extractClassPath(appsCP); if( commonCP!=null ) cpath+=separator+extractClassPath(commonCP); return cpath; } String extractClassPath(URL urls[]){ String separator = System.getProperty("path.separator", ":"); String cpath=""; for(int i=0; i< urls.length; i++ ) { URL cp = urls[i]; if( cp == null ) { continue; } File f = new File( cp.getFile()); if (cpath.length()>0) cpath += separator; cpath += f; } return cpath; } private JspCompilationContext createCompilationContext( Request req, String jspFile, Options opt, Mangler mangler) { JasperEngineContext ctxt = new JasperEngineContext(); ctxt.setServletClassName( mangler.getClassName()); // ctxt.setJspFile( req.servletPath().toString()); ctxt.setJspFile( jspFile ); ctxt.setClassPath( computeClassPath( req.getContext()) ); // System.out.println("computeClasspath:"+ctxt.getClassPath()); ctxt.setServletContext( req.getContext().getFacade()); ctxt.setOptions( opt ); ctxt.setClassLoader( req.getContext().getClassLoader()); ctxt.setOutputDir(req.getContext().getWorkDir().getAbsolutePath()); return ctxt; } // Add an "expire check" to the generated servlet. private Dependency setDependency( Context ctx, JasperMangler mangler, ServletHandler handler ) { ServletInfo info=handler.getServletInfo(); // create a lastModified checker. if( debug>0) log.log("Registering dependency for " + handler ); Dependency dep=new Dependency(); dep.setOrigin( new File(mangler.getJspFilePath()) ); dep.setTarget( handler ); dep.setLocal( true ); File f=new File( mangler.getClassFileName() ); if( mangler.getVersion() > 0 ) { // it has a previous version dep.setLastModified(f.lastModified()); // update the "expired" variable dep.checkExpiry(); } else { dep.setLastModified( -1 ); dep.setExpired( true ); } if( debug>0 ) log.log( "file = " + mangler.getClassFileName() + " " + f.lastModified() ); if( debug>0 ) log.log("origin = " + dep.getOrigin() + " " + dep.getOrigin().lastModified()); try { DependManager dm=(DependManager)ctx.getContainer(). getNote("DependManager"); if( dm!=null ) { dm.addDependency( dep ); } } catch( TomcatException ex ) { ex.printStackTrace(); } info.setDependency( dep ); return dep; } }