While working on Tomcat 7 embedded to automate my integration tests, I realized that my integration tests wasted much of the time in starting/stopping the server. Even if I do not start the integration tests as often as the unit tests, it becomes rapidly irritating. Furthermore, during development I tend to restart the server a couple of times per hour, especially at the beginning of the project. Sure hot deployment helps, but it is not always enough.
On my Mac Book Pro, a cold start took around 10s. Interestingly enough, an empty Tomcat startups in less than a second. The problem comes from the fact that Tomcat 7 scans the classpath to find out annotations that declare Servlets using the
@WebServlet. This, even if you do not use that feature. Don’t get me wrong, not having to configure XML is cool but I am not ready to pay such a high price for it. Especially as the only servlet I use, is the JSF one.
Where do we start from?
For these tests, I use a JEE6 application with JSF, Weld and JPA (no EJBs) that runs under Tomcat.
This is a demo application called JEE-6-Demo that I use to teach JEE6. A mentioned previously, a cold start (without tuning anything) requires around 10s on my Mac Book Pro Intel Core i7 with 8Gb RAM :
INFO: Server startup in 9992 ms
Step 1: Avoid looking for
@WebSerlet and co.
By default, Tomcat 7 (along with the Servlet 3.0 specification) scans the classpath to look for classes that are annotated
@InitParamJSF. It is a nice feature as you do not have to specify the faces servlet anymore.
However, it comes at a price: depending of the classpath this can be very long.
To solve this issues, simple add the
metadata-complete="true" to the
web-app element of our
WEB-INF/web-xml attribute to avoid scanning the classpath.
1 2 3 4 5
Obviously, as it is no more automatically discovered, we have to manually add the faces servlet to the context:
1 2 3 4 5 6 7 8 9 10
Using these modifications in
web.xml, the startup time came down to around 4.5 seconds:
INFO: Server startup in 4404 ms
Step 2: Avoid looking for
@ManagedBean and co.
Similarly, the is a similar feature in JSF 2.0. By default, the JSF implementation looks for classes annotated with
As I use Weld and its
@SessionScoped, and so on, I can disable this feature in JSF.
1 2 3 4
Using this modification in
faces-config.xml, the startup time came down to around 3.7 seconds:
INFO: Server startup in 3730 ms
Step 3: Limiting Weld’s scanning
Finally, I would like to keep Weld scanning to discover the
@Inject, and other Weld annotations but I would like to limit it to my a subset of the classes of the jar. To that end simply add
weld:scan directive and include a pattern with packages to scan.
1 2 3 4 5 6 7 8 9 10 11
Using this modification in
beans.xml, the startup time came down to around 3.3 seconds:
INFO: Server startup in 3312 ms
To conclude, using these minor modifications I divided the startup time by three. This is very useful during development and integration tests when the server is started and stopped many times.