Sunday, December 8. 2019
Presenting JspC for Wildfly
During the past weeks I have been working in developing the JspC (JSP Compiler) utility tool for Wildfly. The old JBoss AS (JBoss Application Server) used a slightly modified version of Tomcat as its web module which, in turn, used Jasper as the JSP parsing and compiling engine. Therefore in those versions the JSP files could be pre-compiled in order to speed up their first execution just using the Tomcat tools. Tomcat offers an ANT task that let you pre-compile the JSP files of your WAR application. The process really parses each JSP file into a Java Servlet and then compiles it producing the final class file. The JspC tool also gives the possibility of creating the web.xml (or other alternative solution) for mapping the final classes to the initial JSP URL. The benefit of pre-compiling is mainly avoiding all this process the first time the JSP is requested, which, in applications with lots of JSPs, can be a slow task.
When Wildfly substituted the old web implementation with undertow, the JspC original utility stopped working (the generated classes are incompatible and do not execute OK). This is mainly because now the new sub-system uses a modified version of Jasper called Jastow that is incompatible with the resulting classes generated by the current Tomcat counterpart. There is a JIRA about all this that explains the current situation.
Some months ago I decided to try another approach, the idea is using directly the jastow version instead of re-using the Tomcat library for pre-compiling. For that I re-developed the JspC tool but linking to the current Wildfly libraries. This tool in Tomcat is also a command line utility that is used by the ANT task. Only the JspC command is going to be replaced for the moment, I think that the task gives no advantage right now that ANT is obsolete and not very used. This little maven project lets you execute the JspC tool in the command line and pre-compile the JSP files of a WAR application. The project is at very early stages but it is usable in many cases.
- The tool uses the TLDs (tag library descriptors) from the internal Wildfly libs (standard jstl and JSF).
- It also loads custom TLDs from the application libraries.
- Almost all the JspC Tomcat options are replicated with the same functionality.
- Other external libraries (JARs that are available to the application through modules or inside an EAR) can be passed to the tool using the -classpath option.
The tool should more or less work with common WAR applications (there are known cases like web-fragments that are not taken into account) but I really do not see any traction for it internally. So I decided to open the project in my personal github and start advertising it (for example in this blog). Maybe other people are stuck because of this lack of functionality and, in the end, this is a process that is done offline and previous to production. So you can just test the tool, if the JSPs are pre-compiled and work OK, you can modify the WAR app to include them.
The tool has been developed using maven. So the exec-maven-plugin is used to execute it and include all the needed Wildfly libraries. The idea behind the tool is that it can be profiled to work with several wildfly (and EAP) versions. Currently there are two profiles (wildfly18, the default one, and eap72) that define the correct library versions for those servers, for example the wildfly18 profile defines the following dependency versions.
<profile>
<id>wildfly18</id>
<properties>
<version.io.undertow.jastow.jastow>2.0.7.Final</version.io.undertow.jastow.jastow>
<version.org.jboss.metadata.jboss-metadata-web>13.0.0.Final</version.org.jboss.metadata.jboss-metadata-web>
<version.org.jboss.spec.javax.servlet.jsp.jboss-jsp-api>2.0.0.Final</version.org.jboss.spec.javax.servlet.jsp.jboss-jsp-api>
<version.org.jboss.logmanager.log4j-jboss-logmanager>1.2.0.Final</version.org.jboss.logmanager.log4j-jboss-logmanager>
<version.org.apache.taglibs.taglibs-standard-spec>1.2.6-RC1</version.org.apache.taglibs.taglibs-standard-spec>
<version.org.jboss.spec.javax.faces.jboss-jsf-api>3.0.0.Final</version.org.jboss.spec.javax.faces.jboss-jsf-api>
<version.com.sun.faces.jsf-impl>2.3.9.SP03</version.com.sun.faces.jsf-impl>
</properties>
</profile>
So, the JspC needs those libraries from Wildfly to work (mainly jastow, metadata information, servlet spec, log-manager and jstl/jsf). The profile specifies the exact version that the specific Wildfly server bundles. In order to add, for example, the next version 19 a new profile should be added with the new versions for that specific release. This way (while the libraries remain the same) adding a new (or older) version is just defining the list of needed libraries bundled in that version using a different profile.
Let's do a simple test. In order to use a sample application I decided to use a WAR application from the web. It is in Chinese but it does not matter, it is just a JSP application with more than 20 files. I have a video to present how the utility is used.
- First the hotel sample application is built.
- After that the application is deployed into Wildfly 18 using the console.
- When using the application, the JSP files are generated by the server under the tmp, so they are compiled at first use (normal behavior).
- Going to the maven target folder, the WAR app is exploded into another directory.
- Time to use the JspC. It is executed against the app and 21 JSP files are pre-compiled.
- Going to the output directory a JAR is created with the output servlet classes (note that a web fragment is used, -webfrg option, which generates a standard fragment file that is automatically loaded by the Wildfly server, the Servlets listed in that fragment are added to the app).
- Finally the new JAR file is moved into the WEB-INF/lib folder of the application and the WAR is re-created.
- After re-deploying the app, no JSP is generated, now the the pre-compiled classes are being used.
Here I copy the full commands done in the video.
# build the hotel application
mvn clean package
cd target
mkdir JavaWeb0_war
cd JavaWeb0_war
jar xvf ../JavaWeb0.war
# execute jspc and re-build the app
cd ${JSPC_HOME}
mkdir -p /tmp/lala/META-INF
mvn exec:java -Dexec.args="-v -p pre.compiled.jsps -d /tmp/lala -webapp /path/to/hotel-management-system/JavaWeb/target/JavaWeb0_war -webfrg /tmp/lala/META-INF/web-fragment.xml"
cd /tmp/lala
jar cvf precompiled-jsp.jar *
cd /path/to/hotel-management-system/JavaWeb/target/JavaWeb0_war
mv /tmp/lala/precompiled-jsp.jar WEB-INF/lib/
jar cMvf ../JavaWeb0.war *
It is just a simple example, but it is a generic application too, and it works. So, if you have an old JSP application and you are interested in pre-compiling JSPs in a newer Wildfly, you can just try the JspC project. Use the profile for your Wildfly version. If your server is a different version, please create the profile by yourself, you only need to check the dependencies of your Wildfly (look for the library versions in the modules directory), locate them in maven and add them to a new profile. Let's see if people are interested in this project.
Pre-compiled regards!
Comments