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:
- Go to Heroku and create an account if you still do not have.
- We are going to need some tools. First Heroku gems:
$ gem install heroku $ gem install foreman
- Then Leiningen, the clojure project automation tool. On my Mac, I installed it with
brew
:$ brew update $ brew install leiningen
- 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.
- 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. - 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
- 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 thepom.xml
. Or you may download it directly if you prefer:$ curl -L https://gist.github.com/raw/1129081/5790e173307d28a8df256e0aaa5a9fe7757e922f/project.clj > project.clj
- 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
). Thepom.xml
that I used is here. - 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 yourproject.clj
. - 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. - Time to push your application to Heroku. Start editing your
.gitignore
: addtarget/
andtmp/
, and please removepom.xml
. Important: Thepom.xml
must not be ignored. - 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!
- Create a git repository:
$ git init $ git add . $ git commit -m "initial import"
- Push everything to Heroku and (hopefully) watch it get built there!
$ heroku create --stack cedar $ git push -u heroku master
- When it ends, open another terminal on the project directory, to follow logs:
$ heroku logs -t
- 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
- 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.