Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

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.

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.

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.

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.

like image 879
Dan Fabulich Avatar asked Jan 31 '09 00:01

Dan Fabulich


People also ask

How do I Precompile Jsps?

To select precompilation, right-click the EAR or WAR file you wish to export and select Export > EAR file or Export > WAR file. In the Export dialog, select the checkbox Pre-compile JSP. Note: the pre-compile JSP option is available only to projects targeted to WebLogic Server 9.2 and 10.0 runtimes.

How does Tomcat compile JSP?

The first time a JSP page is requested, Tomcat passes it to Jasper which parses the code and sends it to its Java compiler (JDT, a component borrowed from the Eclipse IDE project) to have the servlet elements compiled into Java bytecode classes.


1 Answers

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:

<!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 ( ; ).

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

like image 79
ariso Avatar answered Sep 21 '22 08:09

ariso