简体   繁体   English

如何在启动时使Tomcat预编译JSP?

[英]How can I make Tomcat pre-compile JSPs on startup?

We're using both Apache Tomcat 6.0 and Jetty 6 where I work. 我正在使用Apache Tomcat 6.0和Jetty 6。 We mostly use Jetty for testing (it's great for running embedded in JUnit tests) and Tomcat for production. 我们主要使用Jetty进行测试(它非常适合在JUnit测试中运行嵌入式)和Tomcat用于生产。

By default, Tomcat compiles JSPs on-the-fly as users request them. 默认情况下,Tomcat会在用户请求时动态编译JSP。 But this results in degraded performance for the first hit. 但这会导致第一次打击的性能下降。 It also highlights bizarre bugs in Tomcat's JSP compiler. 它还突出了Tomcat的JSP编译器中的奇怪错误

The Tomcat documentation gives recommendations for pre-compiling JSPs at build time using Ant (and a Maven plugin is also available)... but the resulting WAR contains Tomcat-specific stuff eg PageContextImpl.proprietaryEvaluate, so we can't use it in Jetty. Tomcat文档提供了在构建时使用Ant预编译JSP的建议(并且还提供了Maven插件)...但是生成的WAR包含特定于Tomcat的东西,例如PageContextImpl.proprietaryEvaluate,因此我们不能在Jetty中使用它。

Is there some flag or setting we can use somewhere to force Tomcat to precompile all JSPs as soon as the WAR is initialized? 是否有一些标志或设置我们可以在某处使用强制Tomcat在WAR初始化时预编译所有JSP? We're prepared to wait a little longer on startup for this. 我们准备在启动时等待一段时间。

In advance: I know there's a way to pre-compile exactly one JSP by explicitly identifying a /servlet/load-on-startup tag in web.xml for one JSP. 在推进:通过明确识别web.xml中/的servlet /时加载的启动标签为一个JSP我知道有一种方法预编译正是一个 JSP。 But for dozens or even hundreds of JSPs that becomes unmanageable. 但对于几十甚至几百个难以管理的JSP。

http://www.devshed.com/c/a/BrainDump/Tomcat-Capacity-Planning/ http://www.devshed.com/c/a/BrainDump/Tomcat-Capacity-Planning/


    project name="pre-compile-jsps" default="compile-jsp-servlets">

  <!-- Private properties. -- >
  <property name="webapp.dir" value="${basedir}/webapp-dir"/>
  <property name="tomcat.home" value="/opt/tomcat"/>
  <property name="jspc.pkg.prefix" value="com.mycompany"/>
  <property name="jspc.dir.prefix" value="com/mycompany"/> 

  <!-- Compilation properties. -->
  <property name="debug" value="on"/> 
  <property name="debuglevel" value="lines,vars,source"/>
  <property name="deprecation" value="on"/>
  <property name="encoding" value="ISO-8859-1"/>
  <property name="optimize" value="off"/>
  <property name="build.compiler" value="modern"/>
  <property name="source.version" value="1.5"/> 

  <!-- Initialize Paths. -->
  <path id="jspc.classpath">
    <fileset dir="${tomcat.home}/bin">
      <include name="*.jar"/>
    </fileset>
    <fileset dir="${tomcat.home}/server/lib">
      <include name="*.jar"/>
    </fileset>
    <fileset dir="${tomcat.home}/common/i18n">
      <include name="*.jar"/>
    </fileset>
    <fileset dir="${tomcat.home}/common/lib">
      <include name="*.jar"/>
    </fileset>
    <fileset dir="${webapp.dir}/WEB-INF">
      <include name="lib/*.jar"/>
    </fileset>
    <pathelement location="${webapp.dir}/WEB-INF/classes"/>
    <pathelement location="${ant.home}/lib/ant.jar"/>
    <pathelement location="${java.home}/../lib/tools.jar"/>
  </path>
  <property name="jspc.classpath" refid="jspc.classpath"/> 

  <!--========================================================== -->
  <!-- Generates Java source and a web.xml file from JSP files.                     -->
  <!-- ==========================================================
-->
  <target name="generate-jsp-java-src"> 
    <mkdir dir="${webapp.dir}/WEB-INF/jspc-src/${jspc.dir.prefix}"/>
    <taskdef classname="org.apache.jasper.JspC" name="jasper2">
      <classpath>
        <path refid="jspc.classpath"/>
      </classpath>
    </taskdef>
    <touch file="${webapp.dir}/WEB-INF/jspc-web.xml"/>
    <jasper2 uriroot="${webapp.dir}"
             package="${jspc.pkg.prefix}" 
          webXmlFragment="${webapp.dir}/WEB-INF/jspc-web.xml" 
             outputDir="${webapp.dir}/WEB-INF/jspc-src/${jspc.dir.prefix}"
             verbose="1"/>
  </target> 

  <!-- ========================================================== -->
  <!-- Compiles (generates Java class files from) the JSP servlet -->
  <!-- source code that was generated by the JspC task.            -->
  <!-- ========================================================== -->
  <target name="compile-jsp-servlets" depends="generate-jsp-java-src">
    <mkdir dir="${webapp.dir}/WEB-INF/classes"/>
    <javac srcdir="${webapp.dir}/WEB-INF/jspc-src"
           destdir="${webapp.dir}/WEB-INF/classes"
           includes="**/*.java"
           debug="${debug}"
           debuglevel="${debuglevel}"
           deprecation="${deprecation}"
           encoding="${encoding}"
           optimize="${optimize}"
           source="${source.version}">
      <classpath>
        <path refid="jspc.classpath"/>
      </classpath>
    </javac>
  </target> 

  <!-- ========================================================= -->
  <!-- Cleans any pre-compiled JSP source, classes, jspc-web.xml -->
  <!-- ========================================================= -->
  <target name="clean">
    <delete dir="${webapp.dir}/WEB-INF/jspc-src"/>
    <delete dir="${webapp.dir}/WEB-INF/classes/${jspc.dir.prefix}"/>
    <delete file="${webapp.dir}/WEB-INF/jspc-web.xml"/>
  </target>

</project

This build file will find all of your webapp's JSP files, compile them into servlet classes, and generate servlet mappings for those JSP servlet classes. 此构建文件将查找所有webapp的JSP文件,将它们编译为servlet类,并为这些JSP servlet类生成servlet映射。 The servlet map pings it generates must go into your webapp's WEB-INF/web.xml file, but it would be difficult to write an Ant build file that knows how to insert the servlet mappings into your web.xml file in a repeatable way every time the build file runs. 它产生的servlet map ping必须进入你的webapp的WEB-INF / web.xml文件,但是编写一个知道如何以可重复的方式将servlet映射插入web.xml文件的Ant构建文件是很困难的。构建文件运行的时间。 Instead, we used an XML entity include so that the generated servlet mappings go into a new file every time the build file runs and that servlet mappings file can be inserted into your web.xml file via the XML entity include mechanism. 相反,我们使用XML实体包含,以便每次构建文件运行时生成的servlet映射都会进入新文件,并且可以通过XML实体包含机制将servlet映射文件插入到web.xml文件中。 To use it, your webapp's WEB- INF/web.xml must have a special entity declaration at the top of the file, plus a reference to the entity in the content of the web.xml file where you want the servlet mappings file to be included. 要使用它,您的webapp的WEB-INF / web.xml必须在文件顶部有一个特殊的实体声明,另外还有一个对web.xml文件内容中实体的引用,您希望servlet映射文件在该文件中包括在内。 Here is how an empty servlet 2.5 webapp's web.xml file looks with these modifications: 以下是一个空的servlet 2.5 webapp的web.xml文件如何看待这些修改:

<!DOCTYPE jspc-webxml [
    <!ENTITY jspc-webxml SYSTEM "jspc-web.xml">
  ]> 

  <web-app xmlns=http://java.sun.com/xml/ns/javaee
      xmlns:xsi=http://www.w3.org/2001/ XMLSchema-instance 
      xsi:schemaLocation="http:// java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/ 
  javaee/web-app_2_5.xsd"
      version="2.5"> 

    <!-- We include the JspC-generated mappings here. -->
    &jspc-webxml; 

    <!-- Non-generated web.xml content goes here. --> 

  </web-app> 

Make sure your webapp's web.xml file has the inline DTD (the DOCTYPE tag) all the way at the top of the file and the servlet 2.5 web-app schema declaration below that. 确保webapp的web.xml文件在文件顶部和下面的servlet 2.5 web-app模式声明中一直有内联DTD(DOCTYPE标记)。 Then, wherever you want to insert the generated servlet mappings in your web.xml file, put the entity reference &jspc-webxml; 然后,无论您希望在web.xml文件中插入生成的servlet映射,请将实体引用&jspc-webxml; . Remember, the entity reference begins with an ampersand ( & ), then has the name of the entity, and ends with a semicolon ( ; ). 请记住,实体引用以和号(&)开头,然后具有实体的名称,并以分号(;)结尾。

To use the build file, just edit it and set all of the properties at the top to values that match your setup, and then run it like this: 要使用构建文件,只需编辑它并将顶部的所有属性设置为与您的设置匹配的值,然后像下面这样运行:

$ ant -f pre-compile-jsps.xml $ ant -f pre-compile-jsps.xml

If you go with the solution mentioned by duffymo pointing to Vinny Carpenter's blog, I have a tip. 如果你选择duffymo提到的解决方案指向Vinny Carpenter的博客,我有一个提示。 There was one area that caused my container to hang indefinitely while contacting localhost (specifically in the private connect() method). 有一个区域导致我的容器在联系localhost时无限期挂起(特别是在private connect()方法中)。 Using the following hack was my workaround: 使用以下hack是我的解决方法:

    private void connect(final String urlString) {

        HttpURLConnection conn;
        try {
            final URL url = new URL(urlString);
            conn = (HttpURLConnection)url.openConnection();
            conn.setConnectTimeout(5000);
            //time it out quickly - otherwise hangs forever
            //seems to be an issue hitting localhost
            //will still precompile the page
            conn.setReadTimeout(100);
            conn.setAllowUserInteraction(true);
            conn.getInputStream();
            conn.disconnect();
        }
        catch (SocketTimeoutException e) {
            log.debug(e);
        }
        catch (IOException ioe) {
            log.error(ioe);
        }
    }

Setting a timeout and ignoring the SocketTimeoutException worked (though admittedly not the best solution). 设置超时并忽略SocketTimeoutException(虽然不可能是最好的解决方案)。 Also, using this procedure means that you would need to specify the JSPs in web.xml. 此外,使用此过程意味着您需要在web.xml中指定JSP。 This was sufficient for my needs. 这足以满足我的需求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM