JSP Precompilation on JBoss AS
Java EE offers a convenient way to deploy (and redeploy) JSPs by compiling them at runtime when they are added or changed. It provides the performance and static typing of compiled code while not sacrificing the ease of use of script-like deployment. Unfortunately, since there's no build-time validation of JSP code, problems may go unnoticed until after the site is live.
Much of this problem can be avoided by properly delegating business logic to backing classes such as Seam components, Struts actions or EJBs. Using a tag-based view technology such as JSTL or JSF can further reduce JSP compilation errors. But many legacy systems have scriptlet-heavy pages that need the same treatment as normal Java classes.
Default Behavior (no precompilation)
Servlet containers like JBoss Web (and Apache Tomcat on which it is based) don't actually serve JSPs directly. What's actually executed is a compiled Servlet class mapped to a URL with a ".jsp" extension. The intermediate process that happens goes something like this:
The web container receives a request for /index.jsp.
The container checks its servlet registry to see if there's compiled code to support this page, there isn't.
The container then invokes jspc on the file, generating an intermediate .java file in the container's work directory.
The .java file is compiled using javac and the resulting class file is stored in the container's work directory.
An association is made between the URL /index.jsp and the newly compiled class.
This happens for each and every JSP as it is accessed, and can happen repeatedly if the modified date is changed on the JSP. Note that the compiled code is outside the web application deployment.
To precompile JSPs, we're going to perform steps 3-5 at build time and ship the compiled code with our WAR. This involves the following:
Generating Java classes for each JSP in the web application.
Compiling the classes.
Packaging the compiled classes in the WEB-INF/classes directory and creating servlet mappings in the web.xml for each JSP that was compiled.
A critical difference between a WAR that has precompiled JSPs and one that dosen't is the location of the Servlet classes that back each JSP. Without precompilation, JBoss Web happily does the standard behavior above, using its work directory as temporary space. But when we precompile, we have to short-circut the container's desire to watch JSP files and update their backing classes. That's where step 3 comes in, and it is the glue that makes the whole process work.
Example Ant build file
The attached build.xml (or build-5_1.xml for JBoss 5.1) shows only the JSP generation and compilation, so you'll need to integrate these tasks and related properties into your own build file as you see fit. To use it, you'll need to set the two properties to appropriate values based on where you've installed JBoss AS.
jboss.as.home - The root of your JBoss installation, sometimes referred to as JBOSS_HOME.
jboss.as.profile_name - The name of the server profile that contains a JBoss Web deployer. This is usually "default".
webapp.path - This is the relative path to your JSP directory from your project's home directory (the one containing the build file).
The jboss.as. properties are used for nothing more than locating the JBoss Web jars required for JSP generation and compilation. The webapp.path property tells jspc and javac where to look for JSP files and where to drop generated .java and .class files.
The provided file is non-destructive to your existing web.xml. This means that the generated classes are not associated with JSP URLs and thus the process is not complete. When the compile target is executed, a web.xml fragment called generated_web.xml as created in WEB-INF, right next to your existing web.xml. It's your job to merge the providedelements into your own web.xml. You can then package up your WAR and deploy it.
The merge can be done automatically by changing the addWebXmlMappings attribute value from false to true on thetask underneath the jspc target. Take caution when doing this, though, and make sure it's part of an automated build process and not part of your own web application source tree!
Deploying and Testing
When you deploy the packaged WAR file complete with generated and compiled JSPs, everything will appear normal, except that the short pause the first time you access a JSP will be gone. You can ensure that JSPs are in fact precompiled by removing the old automatically generated .java and .class files from the JBoss work directory. If all is as planned, your web application will continue to function even after these files are gone.
This document is based on Apache Tomcat's Jasper How-To page.