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

时间:2022-06-19 20:46:27

We're using both Apache Tomcat 6.0 and Jetty 6 where I work. We mostly use Jetty for testing (it's great for running embedded in JUnit tests) and Tomcat for production.

我正在使用Apache Tomcat 6.0和Jetty 6。我们主要使用Jetty进行测试(它非常适合在JUnit测试中运行嵌入式)和Tomcat用于生产。

By default, Tomcat compiles JSPs on-the-fly as users request them. But this results in degraded performance for the first hit. It also highlights bizarre bugs in Tomcat's JSP compiler.

默认情况下,Tomcat会在用户请求时动态编译JSP。但这会导致第一次打击的性能下降。它还突出了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 e.g. 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? We're prepared to wait a little longer on startup for this.

是否有一些标志或设置我们可以使用某个地方强制Tomcat在WAR初始化后立即预编译所有JSP?我们准备在启动时等待一段时间。

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. But for dozens or even hundreds of JSPs that becomes unmanageable.

事先:我知道有一种方法可以通过在web.xml中为一个JSP显式标识/ servlet / load-on-startup标记来预编译一个JSP。但对于数十甚至数百个难以管理的JSP而言。

2 个解决方案

#1


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. 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. 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. 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. Here is how an empty servlet 2.5 webapp’s web.xml file looks with these modifications:

此构建文件将查找所有webapp的JSP文件,将它们编译为servlet类,并为这些JSP servlet类生成servlet映射。它生成的servlet map ping必须进入你的webapp的WEB-INF / web.xml文件,但是编写一个知道如何以可重复的方式将servlet映射插入web.xml文件的Ant构建文件是很困难的。构建文件运行的时间。相反,我们使用XML实体包含,以便每次构建文件运行时生成的servlet映射都会进入新文件,并且可以通过XML实体包含机制将servlet映射文件插入到web.xml文件中。要使用它,您的webapp的WEB-INF / web.xml必须在文件顶部有一个特殊的实体声明,以及您希望servlet映射文件在web.xml文件内容中的实体的引用包括在内。以下是一个空的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. Then, wherever you want to insert the generated servlet mappings in your web.xml file, put the entity reference &jspc-webxml; . Remember, the entity reference begins with an ampersand ( & ), then has the name of the entity, and ends with a semicolon ( ; ).

确保您的webapp的web.xml文件在文件顶部和下面的servlet 2.5 web-app模式声明中一直有内联DTD(DOCTYPE标记)。然后,无论您希望在web.xml文件中插入生成的servlet映射,请将实体引用&jspc-webxml; 。请记住,实体引用以和号(&)开头,然后具有实体的名称,并以分号(;)结尾。

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

#2


If you go with the solution mentioned by duffymo pointing to Vinny Carpenter's blog, I have a tip. There was one area that caused my container to hang indefinitely while contacting localhost (specifically in the private connect() method). Using the following hack was my workaround:

如果你选择duffymo提到的解决方案指向Vinny Carpenter的博客,我有一个提示。有一个区域导致我的容器在联系localhost时无限期挂起(特别是在private connect()方法中)。使用以下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). Also, using this procedure means that you would need to specify the JSPs in web.xml. This was sufficient for my needs.

设置超时并忽略SocketTimeoutException工作(虽然无可否认不是最佳解决方案)。此外,使用此过程意味着您需要在web.xml中指定JSP。这足以满足我的需求。

#1


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. 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. 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. 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. Here is how an empty servlet 2.5 webapp’s web.xml file looks with these modifications:

此构建文件将查找所有webapp的JSP文件,将它们编译为servlet类,并为这些JSP servlet类生成servlet映射。它生成的servlet map ping必须进入你的webapp的WEB-INF / web.xml文件,但是编写一个知道如何以可重复的方式将servlet映射插入web.xml文件的Ant构建文件是很困难的。构建文件运行的时间。相反,我们使用XML实体包含,以便每次构建文件运行时生成的servlet映射都会进入新文件,并且可以通过XML实体包含机制将servlet映射文件插入到web.xml文件中。要使用它,您的webapp的WEB-INF / web.xml必须在文件顶部有一个特殊的实体声明,以及您希望servlet映射文件在web.xml文件内容中的实体的引用包括在内。以下是一个空的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. Then, wherever you want to insert the generated servlet mappings in your web.xml file, put the entity reference &jspc-webxml; . Remember, the entity reference begins with an ampersand ( & ), then has the name of the entity, and ends with a semicolon ( ; ).

确保您的webapp的web.xml文件在文件顶部和下面的servlet 2.5 web-app模式声明中一直有内联DTD(DOCTYPE标记)。然后,无论您希望在web.xml文件中插入生成的servlet映射,请将实体引用&jspc-webxml; 。请记住,实体引用以和号(&)开头,然后具有实体的名称,并以分号(;)结尾。

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

#2


If you go with the solution mentioned by duffymo pointing to Vinny Carpenter's blog, I have a tip. There was one area that caused my container to hang indefinitely while contacting localhost (specifically in the private connect() method). Using the following hack was my workaround:

如果你选择duffymo提到的解决方案指向Vinny Carpenter的博客,我有一个提示。有一个区域导致我的容器在联系localhost时无限期挂起(特别是在private connect()方法中)。使用以下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). Also, using this procedure means that you would need to specify the JSPs in web.xml. This was sufficient for my needs.

设置超时并忽略SocketTimeoutException工作(虽然无可否认不是最佳解决方案)。此外,使用此过程意味着您需要在web.xml中指定JSP。这足以满足我的需求。