Edit Java webapps Redux: Jetty Launcher and Equinox
A few weeks ago, I realized I was developing webapps the hard way, by re-deploying everytime I made a change. It's important to have a build process that can create a WAR and deploy, but it's also important to have a system where you can edit/save/view changes w/o ever deploying.
I spent some time this weekend working with Jetty Launcher, Eclipse 3.1 and the latest version of Equinox. Below are instructions on how to make the two work together. Once you've completed the steps below, you should be able to launch Jetty and edit/save Java files or JSP files in Equinox - and the changes will show up immediately in your webapp. No deployment or Ant/Maven interaction necessary.
I've tested this setup on Eclipse 3.1, using both OS X and Windows XP.
Step 1: Install Jetty and Launcher
Download and install Jetty. I tested 5.1.4 and 5.1.6 and both seem to work (6.0.0 Beta does not). In Eclipse, go to Help » Software Updates » Find and Install. Select Search for new features to install and click Next. Click on the New Remote Site button and enter "Jetty Launcher" for the name and "http://jettylauncher.sourceforge.net/updates" for the URL. Click OK, continue to download and restart your workspace.
Step 2: Install Equinox and create Eclipse project files
Download Equinox 1.5 beta 1 and extract to your workspace (I generally use c:\Source on Windows and /Users/${username}/dev on OS X). Download Maven 2.0, install it, and add $M2_HOME/bin to your $PATH. From the command line, cd into the equinox directory and type "mvn eclipse:eclipse". Get a cup of coffee or soda while you wait for Maven to download all the dependencies.
Once the project files have been created, open Eclipse and go to File » New » Project » Java Project. Click Next and type "equinox" in the Project name box. Click Finish to begin importing the project. You'll probably get an error like the following. Click OK to continue.
Click Next to continue (I had to click it a few times before it took me to the next screen). On the next screen (Define the Java build settings), select the web directory and click Configure inclusion and exclusion filters. Click the Add button for Exclusion patterns and enter "WEB-INF/classes/" (the trailing slash is important).
You're not done yet, now you need to define the M2_REPO variable that points to all the downloaded dependencies. Click the Libraries tab and then the "Add Variable" button as seen below.
Click the Configure Variables button and add a new one with a name of M2_REPO and Path of to your local Maven repository (/Users/${username}/.m2/repository on OS X and C:\Documents and Settings\${username}\.m2\repository on Windows). Click OK, Cancel and then Finish.
Step 3: Configure Jetty Launcher for Equinox
In Eclipse, go to Run » Debug and select Jetty Web in the Configurations panel. Click the New button. In the "Use Options" section, use "web" for the webapp root and (optionally) "/equinox" for the context name.
Jetty has issues running applications that use commons-logging, so you'll need to turn off all the INFO logging from the projects that Equinox uses. To do this, click on the Arguments tab and add the following to the list of VM arguments:
-DDEBUG -DDEBUG_PATTERNS=org.appfuse -DDEBUG_VERBOSE=-1
This can be placed on the line below the -Djetty.home argument. For more information on logging in Jetty, see the Jetty logging and debugging tutorial.
Step 4: Start Jetty in debug mode and modify to your hearts content
Click Apply and then Debug, and watch Jetty startup. If you go to http://localhost:8080/equinox/users.html in your browser, you should see a log message like the following:
11:44:25.855 DEBUG [SocketListener0-1] org.appfuse.web.UserController.handleRequest (UserController.java:24) >29> entering 'handleRequest' method...
You should be able to click on "UserController.java:24" to navigate to the UserController.java class. In this class, modify the log.debug(...) message, save the file and hit refresh on your browser. The console should spit out your updated log message. If it didn't, go to Window » Preferences » Workspace and make sure Build automatically is checked.
As far as I can tell, edit/save/refresh will work for .java and .jsp files, but not for .xml files. For that, the Jetty Launcher adds a Stop/Restart Jetty icon to your Eclipse toolbar. This setup seems to work great, except for the fact that you can't see when Jetty is done starting up in the console.
NOTE: I tried to get a similar setup working with the Tomcat Eclipse Plugin (v3.1 beta) and the Eclipse Web Tools Project (v0.7.1). Neither worked as smoothly, and the WTP wouldn't even deploy to Tomcat.
Posted by Bob Lee on November 28, 2005 at 07:15 PM MST #
Posted by gerryg on November 28, 2005 at 10:51 PM MST #
AFAIK, Resin 2.x does what you're talking about, but I'm not sure it works in 3.x. I'd love to see a howto replacing steps 3 and 4 with a Resin equivalent.
Posted by Matt Raible on November 28, 2005 at 11:43 PM MST #
Posted by Will Jaynes on November 29, 2005 at 01:10 PM MST #
Thanks for the info Will. So my next question is - do you and Gerry really store your web projects in source control in an exploded WAR format? I typically put my web sources in a "web" directory (I've seen others use src/webapp) - rather than everything in the root project directory.
My guess is that Equinox already conforms to the exploded WAR format with the following structure?
So I should be able to configure Resin to point at my "web" directory for an application's context root? If so, this doesn't solve the problem of re-compiling my sources.
Here's a guess of how you guys might be structuring your projects:
Is this the way you do it? I've yet to see a project that stores their sources under WEB-INF/classes, but to each their own. ;-) Do you know the configuration settings to setup Resin in Eclipse so I can start/stop from w/in the IDE?
Posted by Matt Raible on November 29, 2005 at 03:49 PM MST #
Posted by Will Jaynes on November 29, 2005 at 07:57 PM MST #
I don't keep my JSPs under WEB-INF, though, just in the regular webapp root area in subfolders, usually Struts module names, as our projects typically use modules where we have multiple struts-config.xml files to better support larger teams writing a webapp (just finished 1-year project to build webapp, had 5 developers for 75% of it, sometimes 6 and down to 3 right now in maintenance). I wish modules were 1st-class citizens in Struts - hopefully WebWork/Struts Ti/Clarity will support multiple config files better? It's a pain when one file becomes a bottleneck or source of bugs and confusion.
I'm not currently using Resin, though I wish I was. I've used it on and off for personal projects or tests where I need fast iteration to test stuff out, but I still haven't been able to talk anyone into paying for a license at work (we use Tomcat (optional for development) and WebLogic (Dev, QA, Prod) as a company standard). If there were more mentions of Resin being a good choice in various blogs, articles, books, etc., I'd probably get more traction in my arguments for using Resin. If you're going to work with it then Will would be the better contact.
Posted by gerryg on November 30, 2005 at 04:32 PM MST #
In our case, we keep our applications always expanded, in CVS just the source files and in production just the compiled versions. We started using wars and ears at the beginning but we soon got tired of the web context re-starting simply because we wanted to modify a runtime configuration, a css style or some minor issue. So now we simply update the modified files and as our framework and logging configuration files can be changed in runtime, we just need to restart the context if we have a modified class or jar library. Restarting the context also was giving us headaches in the long run due to classloaders not freeing all resources and then you end up sooner or later with OutOfMemoryErrors.
So no more ears/wars for us and we are quite happy about it.
And regarding Resin, we've been using it for ages, since version 1.0.something, and it's always worked great for us. The issues we came across were promptly solved and we have now around 10 instances running with about 20-30 small-medium applications, as we have more variety than "weight". But yes, Resin's main "problem" has been always lack of publicity, because it's great otherwise. Hey, I even came across the Caucho guys at JavaOne and they gave me two caps, so what can I say! :)
My 2 eurocents
Posted by D.Lopez on December 01, 2005 at 10:53 PM MST #