Wednesday, August 15, 2012

How to make an executable WAR file

If you ever used Jenkin CI server, you have probably seen their super easy one-line instruction to get started: java -jar jenkins.war. Now that's one awesome way to get your users to try out your product!

In this article, I will show you how to make a WAR file self executable just like above. Before I do that, let me tell you a little web app that I made, and then I will show how I converted it into runnable war.

The Requirement: Taking Notes Efficiently

I tend take a lot of notes when I do my work. Instead of using the crapy Windows Notepad.exe, I would setup my cygwin .bash_profile like this:

function e() { /cygdrive/C/apps/notepad++.exe $(cygpath -w "$@") ; }
function n() { F="$HOME/notes/$(date "+%Y%m%d-%H%M%S").markdown"; touch $F; e $F; }

I happen to have Notepad++ installed, but you can use just about any text editor you like. With that setup, then anywhere in my terminal I can simply type n to have a GUI editor pop open, and I will have my note file ready to record notes. The file name would have a timestamp date, and I like to record them with simple Markdown syntax for easy viewing later. Since I write them in plain text, it's easy and fast to search using grep MYSTUFF ~/notes/*. Once I found it, I can quickly edit it by e ~/notes/20120814-000000.markdown.

This method of taking notes is fast and portable between my Linux and Windows systems (well, I have to choose a different editor between OS but not big deal). Now since I already recorded my notes in markdown, there is no way I can see the pretty result unless I use an online converter. So I thought it would be awesome if I can have a web app that let me record these notes, and yet give me a nice quick Markdown to html view.

Introducing the webnotepad.war

To improve my notes taking, I created little web app called webnotepad.war. It's really simply one page web app that record notes in Markdown syntax, preview it, and allow you to search it. It also let you edit old notes too. The whole thing is just a Servlet class, and it doesn't even use JSP.

However, after I have this little web app, it bothers me that I would always need an app server to run it. (I can run mvn tomcat7:run directly from my source project, but it would require me be online to satisfy initial Maven build.) So here is a perfectly example use case that would be super nice to make this war file self executable. In fact I have succeeded in this little experiement. You can download it and try it out yourself with java -jar webnotepad.war, and then browse http://localhost:8080. If you get port conflict, try --httpPort=8081.

Of course you continue to deploy webnotepad.war into any app server too.

Making war file executable

Before you want to make war executable, you want to build and package your normal web application first. The trick to make the war file runnable is that you want additionally add a Main-Class in the META-INF/MANIFEST.MF in the war file. And from this we need to load and run an embedded Servlet server with the self war file deployed. I noticed Jenkins uses one called Winstone container server. I was amazed to find that this server only has 300K in size! It support full Servlet 2.5 spec! It can optionally support JSP with Jasper which will cost you up to 3MB in size. That's still a very small price to pay compare to any Servlet container out there!

Making a Main-Class WinstoneMain.java is not hard. However making Maven to package everything is pretty tricky. Now my webnotepad.war doesn't need Jasper, but if you do, you specially want these jars outside of WEB-INF/lib because you don't want your normal app server to load these! Else you will get very odd errors.

In my example I added into WEB-INF/lib-winstone, and then the WinstoneMain would extract these and then load the winstone.Launcher using a custom class loader. Also, we need to use some Winstone options to start a web server properly. I use this webnotepad/pom.xml to package it all up the war file. You can easily use my example because I have create them under a separate profile and you just need add to your web module/pom.xml. In my project demo you would run mvn package -Pdist to generate the executable war file.

NOTE: I noticed Winstone project didn't show much activity since last release since 2008. But I was happy to find that there is a fork project on GoogleCode Winstone that shows activities. I didn't use this for my demo, but it looks promising.

4 comments:

  1. Nice tutorial. I was aware of how to create executable JAR in java but creating executable WAR file is something new to me.

    ReplyDelete
  2. This is fab. I have been struggling with this for a while. You know how these things are. Irritating bits but there is always something more urgent to attend to. I have not had a chance to try it out myself yet, but this is exactly what I have been trying to do for sometime. I think when I get around to doing it I will also try out with Jetty, in addition to Winstone.

    ReplyDelete
  3. Awesome! Just got my standalone war file running. Thank you so much for fighting through the maven pom config - much appreciated.

    To get Jasper there was one missing jar I needed to put in the lib-winstone above and beyond the ones that are spec'd on the winstone page, namely: jasper-compiler-jdt.jar

    I also needed to add commons-logging dependency set to the provided scope or it created conflicts with the one in lib-winstone.

    ReplyDelete
    Replies
    1. Thank you for the notes Will.

      I have recently made the TimeMachine project war module self executable as well, and it uses Jasper. You can see how I manage the pom.xml to compare notes. It's not released yet, but there is a snapshot you can try it out like this:

      java -jar timemachine-web-1.3.0-SNAPSHOT.war

      Get the war here: https://bitbucket.org/timemachine/scheduler/downloads

      Delete