Matt RaibleMatt Raible is a Java Champion and Developer Advocate at Okta. developer.okta.com

The JHipster Mini-Book The JHipster Mini-Book is a guide to getting started with hip technologies today: Angular, Bootstrap, and Spring Boot. All of these frameworks are wrapped up in an easy-to-use project called JHipster.

This book shows you how to build an app with JHipster, and guides you through the plethora of tools, techniques and options you can use. Furthermore, it explains the UI and API building blocks so you understand the underpinnings of your great application.

For book updates, follow @jhipster-book on Twitter.

10+ YEARS


Over 10 years ago, I wrote my first blog post. Since then, I've authored books, had kids, traveled the world, found Trish and blogged about it all.

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.

Eclipse Error

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.

Eclipse Error

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 in Java at Nov 28 2005, 11:58:15 AM MST 8 Comments
Comments:

It's important to note that this (more specifically Eclipse's debugger) loads up a second copy of your class; it doesn't actually reload it. If you have instances of your class in the HTTP session or in a cache, they will still reference the old copy of the class. Also, frameworks like Hibernate and Spring (and even Dynaop :() will hold on to and continue to instantiate the old class, so you need to explicitly reload them as well. We need real class reloading for development. If you're interested, please vote for the bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4910812 Finally, WebWork supports automatically reloading its configuration. You can set a property in the webwork.properties file to enable it.

Posted by Bob Lee on November 28, 2005 at 01:15 PM MST #

Wow, even that seems like a long involved process. About 4-5 years ago I was using Resin and just kept my code with the classes in an exploded .war directory format in the server's 'webapps' directory and did editing in-place, then used a simple script to build the .war sans source files when ready to deploy elsewhere. Edit JSP or class, run page in browser, just works. Can't get simpler than that. Seems like tool dependencies/requirements and fancy directory structures and other stuff just make things more complicated and may not buy any or much value for the trouble.

Posted by gerryg on November 28, 2005 at 04:51 PM MST #

Gerry - most of the instructions in this post are related to Maven and Equinox. If I changed Equinox's directory structure, it'd probably be a lot easier - or if I hadn't integrated dependency downloading. ;-)

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 05:43 PM MST #

I work with Resin 3.x, and also use the exploded .war directory format as gerryg does. Works great. Plus I can configure Resin to watch any files for change, not just the JSPs and Java classes. I have it watch struts-config.xml, and my spring config files.

Posted by Will Jaynes on November 29, 2005 at 07:10 AM 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?

  src
  test
  web
    WEB-INF
      classes

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:

  web
    WEB-INF
      classes
        **/*.java

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 09:49 AM MST #

I really do store projects in CVS in an exploded war format. (Except, of course, for class files and jar files (I use Ivy)). Here's my structure
 /resin-webapps
   /myproject
     index.jsp
     /WEB-INF
       /classes
       /src
          spring.xml
          mesages.properties
       /test
       /jsp
       struts-config.xml
In Eclipse classes is the output folder. So an edit and save in Eclipse causes and updated class file that Resin detects and reloads the app. My script to build the war file excludes the src and test folders, and whatever else isn't appropriate. I never saw the need to use any of the Eclipse plugins for app servers. I just run Resin externally and debug remotely.

Posted by Will Jaynes on November 29, 2005 at 01:57 PM MST #

Yeah, I store all deployable files in source control in the exploded war directory structure (ie, webapps\myproject\*, webapps\myproject\WEB-INF\*, etc, similar to Will's post). Webapps are our bread-and-butter, but we try to keep things separated enough that if we need a GUI or console app that we can just JAR up model/logic/DAO subdirectories in WEB-INF\classes and drop them in the other app. I like having the source in the same place as the classes instead of a separate source tree. It's not difficult to create WAR files using a filter to leave out .java files. Separation of source/compile areas increases the number of folders to manage (at best it's just redundant, but I've seen worst cases where folders in src don't map 1-1 with dest, and configuring mappings correctly can be a headache), so we feel the simple structure we use reduces bugs and config time.

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 10:32 AM MST #

Hello there,
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 04:53 PM MST #

Post a Comment:
Comments are closed for this entry.