Heroku for Java

We're pleased to announce the public beta of Heroku for Java. Java is the fourth official language available on the Cedar stack.

Java is, by many measures, the world's most popular programming language. In addition to its large and diverse developer base, it offers a huge ecosystem of libraries and tools, an extremely well-tuned VM for fast and reliable runtime performance, and an accessible C-like syntax.

But there are also many criticisms commonly leveled against the language. We'll take a closer look at Java's strengths and weaknesses in a moment, but first:

Heroku for Java in 2 minutes

Create a project with three files:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <version>1.0-SNAPSHOT</version>
    <artifactId>helloworld</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-servlet</artifactId>
            <version>7.6.0.v20120127</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.4</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals><goal>copy-dependencies</goal></goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

src/main/java/HelloWorld.java

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.*;

public class HelloWorld extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.getWriter().print("Hello from Java!\n");
    }

    public static void main(String[] args) throws Exception{
        Server server = new Server(Integer.valueOf(System.getenv("PORT")));
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        server.setHandler(context);
        context.addServlet(new ServletHolder(new HelloWorld()),"/*");
        server.start();
        server.join();   
    }
}

Procfile

web:    java -cp target/classes:target/dependency/* HelloWorld

Commit these files to Git:

$ git init
$ git add .
$ git commit -m init

Create an app on the Cedar stack and deploy. Your Java program and all its dependencies will be built at slug compile time:

$ heroku create --stack cedar
Creating hollow-dawn-737... done, stack is cedar
http://hollow-dawn-737.herokuapp.com/ | git@heroku.com:hollow-dawn-737.git
Git remote heroku added

$ git push heroku master
Counting objects: 9, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (9/9), 1.36 KiB, done.
Total 9 (delta 0), reused 0 (delta 0)

-----> Heroku receiving push
-----> Java app detected
-----> Installing Maven 3.0.3..... done
-----> Installing settings.xml..... done
-----> executing .maven/bin/mvn -B -Duser.home=/tmp/build_yiuhjlk5iqs4 -s .m2/settings.xml -DskipTests=true clean install
       [INFO] Scanning for projects...
       [INFO]                                                                         
       [INFO] ------------------------------------------------------------------------
       [INFO] Building helloworld 1.0-SNAPSHOT
       [INFO] ------------------------------------------------------------------------
       ...
       [INFO] ------------------------------------------------------------------------
       [INFO] BUILD SUCCESS
       [INFO] ------------------------------------------------------------------------
       [INFO] Total time: 5.377s
       [INFO] Finished at: Mon Aug 22 16:35:58 UTC 2011
       [INFO] Final Memory: 12M/290M
       [INFO] ------------------------------------------------------------------------
-----> Discovering process types
       Procfile declares types -> web
-----> Compiled slug size is 13.6MB
-----> Launching... done, v3
       http://hollow-dawn-737.herokuapp.com deployed to Heroku

Then view your app on the web:

$ curl http://hollow-dawn-737.herokuapp.com
Hello from Java!

For more detail see:

Why Java?

Java is a solid language for building web apps:

  • The JVM is one of the best runtime VMs in the world, offering fast performance and a reliable memory footprint over time.
  • Java boasts an estimated population of six million developers, with a vast ecosystem of tools, libraries, frameworks and literature. It is the most mature and established programming language for building server-side applications in existence today.
  • Born at the beginning of the Internet age, Java began with the goal of "write once, run anywhere." Though it took a long time to get there, this goal has been largely achieved. The universal JVM runtime environment is available on an incredibly wide range of platforms and offers near-perfect portability between those platforms with no changes in application code, and even build artifacts are binary-compatible.

Despite these strengths, Java faces criticism from many sides. Partially, this is an inescapable effect of popularity. But many of these criticisms are valid, and reflect the downside of being a mature community: substantial legacy baggage.

To understand this better, we need to tease apart Java (the programming language) from J2EE (the "enterprise" application container interface).

How J2EE Derailed Java

Java took off as a server-side programming language with the emergence of the JDBC and Servlet APIs in the late 1990s. Since then a vast number of web applications have been built using these basic APIs combined with other technologies like JSP, JSF, Struts, Spring and more. The emergence of J2EE and J2EE application servers boosted Java's presence in the enterprise and created a lucrative software segment of J2EE middleware vendors. But it also added complexity to applications and deployment processes.

J2EE was built for a world of application distribution — that is, software packaged to be run by others, such as licensed software. But it was put to use in a world of application development and deployment — that is, software-as-a-service. This created a perpetual impedance mismatch between technology and use case. Java applications in the modern era suffer greatly under the burden of this mismatch.

As one illustration, consider the J2EE Development Roles document. It suggests an assembly-line model for development and deployment of apps, with the code passing along a chain of eight different people. This was a fairly complex and bureaucratic model that didn't match how software was developed a decade ago, let alone today.

In Stop Wasting Money On WebLogic, WebSphere, And JBoss Application Servers, Forrester analyst Mike Gualtieri writes:

Traditional application servers or containers such as Tomcat will fast become legacy methods for deploying Java applications. The next generation is elastic application platforms (EAP) that are containerless.

In recent years, J2EE vendors have attempted to fix the problems (including a re-branding from J2EE to JEE). Unfortunately, it was too little too late. The Java space is now ripe for disruptive innovation by cloud application platforms.

Heroku for Java

If you've worked with Java before, the content of the hello-world sample app shown above may have surprised you. There is no "application container" in the J2EE sense; the app uses Jetty as an embedded webserver, just as one might use Unicorn for Ruby or Tornado for Python, or Jetty itself for Clojure.

The capabilities promised by J2EE application containers for managing your app include deployment, restart, logging, service binding (config), and clustering (horizontal scaling). Running your Java app on Heroku, you achieve these ends via the platform instead.

But unlike J2EE, Heroku is a polyglot platform. Techniques for deployment, logging, and scaling are applicable to all app deployments, regardless of language. A common deployment infrastructure reduces language choice to just a question of syntax and libraries. Reduced coupling between app and infrastructure enables picking the right language for each job.

A New Era for Software Delivery

Using Heroku's platform to run Java apps finally solves the impedance mismatch between application containers designed for traditional software distribution, and the modern world of software-as-a-service.

In the classic software delivery process (development → packaging → distribution → install → deployment), code passes through many hands before it finally reaches the end user. Developers build, QA verifies, ops deploys, and finally end users can access. In this environment, the feedback loop for information about how code behaves in production is slow and inefficient — it may take weeks or months for this to make it back to developers, and often in a highly-filtered format.

Heroku is built for the new era of software-as-a-service. An app is built by a small, cross-functional, relatively independent team which builds and deploys everything itself, with few or no hand-offs to other teams. There is no packaging, distribution, or install element because the code never leaves the team/organization. This keeps developers who build the software in close touch with how it behaves in production. And it enables continuous delivery, for a tight feedback loop between customer needs and resulting software built for those needs.

Java teams are often still stuck with the classic process because it's built into the toolchain. Heroku for Java is optimized for compact applications that require robust, yet agile deployment and rapid iterations. You can deploy any Java application to Heroku, including J2EE applications, but you aren't constrained by the J2EE deployment process.

Other JVM Languages

This announcement is official support for Java the language, but developers familiar with the JVM have already seen that it's possible to deploy any other JVM-based language, by bootstrapping with pom.xml. The JVM is becoming popular as the runtime VM for both new and existing languages, so Java support on Heroku makes it much easier to bootstrap into running any JVM language on our platform.

For example, JRuby is one of the most frequently-requested languages on Heroku. Matthew Rodley has already put a Rails app onto JRuby on Heroku by adding JRuby to pom.xml. Scala, another common request, could be done the same way. We do look forward to being able to offer the same kind of first-class support for JRuby and Scala that we offer for Clojure; but in the meantime, bootstrapping via Java is a reasonable strategy.

Learning From Each Other

With the rise of polyglot programming, cross-talk between language communities has become much more common. Heroku's polyglot platform further reinforces that trend.

Younger language communities have much they can learn from a mature community like Java. For example, Java began working on build automation and dependency management (via Ant, and later Maven) long before Ruby/Rails got Gem Bundler, Python got Pip, or Clojure got Leiningen. These tools owe much of their underlying theory to the learning (and battle scars) accumulated by Java build tools.

At the same time, Java has much it can learn from younger languages which are unencumbered by legacy baggage. Patterns, frameworks, and build systems in newer languages are already optimized for cloud application deployment with no left-over cruft from past eras. Java has already borrowed ideas in the framework space — see Play! or Grails for two examples. But sharing common deployment infrastructure between languages opens up the possibility for Java developers to get more exposure to deployment and scaling best practices from other communities.

The Future

Java is another milestone on the polyglot platform path, but there's more to come. Future language packs will span the gamut from venerable (like Java) to cutting-edge (like Clojure and Node.js) to squarely in-between (like Ruby). Our desire is to be as inclusive as possible. Choice of language is up to the developer.

Heroku is driven by a simple first principle: do what's best for developers. Supporting Java is what's best for the large world of Java developers; it's what's best for developers who want to use other JVM languages; and it's even good for users of other languages, who will benefit indirectly from the learning their community may gain from contact with Java. We're pleased to welcome Java developers to Heroku.

Get Going

Ready to get started building Java apps on Heroku? Start with these articles in the Dev Center:

Browse the blog archives or subscribe to the full-text feed.