Saturday, July 05, 2003
Precompiling JSP's under JBoss/Jetty 3.2.1...
One of the things I've been meaning to do for quite some time now was to modify our build process to precompile our JSP's. With so many other higher priority things this kept getting pushed to the back burner... After the 10th time someone reported a bug which amounted to "first access to webpage seems abysmally slow", I finally broke down and started on this on the plane ride home from Montreal this week. Here's a summation of what is required to get JSP precompilation going under JBoss/Jetty 3.2.1.
First, a high-level rundown of the process :
In the above fragment, the following two properties are defined earlier :
And cp.jspc is defined as the following :
Now, modify your existing web.xml file to have the following comment in it, just AFTER the last <servlet> tag and just BEFORE the first <servlet-mapping> tag.
This comment will serve as the insertion point where we'll include the jspc generated web.xml fragment. Here's an example of our web.xml file :
OK - that's about it. A couple of things to watch out for :
- Using Jasper (a JSP compiler), we'll transform the JSP's into .java files (servlets).
- Jasper will also spit out a web.xml fragment with servlet definitions and mappings for each of the above generated servlets.
- We'll use the ant jspc task to make the previous two bullets a little easier.
- We'll compile the .java files into .class files and then copy them into the WEB-INF/classes directory of the WAR
- We'll insert the web.xml fragment generated by Jasper into your normal web.xml file (at a very specific place).
- build your war as normal
<target name="dist" depends="compile"> <!-- copy required libs to WEB-INF/lib--> <copy todir="${dir.webinf}/lib"> <fileset dir="${dir.thirdpartytools.struts}/lib"/> <fileset dir="${dir.thirdpartytools.jstl}/lib"/> <fileset dir="${dir.search}/dist/lib" includes="${jar.name.search-domain}, ${jar.name.search-services}"/> </copy> <!-- copy classes we built earlier to WEB-INF/classes --> <copy todir="${dir.webinf}/classes"> <fileset dir="${classes}"/> </copy> <!-- copy the tld's we're using to WEB-INF/tld --> <copy todir="${dir.webinf}/tld"> <fileset dir="${dir.thirdpartytools.struts}/tld" includes="*.tld"/> <fileset dir="${dir.thirdpartytools.jstl}/tld" includes="*.tld"/> <fileset dir="${dir.config}/tld" includes="*.tld"/> </copy> <!-- these two properties are the generated web.xml fragment, and the resulting merged xml--> <property name="jspc.webxml.fragment" value="${dir.jspc.temp}/jspc-web-xml-fragment.xml"/> <property name="jspc.webxml.merged" value="${dir.jspc.temp}/merged-web.xml"/> <path id="jspc.classpath"> <path refid="cp.jspc"/> <path refid="cp.jboss"/> <path refid="cp.webclient-thirdparty.tools"/> <path refid="build.classpath"/> <fileset dir="${classes}"/> <fileset dir="${dir.webinf}/lib" includes="*.jar"/> </path> <!-- JSP -> .java --> <jspc srcdir="${dir.docroot}" destdir="${dir.jspc.gensrc}" verbose="9" classpathref="jspc.classpath" webinc="${jspc.webxml.fragment}"> <include name="**/*.jsp"/> </jspc> <!-- .java -> .class --> <javac deprecation="${deprecation.javac}" fork="${fork.javac}" source="${source}" debug="${debug}" srcdir="${dir.jspc.gensrc}" destdir="${dir.webinf}/classes" classpathref="jspc.classpath"/> <!-- merge the jspc generated web.xml fragment with the real web.xml--> <loadfile property="jspc.webxml.fragment.contents" srcFile="${jspc.webxml.fragment}"/> <copy file="${dir.webinf}/web.xml" tofile="${jspc.webxml.merged}"/> <replace file="${jspc.webxml.merged}"> <replacefilter token="<!-- @JSPC-INSERT-HERE@ -->" value="${jspc.webxml.fragment.contents}"/> </replace> <!-- Now war everything up --> <war destfile="${dist}/lib/${jar.name.epcportal-presentation}" webxml="${jspc.webxml.merged}"> <fileset dir="${dir.docroot}"/> <classes dir="${classes}"/> <classes dir="${dir.property.file}"> <include name="**/*.properties"/> </classes> </war> </target>
<property name="dir.jspc.temp" value="${dist}/tmp/jspc"/> <property name="dir.jspc.gensrc" value="${dir.jspc.temp}/gensrc"/>
<path id="cp.jspc"> <pathelement location="${jboss.home}/server/default/deploy/jbossweb-jetty.sar/jasper-compiler.jar"/> <pathelement location="${jboss.home}/server/default/deploy/jbossweb-jetty.sar/jasper-runtime.jar"/> <pathelement location="${ant_home}/lib/ant.jar"/> </path>
<!-- @JSPC-INSERT-HERE@ -->
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN' 'http://java.sun.com/dtd/web-app_2_3.dtd'> <web-app> <!-- Struts Action Servlet Configuration --> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>application</param-name> <param-value>ApplicationResources</param-value> </init-param> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>detail</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>validate</param-name> <param-value>true</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <!-- JSP-PRECOMPILER INSERTION POINT, DO NOT REMOVE, MODIFY OR MOVE THE FOLLOWING LINE--> <!-- @JSPC-INSERT-HERE@ --> <!-- Action Servlet Mapping --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- The Welcome File List --> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <error-page> <error-code>404</error-code> <location>/error404.jsp</location> </error-page> <error-page> <error-code>400</error-code> <location>/error400.jsp</location> </error-page> <error-page> <error-code>401</error-code> <location>/error401.jsp</location> </error-page> <error-page> <error-code>500</error-code> <location>/error500.jsp</location> </error-page> <!-- Struts Tag Library Descriptors --> <taglib> <taglib-uri>/tags/struts-bean</taglib-uri> <taglib-location>/WEB-INF/tld/struts-bean.tld</taglib-location> </taglib> <taglib> <taglib-uri>/tags/struts-html</taglib-uri> <taglib-location>/WEB-INF/tld/struts-html.tld</taglib-location> </taglib> <taglib> <taglib-uri>/tags/struts-logic</taglib-uri> <taglib-location>/WEB-INF/tld/struts-logic.tld</taglib-location> </taglib> <taglib> <taglib-uri>/tags/struts-nested</taglib-uri> <taglib-location>/WEB-INF/tld/struts-nested.tld</taglib-location> </taglib> <taglib> <taglib-uri>/tags/struts-tiles</taglib-uri> <taglib-location>/WEB-INF/tld/struts-tiles.tld</taglib-location> </taglib> <!-- JSTL --> <taglib> <taglib-uri>/tags/jstl-c</taglib-uri> <taglib-location>/WEB-INF/tld/c.tld</taglib-location> </taglib> <taglib> <taglib-uri>/tags/jstl-c-rt</taglib-uri> <taglib-location>/WEB-INF/tld/c-rt.tld</taglib-location> </taglib> <taglib> <taglib-uri>/tags/jstl-fmt</taglib-uri> <taglib-location>/WEB-INF/tld/fmt.tld</taglib-location> </taglib> <taglib> <taglib-uri>/tags/jstl-fmt-rt</taglib-uri> <taglib-location>/WEB-INF/tld/fmt-rt.tld</taglib-location> </taglib> <taglib> <taglib-uri>/tags/jstl-x</taglib-uri> <taglib-location>/WEB-INF/tld/x.tld</taglib-location> </taglib> <taglib> <taglib-uri>/tags/x-rt</taglib-uri> <taglib-location>/WEB-INF/tld/x-rt.tld</taglib-location> </taglib> <!-- Project Specific Tag Libs --> <taglib> <taglib-uri>/tags/epc</taglib-uri> <taglib-location>/WEB-INF/tld/epc.tld</taglib-location> </taglib> </web-app>
- The ant jspc task and jasper are VERY sensitive to the directory structure of your webapp. To get jasper to work, I had to make our pre-war directory structure pretty much exactly like the war archive structure. In other words, I had to make sure that the directory at task execution time was exactly like it would be in when looking at it in the war - all the classes that were destined to be in WEB-INF/classes were already there, all the libs which were destined to be in WEB-INF/lib were already there, all the TLD's were in their final destination (somewhere under WEB-INF), the web.xml file was already in WEB-INF, etc. This might seem obvious, but the ant <war> task will let you leave libs and classes in various directories and then bind them together into the appropriate archive with nested <lib> and <classes> tags
- Make sure you have at least Ant 1.5.3 (latest version of the ant jspc task)