Developing with Tomcat and Eclipse: the Definitive Guide
I (Kalle) am a big fan of Jetty's clean APIs and I use it both for development and production depending on a project, but since I got tired of people complaining about Tomcat just because they haven't used it or use it wrong, I put this guide together for developing webapps (but obviously mostly Tapestry) in Eclipse with Tomcat.
Here's the toolset that you should be using: Eclipse 3.6, Sysdeo's Tomcat Plugin and Tomcat 7.x ( + M2Eclipse 0.1.x depending on whether you are using Maven or not). Eclipse WTP's generic container support is slow and error prone, just don't use it. There are other Tomcat plugins for Eclipse but Sysdeo's plugin is the most comprehensive one. Generic tools that support more than one container or technology tend to copy files around for easier deployment but avoid all of that - it slows things down and is completely unnecessary given the right set of tools.
It's assumed you have Java, Eclipse and m2eclipse installed. Below we use one of Tynamo's sample modules as an example but you could use any other web application as well. If you are otherwise starting pretty much from scratch, follow these steps to set everything up:
- Download Tomcat 7 and install to a location of choice
- Download Sysdeo Eclipse Tomcat Launcher. Unzip, copy the com.sysdeo.. folder to your /eclipse/plugins/ folder
- Restart Eclipse, verify you see the Tomcat control buttons in Eclipse toolbar
- svn checkout http://svn.codehaus.org/tynamo/trunk/tynamo-examples/simple/ tynamo-simple
- Import... / Existing Maven Projects, navigate to tynamo-simple folder
- Configure Tomcat Launcher:
- In Window / Preferences / Tomcat, set Tomcat version to 6.x (even for 7.x) and set the home folder. Set Context declaration mode to context files
- In Tomcat / JVM Settings, add Jar/Zip item and point to tomcat-juli.jar found in your TOMCAT_HOME/bin folder (this is only needed for 7.x)
- In Tomcat / JVM parameters, add parameters as appropriate. I typically use at least: -Dtapestry.production-mode=false and -XX:MaxPermSize=256m
- In Tomcat / Tomcat Manager App, set manager app url and manager admin username and password correctly. Set manager app url to .../manager/text (for Tomcat 7.x - the default /manager/ is correct for previous versions)
- Install devloader for Tomcat (for Tomcat 7.x use this devloader I modified. The one from Sysdeo's isn't yet updated to handle 7.x. Unzip and copy the org folder and its contents to TOMCAT_HOME/lib
- In project properties (choose project, ALT+ENTER) configure Java build path
- For src/main/resources folder, set the output to the same as the source folder (i.e. src/main/resources, Eclipse doesn't have a separate checkbox for "don't copy")
- For default output folder, set the output to something else, such as /target/eclipse-classes than the Maven default (/target/classes) so you can use both Eclipse and Maven from command line without them stepping on each other
- In project properties / Tomcat
- Check "Is a Tomcat Project"
- Context name as you like, I often use just /ROOT for simplicity
- "Subdirectory to set as web application root", set "src/main/webapp"
- On "DevLoader ClassPath" tab, check activate DevLoader. Configure as appropriate, but typically "Check All" libraries, find servlet-api and uncheck it (obviously the container contains those classes and wants to load from there rather than as part of the web application). Also uncheck all of jre libs and "MAVEN2_CLASSPATH_CONTAINER" at the bottom, and for the simple webapp, you also need to uncheck the jetty-libs, particularly the servlet api included by Jetty.
- Enjoy live class reloading and your optimized development cycle
If you know your way around Eclipse and Tomcat, there's no need to follow the steps literally but most of it applies regardless of Eclipse or Tomcat versions or even if you were using something else than Tapestry. I've been developing webapps this way for years, since the days of Eclipse 3.0 and Tomcat 5.0 (with MyFaces...) circa 2004. With run-jetty-run (which I do use frequently), it's simpler and more automated to get started but it's not possible to directly modify the container's classpath, you cannot reload the context only and it's not meant for running several web applications at the same (which may or may not be important for your use case). Choose the tool that better fits your development style, but in any case, it's always useful to know your tools. Also, it's always useful to support multiple configurations to harden your application. Typically, you should make sure that other people could simply run mvn jetty:run (or similar) from the command line to run your web application even if you don't use it for development. An automated and well-maintained development environment always pays dividends later regardless of whether it is an open source or a commercial web application that you are developing. Most of the designers I work with are happily running running mvn jetty:run and editing the Tapestry html templates live on their own systems without any knowledge of Java or Maven or the machinery that makes this possible in the background. Finally, if you checked out tynamo-simple for this guide, take a look at our functional tests that have served us well in numerous occasions.
One frustrating aspect with Sysdeo's launcher is the need to manually maintain the webapp classpath. When you are starting a new project, you frequently hit duplicate class errors which you need to fix first. However, in return you get full control of the classpath and very quick way of switching between different versions of a particular library. Personally I switch between development and release libraries quite often and nothing beats the convenience of developing and debugging a new version of a library in place together with your actual web application.
As mentioned earlier, a duplicate servlet-api in your webapplication classpath will mess up your container for sure. For Tapestry, if you see an exception trace similar to:
you can be pretty sure that you have a duplicate servlet-api in your classpath.
Most other issues running your webapp can traced down to classpath issues, such as library incompatibilities and classloader problems. Fix the common cases first and if you still don't know what the problem is, reduce the problem scope to minimum number of dependencies and then enable libraries one by one. Nowadays modern webapps use tens of different libraries today so this may often take some time but at least you'll learn something along the way and hopefully might even be able to eliminate a few dependencies that you didn't even know were included.