Running Java Web Applications on Heroku Cedar Stack

Update: Heroku now does support Java.

Heroku does not officially support Java applications yet (yes, it does). However, the most recently launched stack comes with support for Clojure. Well, if Heroku does run Clojure code, it is certainly running a JVM. Then why can we not deploy regular Java code on top of it?

I was playing with it today and found a way to do that. I admit, so far it is just a hack, but it is working fine. The best (?) part is that it should allow any maven-based Java Web Application to be deployed fairly easy. So, if your project can be built with maven, i.e. mvn package generates a WAR package for you, then you are ready to run on the fantastic Heroku Platform as a Service.

Wait. There is more. In these polyglot times, we all know that being able to run Java code means that we are able to run basically anything on top of the JVM. I’ve got a simple Scala (Lift) Web Application running on Heroku, for example.

There are also examples of simple spring-roo and VRaptor web applications. I had a lot of fun coding on that stuff, which finally gave me an opportunity to put my hands on Clojure. There are even two new Leiningen plugins: lein-mvn and lein-herokujetty. :-)

VRaptor on Heroku

Let me show you what I did with an example. Here is a step-by-step to deploy a VRaptor application on Heroku Celadon Cedar:

  1. Go to Heroku and create an account if you still do not have.
  2. We are going to need some tools. First Heroku gems:
    $ gem install heroku
    $ gem install foreman
    
  3. Then Leiningen, the clojure project automation tool. On my Mac, I installed it with brew:
    $ brew update
    $ brew install leiningen
    
  4. The vraptor-scaffold gem, to help us bootstrap a new project:
    $ gem install vraptor
    

That is it for the preparation. We may now start dealing with the project.

  1. First, we need to create the project skeleton:
    $ vraptor new <project-name> -b mvn
    $ lein new <project-name>
    $ cd <project-name>
    

    The lein command is not strictly necessary, but it helps with .gitignore and other minor stuff.

  2. Now, the secret sauce. This is the ugly code that actually tries to be smart and tricks Heroku to do additional build/compilation steps:
    $ wget https://gist.github.com/raw/1129069/f7ffcda9c42a3004fa8d3c496d4f13887c11ebf4/heroku_autobuild.clj -P src
    

    Or if you do not have wget installed:

    $ curl -L https://gist.github.com/raw/1129069/f7ffcda9c42a3004fa8d3c496d4f13887c11ebf4/heroku_autobuild.clj > src/heroku_autobuild.clj
    
  3. You also need to tweak the Leiningen project definition – project.clj. The template is here. Please remember to adjust your project name. It must be the same that you are using in the pom.xml. Or you may download it directly if you prefer:
    $ curl -L https://gist.github.com/raw/1129081/5790e173307d28a8df256e0aaa5a9fe7757e922f/project.clj > project.clj
    
  4. Unfortunately, Leiningen comes bundled with an old version of the Maven/Ant integration. The embedded maven is currently at version 2.0.8, which is old. The versions of some plugins configured in the default pom.xml from vraptor-scaffold are incompatible with this old version of maven.

    The best way to solve it for now is to remove all <version> tags from items inside the <plugins> section of your pom.xml. This is specific to VRaptor and other frameworks may need different modifications. See below for spring-roo and Lift instructions.

    If even after removing versions from all plugins, you still get ClassNotFoundException: org.apache.maven.toolchain.ToolchainManager errors, try cleaning your local maven repository (the default location is $HOME/.m2/repository). The pom.xml that I used is here.

  5. Now, try the same command that Heroku uses during its slug compilation phase. It should download all dependencies and package your application in a WAR file inside the target directory.
    $ lein deps
    

    Confirm that the WAR was built into target/. It must have the same name as defined in your project.clj.

  6. Create your Heroku/Foreman Procfile containing the web process definition:
    web: lein herokujetty
    

    And test with:

    $ foreman start
    

    A Jetty server should start and listen on a random port defined by foreman. Heroku does the same.

  7. Time to push your application to Heroku. Start editing your .gitignore: add target/ and tmp/, and please remove pom.xml. Important: The pom.xml must not be ignored.
  8. You may also remove some unused files (created by leiningen):
    $ rm -rf src/<project-name>
    # do not remove src/main src/test and src/heroku_autobuild.clj!
    
  9. Create a git repository:
    $ git init
    $ git add .
    $ git commit -m "initial import"
    
  10. Push everything to Heroku and (hopefully) watch it get built there!
    $ heroku create --stack cedar
    $ git push -u heroku master
    
  11. When it ends, open another terminal on the project directory, to follow logs:
    $ heroku logs -t
    
  12. Open the URL that heroku creates and test the application. You may also spawn more instances and use everything Heroku has to offer:
    $ heroku ps:scale web=2
    $ heroku ps:scale web=4
    $ heroku ps:scale web=0
    
  13. Quick tip: you do not have SSH access directly to Dynos, but this little trick is very useful to troubleshoot issues and to see how slugs were compiled:
    $ heroku run bash
    

spring-roo on Heroku

Once you understand the process for a VRaptor application, it should be easy for others as well. Just follow the simple step-by-step here, to create a simple spring-roo maven application.

Then, before running lein deps or foreman start, you must adjust your pom.xml, to remove plugins incompatible with the older mvn that is bundled in Leiningen. Here is the pom.xml that I am using (with some of the plugins downgraded).

You can see my spring-roo application running on Heroku here.

Scala/Lift on Heroku

The same for Scala/Lift: follow the instructions to bootstrap an application with maven. One simple change to the pom.xml is required. My version is here.

You also need to turn off the AUTO_SERVER feature of H2 DB. It makes H2 automatically starts a server process, which binds on a tcp port. Heroku only allows one port bound per process/dyno, and for the web process, it must be the jetty port.

To turn it off, change the database URL inside src/main/scala/bootstrap/liftweb/Boot.scala. I changed mine to a in-memory database. The URL must be something similar to jdbc:h2:mem:<project>.db.

My Lift application is running on Heroku here.

I hope it helps. Official support for Java must be in Heroku’s plans, but even without it yet, we’ve got a lot of possibilities.

VRaptor2 on Google App Engine

With all the recent excitement about Google supporting Java for the App Engine platform, I’m proud to say that VRaptor2 just works, out-of-the-box!

All you have to do is copy the required jars that come in the vraptor distribution, configure the VRaptorServlet (or VRaptorFilter) in web.xml and enable session support in the appengine-web.xml:

<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
	<application>...</application>
	<version>1</version>
	<sessions-enabled>true</sessions-enabled>

	<!-- ... -->
</appengine-web-app>

Being able to use my favorite Java Web MVC framework, to develop and deploy applications to Google’s cloud infrastructure, is really nice.

jetty-rails 0.4 is also jetty-merb

I’ve just released a new version of the jetty-rails gem. Now, you can also run Merb applications inside JRuby and Jetty!

jruby -S gem install jetty-rails
cd mymerbapp
jruby -S jetty_merb

Unfortunately, it’s blocking my console (ctrl + c doesn’t terminate it). Has anyone suggestions on this?

I’ve also updated the basic documentation, as you can see here.

The Merb support was actually done in JRuby Rack. It was quite simple to support it. Many thanks to Nick Sieger, Dudley Flanders e cia!

jetty-rails gem – Simple JRuby On Rails Development with Servlet Containers

This is the first time I’m writing about it, but jetty-rails is already 0.3!

Most people doing JRuby on Rails development are using JMongrels1 for development and some real Java Application Server in production.

The common flow is:

$ jruby script/server 
=> Booting Mongrel (use 'script/server webrick' to force WEBrick)
=> Rails application starting on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
** Starting Mongrel listening at 0.0.0.0:3000
** Starting Rails with development environment...

code, code, test, code, code, test, … (shouldn’t it be red-green-refactor?)

$ jruby -S warble
$ cp myapp.war $TOMCAT_HOME/webapps
$ $TOMCAT_HOME/bin/startup.sh

Sure you can automate those things with ant, rake, sake or anything else. Some people are still using the goldspike-plugin, but be warned: I suspect it won’t get much more attention.

The great Warbler from Nick Sieger is becoming the de facto standard for JRuby on Rails packaging. The Warbler’s recent move from goldspike to JRuby-Rack adapter reveals two interesting points:

  1. Goldspike is likely going to be deprecated (or merged with jruby-rack adapter?).
  2. Warbler will soon package any rack compatible application to be runned inside Java Containers. Such applications include Merb, Sinatra, Vintage, Camping ones, and growing

Although Warbler works really well, it introduces complexity in the development cycle. You can no more save code and immediately test it in your browser:

  1. change code;
  2. warble it;
  3. deploy war file;
  4. restart server; (takes long time)
  5. open browser;
  6. change code;

It breaks one of the most important rails development characteristics: instant feedback. During development, it’s very important to see changes without have to wait for server/context restarting.

JMongrel and Glassfish Gem are good candidates for JRuby on Rails development with instant feedback, but you can’t use Java (Servlet specification) specific features, such as web.xml; they aren’t complete Servlet Containers. Some things have an alternative in pure-rails as Servlet Filters and Servlet Listeners, but many haven’t. Servlet Context might be a good way to share things between rails runtimes. I know railers should “share nothing”, but -hey- sometimes it’s so good to share!

You can take the Servlet Application Context as your in-memory cache store (fragment and page caching), eliminating the need for filesystem or database overhead and even memcached, in many cases.

I had also a specific reason to share the same HttpSession between Rails and “pure Java” applications. Single sign-on wasn’t an option, so I needed to run both applications in the same context. I’m going to tell more about it soon.

Anyone can fall in cases, like mine, when you just can’t use jmongrel or glassfish_rails. Now, we fortunately have jetty-rails to rescue!

It’s a (one more) gem to run rails applications, based on the nice JRuby-Rack adapter, which I recommend you to take a look. Jetty is a very powerful Servlet Container, known for being pioneer at being embedded and at using NIO Connectors.

The gem creates a Jetty Server with two Handlers. The first is for static content and the last to serve dynamic requests using JRuby-Rack. These handlers are applied in order and request processing stops when one responds. That way, no rails code is runned to serve static content, improving response times. Take a look at the rdocs for more details.

Jetty is also very quick to start. I’ve measured (in a complete inaccurate way) some start times just for ugly2 comparison:

$ jruby -v
ruby 1.8.6 (2008-03-28 rev 6360) [i386-jruby1.1]
$ time jruby script/server
=> Booting Mongrel (use 'script/server webrick' to force WEBrick)
=> Rails application starting on http://0.0.0.0:3000
...
** INT signal received.
Exiting

real	0m13.947s
user	0m11.327s
sys	0m0.892s

$ ruby -v
ruby 1.8.6 (2007-09-24 patchlevel 111) [universal-darwin9.0]
$ time ./script/server 
=> Booting Mongrel (use 'script/server webrick' to force WEBrick)
=> Rails application starting on http://0.0.0.0:3000
...
** INT signal received.
Exiting

real	0m6.273s
user	0m1.893s
sys	0m0.611s

With the same JRuby 1.1 (and without Charles recent speedup patch for jruby startup):

$ time jruby -S jetty_rails
2008-05-04 10:50:00.846::INFO:  Logging to STDERR via org.mortbay.log.StdErrLog
2008-05-04 10:50:01.013::INFO:  jetty-6.1.9
...
2008-05-04 10:50:02.987::INFO:  Started SelectChannelConnector@0.0.0.0:8080

real	0m7.035s
user	0m4.387s
sys	0m0.296s

As you can see, jetty_rails is very close to mongrel running in MRI, but please, don’t take those numbers so seriously.

Jetty Rails should be ready to run any rails application (tell me otherwise!) with no dependencies on extra jars. All gems used by the application must be installed in your JRuby. I’ve made some simple benchmarks with JMeter and only one thread, firing 500 consecutive requests to a simple rails blog application. All requests pointed to ‘/posts’ controller, and there was only one Post is the MySQL database.

The machine used to run all tests is a Intel Core2 Duo E4500 @ 2.20GHz, 2.0GB RAM, running Ubuntu 7.10, Ruby (MRI) 1.8.6 and JRuby 1.1.1. MRI tests were done using plain ruby activerecord-mysql-adapter and JRuby tests were done using activerecord-jdbcmysql-adapter.

Mongrel: 30.7 req/s
JMongrel: 19.1 req/s

Glassfish Gem: 17.5 req/s
dropping JVM warm time: 23.8 req/s

Jetty Rails: 18.2 req/s
dropping JVM warm time: 26.6 req/s

This is obviously a simple measure, just to feel how jetty-rails is going. But I’m very happy with the results. If we ignore the time that JVM takes to warm and JIT compile things, jetty-rails comes close to Mongrel! Impressive. I knew Jetty was always very fast, but I really didn’t expect those results.

There is much more to do. Things from the roadmap I wanted to see working soon include:

  • read warble.rb configuration files and register extra jars and public folders defined there;
  • use any custom web.xml defined in config/web.xml (or config/web.erb.xml), following Wabler conventions.
  • jetty_merb
  • jetty_camping

The source code lives in GitHub. Feel free to fork, contribute, send patches and suggestions!


  1. Can we stop calling everything that comes from Java with that damn J-at-start or with -let termination? ;)
  2. I’ve stopped the processes (ctrl+c) as soon as I saw they were ready to respond to requests.

Servlet Event Listeners: Did you know?

… you could automatically register any number of servlet event listeners (ServletContextListener, HttpSessionListener, ServletRequestListener, and more …) without any additional web.xml line?

The Java Server Pages 2.0 specification dictates that all .tld files inside META-INF directories, from jars in the web application classpath (WEB-INF/lib), must be read and parsed by the Servlet Container (JSP.7.1.9 – Event Listeners).

Fortunately, .tld files accept the <listener> element. What about creating reusable jars (web frameworks?) and do their setup inside such listeners, as Sun JSF RI (Mojarra) is doing:

<!-- jsf-impl.jar!/META-INF/jsf_core.tld -->
<taglib xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee

http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"

  version="2.1">

  <description>
    The core JavaServer Faces custom actions that are independent of
    any particular RenderKit.
  </description>
  <tlib-version>1.2</tlib-version>
  <short-name>f</short-name>
  <uri>http://java.sun.com/jsf/core</uri>

  ...

  <!--
    This ServletContextListener initializes the runtime environment
    of the JavaServer Faces Reference Implementation when a web
    application including it is initialized by the container.
  -->
  <listener>
    <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
  </listener>

  ...
</taglib>

That said, why am I still having to add <listener> from web frameworks in my web.xml?