Java in the Cloud: EEruption of the Heroku

Today all major vendors have their own Cloud platform and most of them are betting on it for the future. Even if the cloud is not yet the everyday solution, it brings new challenges for servers. This post will deal with one example to show how Apache TomEE meets the challenge with success.

What are the challenges of the Cloud?

All Cloud platforms are a bit different as each have different default targets (i.e., Spring, Java EE, your own platform, only runtime, only build, or even build + runtime, etc.), however, they all share some common points a modern framework/server needs to tackle:

Injection of Resource configuration through the environment: For Java it would be very simple to get to the system property. But these Cloud platforms generally support more than Java (e.g., JavaScript, Ruby, Perl, etc.), so they use the same common point of all languages, i.e., the environment.

Packaging: Here the differences become huge! Some platforms accept standard binary (as for TomEE, a WAR file), some needs an executable JAR file, while others directly accept source code. This is surely the most vendor specific part of the cloud solution. What is important to keep in mind is that either your platform supports your target (for example, if you want to deploy on TomEE; it provides you with an instance of TomEE and you just need to copy your WAR file) or you need to do it yourself. In the latter case you have two main subcases: either you can start your container by configuration and deploy your application in it, or you need to provide a Java command for the platform to launch. This can be an executable JAR file or just a main (String[]); depending on the solution you choose.

Configuration: In the case of Java you need a few, but important configurations, to ensure you are running as expected (e.g., JVM, binaries, command to launch, etc.). The most common configurations are the JVM version and the launch command (assuming you are not relying on a provided container). Once again, how to configure it at 100% depends on your Cloud platform. Some providers need a marker file or read the property value in a particular file, while others just have it configured somewhere in their GUI. However, this point is a bit different from the previous one even if your configuration is not portable. It doesn’t affect your code or build; at worst you add a file in your project.

Heroku

Create your account

If you already have a Heroku account you can skip this part. If not, here are few screenshots showing you how fast and easy it is to register for a free Heroku account.
Once done, you can go on Heroku and download the Heroku client. There is a Maven plugin available, but we don’t need it for this post; plus it is easier to create an application with the provided client.

Customize the JAX-RS starter project to run on Heroku

Heroku supports several types of packaging, but I’ll choose a simple one to use. Here, “simple” means easy to debug, easy to test, and easy to deploy and manage.

Today the challenge for an application server is to let you package them like a standard Java application, such as “main(String[])”. This is the trend as you can see with Spring Boot, Payara Micro, Wildfly Swarm and others, but it has been a core feature of TomEE for several years now.

As a quick reminder with TomEE, you can:

Create a shade to get an executable JAR or WAR file: `java -jar my-awesome-app.jar`

Run a WAR file from the command line: `java -jar tomee-embedded-uber.jar –path my-awesome-app.war`

Create an executable WAR file but executed in a real TomEE instance (i.e., a fork to ensure you don’t get classloading surprises as opposed to an embedded instance): `java -jar my-awesome-app-runner.war`

Reuse Tomcat Maven Plugin to create an executable war “Ã la Tomcat”

We decided to use the main method to run our application as any Java program.

Or just reuse the existing one from TomEE: org.apache.tomee.embedded.Main

We’ll go for this last solution and just wire $PORT environment variables to the port option of the main method. It means that we’ll need to launch this command to run our application:

# --as-war means deployClasspathAsWebApp() since we deploy a classpath "as" a war
# but this main also support executable wars
$ java -cp "the classpath" org.apache.tomee.embedded.Main --port=$PORT --as-war

If you run it manually (you can hardcode the port if it is just for a local test) you’ll get these logs:

Now, we know how to run our application in embedded mode. Let’s configure it for Heroku.

Let Heroku know how to handle your application

First, and to avoid surprises, we’ll force the JVM to version 1.8 on the Heroku platform for our application. This step at the moment is optional since it is the default case, but it will help avoid surprises should the version change.

To do so, just add a system.properties file in the root of your project containing:

java.runtime.version=1.8

To launch our application, we need to specify the command to run. To do it, just create a Procfile file in the root of the project with:

Time to deploy!

This will trigger a build on Heroku. The first build can be long since it downloads all dependencies, but it is fast once completed and will run Procfile of the application. (Download sections has been removed to keep the logs readable):

All required information is here, but it looks more like a PHP URL than a JDBC one.

Heroku GUI allows you to edit this variable and add some others. Thus, it would be easy to use TomEE resource placeholders to wire this configuration to a TomEE datasource. Suppose you define JDBC_URL, JDBC_User, JDBC_PASSWORD then you could define the resource through system properties on your command line (Procfile) like this:

However changing default environment variables is not that nice because it makes your application “custom” and it is harder to share the knowledge about its monitoring and configuration setup.

To avoid that, the alternative would be to make TomEE understand this URL. Since we are embedded we could hack it just before running TomEE and set it as system properties or container properties directly in our main method. This would work, but I’d like to share another solution.

TomEE supports properties-provider attribute on resources since last year. This property takes a fully qualified name of a class providing properties (configuration) for the resource on which it is defined. In our case it is easy to implement a HerokuPropertiesProvider to read the DATABASE_URL and convert it into properties JdbcUrl, UserName, Password and JdbcDriver.

I will not detail the implementation here (parsing the DATABASE_URL and recreating a JDBC URL from it) since it is now built-in in TomEE: org.apache.openejb.resource.heroku.HerokuDatabasePropertiesProvider.

What does it mean for us? To get our Heroku database wired to myDb in the application, we simply need to define:

This indicates everything is ready to use myDb as JTA datasource in a persistence.xml. 😉

Conclusion

There are many alternatives to deploy on a Cloud platform that allow you to provide the launch and stop command for your application. It’s nice to see that TomEE is able to integrate smoothly with whatever solution you choose for your deployment.

Some things that you need to take into account when using it for real applications, especially Heroku:

Using Git and several remotes is nice for a developer, but do you want to share the same repository between production teams and developers?

It’s quite easy to push binaries (e.g., a shade myapp.jar) and replace the command in Procfile to execute it, but do you want to build on your production nodes?

It’s nice to have the ability to easily rollback whatever solution you choose thanks to Git. Plus, the learning curve is quite small because it’s a well-known technology. Do you need more reasons?

Finally the TomEE JAX-RS starter project now has a heroku branch ready to deploy on Heroku if you don’t want to do all these configurations yourself!