Sunday, June 6, 2010

Java Hot Deploy Alternative

I have been working with Apache Wicket for the past 9 months or so and I started to tire of the time I was losing. For lack of a better process, I had been making java or html edits and then running "mvn package" at which point I had to sit and wait for a hot deploy on Tomcat. Thats a good 3 or so minutes, and I was tired of losing those 3 minutes.

I discovered something those guys using wicket for some time have known, but I just didn't see the answer until today. With Jetty, you can run an instance with just a few lines of code. With the magic of the Eclipse java debugger, and those few lines of java code, you can start an instance of Jetty and see all of your .java changes in real time. This is NOT hot deploy. No. This is edit java, save java, Eclipse builds automatically, you see the changes.

For this to work, I had to make a few modifications to my Eclipse project. These may or may not be required, its just what worked for me.

* In Project > Properties > Java Build Path : Default output folder = target/MY_APPLICATION/WEB-INF/classes
* In Project > Properties > Java Build Path : Inclusion patterns, I added **/*.html (wicket specific)
* In Run -> Configurations -> Classpath : Added target/MY_APPLICATION/WEB-INF/classes to my classpath
* I use Maven, added pom dependencies:


<properties>
<jetty.version>6.1.4</jetty.version>
</properties>
<!--  JETTY DEPENDENCIES FOR TESTING  -->
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>${jetty.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${jetty.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-management</artifactId>
<version>${jetty.version}</version>
<scope>provided</scope>
</dependency>



That is it. If you don't use maven, then you will have to get the required jetty jars some other way.

Here is the source:
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.jetty.webapp.WebInfConfiguration;
import org.mortbay.jetty.webapp.WebXmlConfiguration;

/**
 * This is a very simple, but very nice way to test out a web app. Using
 * Create a runtime configuration. When I did this, I had to add
 * the maven output folder to the classpath:
 * 
 * target/MY_APPLICATION/WEB-INF/classes
 * 
 * Then, whe you have it running, you can hit enter to kill it.
 * The REAL MAGIC - run the config in DEBUG mode. Any changes to your 
 * java files will be picked up and you can test WITHOUT RESTARTS!!
 * 
 * Of note, in my project properties, I added all the HTML files to the source 
 * location, which has the effect of getting Eclipse to copy over the
 * the HTML files in src/main/java. It doesn't try to compile them, but it does
 * move them to the target/MY_APPLICATION/WEB-INF/classes dir where they 
 * belong.
 * 
 * Now, I don't think this will have any impact at all on Spring configs,
 * but since this starts up SOOOOO much faster than tomcat does, its a 
 * very big win. Even BIGGER - You can see your java changes WITHOUT a restart
 * 
 * @author Russell Simpkins
 */
public class Start {

 public static void main(String[] args) throws Exception {
  Server server = new Server();
  SocketConnector connector = new SocketConnector();
  
  // Set some timeout options to make debugging easier.
  connector.setMaxIdleTime(1000 * 60 * 60);
  connector.setSoLingerTime(-1);
  connector.setPort(8080);
  server.setConnectors(new Connector[] { connector });

  WebAppContext bb = new WebAppContext();
  bb.setServer(server);
  bb.setContextPath("/");
  bb.setWar("target/MY_APPLICATION");
  bb.setConfigurationClasses(new String[] { WebInfConfiguration.class.getName(), WebXmlConfiguration.class.getName() });
        bb.setParentLoaderPriority(true);

  // START JMX SERVER
  // MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
  // MBeanContainer mBeanContainer = new MBeanContainer(mBeanServer);
  // server.getContainer().addEventListener(mBeanContainer);
  // mBeanContainer.start();
  
  server.addHandler(bb);

  try {
   System.out.println(">>> STARTING EMBEDDED JETTY SERVER, PRESS [ENTER] TO STOP");
   server.start();
   System.in.read();
   System.out.println(">>> STOPPING EMBEDDED JETTY SERVER"); 
   server.stop();
   server.join();
  } catch (Exception e) {
   e.printStackTrace();
   System.exit(100);
  }
 }
}

Of Note I found this article helpful http://www.draconianoverlord.com/2009/01/10/war-less-dev-with-jetty.html when digging around.