/javaOnHeroku

Simple Java on Heroku

Primary LanguageJavaMIT LicenseMIT

java-getting-started

A barebones Java app, which can easily be deployed to Heroku.

This application support the Getting Started with Java on Heroku article - check it out.

Running Locally

Make sure you have Java and Maven installed. Also, install the Heroku Toolbelt.

$ git clone https://github.com/heroku/java-getting-started.git
$ cd java-getting-started
$ mvn install
$ foreman start web

Your app should now be running on localhost:5000.

Deploying to Heroku

$ heroku create
$ git push heroku master
$ heroku open

Documentation

For more information about using Java on Heroku, see these Dev Center articles:

Getting Started with Java on Heroku

Introduction

This tutorial will have you deploying a Java app in minutes.

Hang on for a few more minutes to learn how it all works, so you can make the most out of Heroku.

The tutorial assumes that you have:

  • a free Heroku account
  • Java installed
  • Maven 3 installed

Set up

In this step you will install the Heroku Toolbelt. This provides you access to the Heroku Command Line utility, as well as git and Foreman, tools you’ll use in later steps.

Download Heroku Toolbelt for...

Once installed, you can use the heroku command from your command shell.

Log in using the email address and password you used when creating your Heroku account:

heroku login
Enter your Heroku credentials.
Email: java@example.com
Password:

Authenticating is required to allow both the heroku and git commands to operate.

Prepare the app

In this step, you will prepare a simple application that can be deployed.

Execute the following commands to clone the sample application:

 git clone https://github.com/heroku/java-getting-started.git

 cd java-getting-started

You now have a functioning git repository that contains a simple application as well as a pom.xml file, which is used by Java’s dependency manager, Maven.

Deploy the app

In this step you will deploy the app to Heroku.

Create an app on Heroku, which prepares Heroku to receive your source code:

heroku create
Creating warm-eyrie-9006... done, stack is cedar-14
http://warm-eyrie-9006.herokuapp.com/ | https://git.heroku.com/warm-eyrie-9006.git
Git remote heroku added

When you create an app, a git remote (called heroku) is also created and associated with your local git repository.

Heroku generates a random name (in this case warm-eyrie-9006) for your app - you can pass a parameter to specify your own, or rename it later with heroku apps:rename.

Now deploy your code:

git push heroku master
Initializing repository, done.
Counting objects: 68, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (19/19), done.
Writing objects: 100% (68/68), 7.07 KiB | 0 bytes/s, done.
Total 68 (delta 22), reused 65 (delta 22)

-----> Java app detected
-----> Installing OpenJDK 1.7... done
-----> Installing Maven 3.0.3... done
-----> executing /app/tmp/cache/.maven/bin/mvn -B -Duser.home=/tmp/build_4244c199-7f9b-4f62-bf60-bbd0aff3f978 -Dmaven.repo.local=/app/tmp/cache/.m2/repository  -DskipTests=true clean install
       [INFO] Scanning for projects...
       [INFO]
       [INFO] ------------------------------------------------------------------------
       [INFO] Building helloworld 1.0-SNAPSHOT
       [INFO] ------------------------------------------------------------------------
       Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-dependency-plugin/2.4/maven-dependency-plugin-2.4.pom

...

-----> Discovering process types
       Procfile declares types -> web

-----> Compressing... done, 62.7MB
-----> Launching... done, v7
       http://warm-eyrie-9006.herokuapp.com/ deployed to Heroku

To git@heroku.com:warm-eyrie-9006.git
 * [new branch]      master -> master

The application is now deployed. Ensure that at least one instance of the app is running:

 heroku ps:scale web=1

Now visit the app at the URL generated by its app name. As a handy shortcut, you can open the website as follows:

 heroku open

View logs

Heroku treats logs as streams of time-ordered events aggregated from the output streams of all your app and Heroku components, providing a single channel for all of the events.

View information about your running app using one of the logging commands, heroku logs:

heroku logs --tail
2014-08-08T13:55:07.254053+00:00 heroku[web.1]: State changed from starting to up
2014-08-08T13:55:05.214125+00:00 heroku[web.1]: Starting process with command `java -Xmx384m -Xss512k -XX:+UseCompressedOops -cp target/classes:target/dependency/* Main`
2014-08-08T13:56:08.662643+00:00 heroku[router]: at=info method=GET path="/" host=warm-eyrie-9006.herokuapp.com request_id=f99b4889-1b16-4862-876d-4f752ad3d843 fwd="94.174.204.242" dyno=web.1 connect=1ms service=3ms status=200 bytes=579

Visit your application in the browser again, and you’ll see another log message generated. Press Control+C to stop streaming the logs.

Define a Procfile

Use a Procfile, a text file in the root directory of your application, to explicitly declare what command should be executed to start your app.

The Procfile in the example app you deployed looks like this:

web: java $JAVA_OPTS -cp target/classes:target/dependency/* Main

This declares a single process type, web, and the command needed to run it. The name web is important here. It declares that this process type will be attached to the HTTP routing stack of Heroku, and receive web traffic when deployed.

Procfiles can contain additional process types. For example, you might declare one for a background worker process that processes items off of a queue.

Scale the app

Right now, your app is running on a single web dyno. Think of a dyno as a lightweight container that runs the command specified in the Procfile.

You can check how many dynos are running using the ps command:

 heroku ps
=== web (1X): `java $JAVA_OPTS -cp target/classes:target/dependency/* Main`
web.1: up 2014/08/08 14:55:07 (~ 2m ago)

Having only a single web dyno running will result in the dyno going to sleep after one hour of inactivity. This causes a delay of a few seconds for the first request upon waking. Subsequent requests will perform normally.

To avoid this, you can scale to more than one web dyno. For example:

 heroku ps:scale web=2

For abuse prevention, scaling the application may require account verification. If your account has not been verified, you will be directed to visit the verification site.

For each application, Heroku provides 750 free dyno-hours. Running your app at 2 dynos would exceed this free, monthly allowance, so scale back:

 heroku ps:scale web=1

Introduction Set up Prepare the app Deploy the app View logs Define a Procfile Scale the app Declare app dependencies Run the app locally Push local changes Provision add-ons Start a one-off dyno Define config vars Use a database Next steps

Declare app dependencies

Heroku recognizes an app as Java by the existence of a pom.xml file in the root directory. For your own apps, you can create one by running mvn clean install.

The demo app you deployed already has a pom.xml (see it here). Here’s an excerpt:

<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>

The pom.xml file specifies dependencies that should be installed with your application. When an app is deployed, Heroku reads this file and installs the dependencies using the mvn clean install command.

Another file, system.properties, determines the version of Java to use. The contents of this file, which is optional, is quite straightforward:

java.runtime.version=1.7

Run mvn clean install in your local directory to install the dependencies, preparing your system for running the app locally:

mvn clean install
INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building helloworld 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
...
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/maven-archiver/2.5/maven-archiver-2.5.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/maven-archiver/2.5/maven-archiver-2.5.pom (5 KB at 14.9 KB/sec)
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.584s
[INFO] Finished at: Fri Aug 08 15:05:39 BST 2014
[INFO] Final Memory: 19M/222M
[INFO] ------------------------------------------------------------------------

Once dependencies are installed, you will be ready to run your app locally.

Run the app locally

Now start your application locally using Foreman, which was installed as part of the Toolbelt:

foreman start web
15:10:15 web.1  | started with pid 2071
15:10:15 web.1  | 2014-08-08 15:10:15.329:INFO:oejs.Server:jetty-7.6.0.v20120127
15:10:15 web.1  | 2014-08-08 15:10:15.378:INFO:oejsh.ContextHandler:started o.e.j.s.ServletContextHandler{/,null}
15:10:15 web.1  | 2014-08-08 15:10:15.411:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:5000

Just like Heroku, Foreman examines the Procfile to determine what to run.

Your app will now be running at http://localhost:5000. Test that it’s working with curl or a web browser, then Ctrl-C to exit.

Foreman doesn’t just run your app - it also sets “config vars”, something you’ll encounter in a later tutorial.

Introduction Set up Prepare the app Deploy the app View logs Define a Procfile Scale the app Declare app dependencies Run the app locally Push local changes Provision add-ons Start a one-off dyno Define config vars Use a database Next steps

Push local changes

In this step you’ll learn how to propagate a local change to the application through to Heroku. As an example, you’ll modify the application to add an additional dependency and the code to use it.

Modify pom.xml to include a dependency for jscience. The section should look something like this:

<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>
    <dependency>
      <groupId>postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>9.0-801.jdbc4</version>
    </dependency>
    <dependency>
      <groupId>org.jscience</groupId>
      <artifactId>jscience</artifactId>
      <version>4.3.1</version>
    </dependency>
  </dependencies>

Modify src/main/java/Main.java so that it imports this library at the start. Also modify the call to route that handles '/' to use it. Your final code should look like this:

import static javax.measure.unit.SI.KILOGRAM;
import javax.measure.quantity.Mass;
import org.jscience.physics.model.RelativisticModel;
import org.jscience.physics.amount.Amount;

And modify the showHome() method so that it reads like this:

private void showHome(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    // Energy is compatible with mass (E=mc2)
    RelativisticModel.select();
    Amount<Mass> m = Amount.valueOf("12 GeV").to(KILOGRAM);

    resp.getWriter().print("E=mc^2: 12 GeV = " + m);
}

Here’s the final source code for Main.java - yours should look similar.

Now test locally:

 mvn clean install

 foreman start web

Visiting your application at http://localhost:5000, you should see some great scientific conversions displayed:

E=mc^2: 12 GeV = (2.139194076302506E-26 ± 1.4E-42) kg

Now deploy. Almost every deploy to Heroku follows this same pattern. First, add the modified files to the local git repository:

 git add .

Now commit the changes to the repository:

 git commit -m "Demo"

Now deploy, just as you did previously:

 git push heroku master

Finally, check that everything is working:

 heroku open

Provision add-ons

Add-ons are third-party cloud services that provide out-of-the-box additional services for your application, from persistence through logging to monitoring and more.

By default, Heroku stores 1500 lines of logs from your application. However, it makes the full log stream available as a service - and several add-on providers have written logging services that provide things such as log persistence, search, and email and SMS alerts when certain conditions are met.

Provision the papertrail logging add-on:

 heroku addons:add papertrail

Adding papertrail on warm-eyrie-9006... done, v8 (free)
Welcome to Papertrail. Questions and ideas are welcome (support@papertrailapp.com). Happy logging!
Use `heroku addons:docs papertrail` to view documentation.

To help with abuse prevention, provisioning an add-on may require account verification. If your account has not been verified, you will be directed to visit the verification site.

The add-on is now deployed and configured for your application. You can list add-ons for your app like so:

 heroku addons

To see this particular add-on in action, visit your application’s Heroku URL a few times. Each visit will generate more log messages, which should now get routed to the papertrail add-on. Visit the papertrail console to see the log messages:

 heroku addons:open papertrail

A console will open up, showing the latest log events, and providing you with an interface to search and set up alerts:

Image

Start a one-off dyno

You can run a command, typically scripts and applications that are part of your app, in a one-off dyno using the heroku run command. It can also be used to launch a REPL process attached to your local terminal for experimenting in your app’s environment, or bespoke code that you deployed with your application:

 heroku run bash
Running `bash` attached to terminal... up, run.9640

~ $ java -version
Picked up JAVA_TOOL_OPTIONS:  -Djava.rmi.server.useCodebaseOnly=true -Djava.rmi.server.useCodebaseOnly=true
java version "1.7.0_45"
OpenJDK Runtime Environment (IcedTea 2.4.3) (Ubuntu build 1.7.0_45-b31)
OpenJDK 64-Bit Server VM (build 24.45-b08, mixed mode)

If you receive an error, Error connecting to process, then you may need to configure your firewall.

Don’t forget to type exit to exit the shell and terminate the dyno.

Introduction Set up Prepare the app Deploy the app View logs Define a Procfile Scale the app Declare app dependencies Run the app locally Push local changes Provision add-ons Start a one-off dyno Define config vars Use a database Next steps

Define config vars

Heroku lets you externalise configuration - storing data such as encryption keys or external resource addresses in config vars.

At runtime, config vars are exposed as environment variables to the application. For example, modify src/main/java/Main.java so that the method repeats grabs an energy value from the ENERGY environment variable:

  private void showHome(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {

    // Energy is compatible with mass (E=mc2)
    RelativisticModel.select();

    String energy = System.getenv().get("ENERGY");

    Amount<Mass> m = Amount.valueOf(energy).to(KILOGRAM);
    resp.getWriter().print("E=mc^2: " + energy + " = " + m);

  }

Now compile the app again so that this change is integrated by running mvn clean install.

Foreman will automatically set up the environment based on the contents of the .env file in your local directory. Create a .env file that has the following contents:

ENERGY=20 GeV

If you run the app with foreman start and visit it at http://localhost:5000, you’ll see the conversion value for 20 GeV.

To set the config var on Heroku, execute the following:

 heroku config:set ENERGY="20 GeV"
Setting config vars and restarting warm-eyrie-9006... done, v10
ENERGY: 20 GeV

View the config vars that are set using heroku config:

 heroku config
== warm-eyrie-9006 Config Vars
PAPERTRAIL_API_TOKEN: erdKhPeeeehIcdfY7ne
ENERGY: 20 GeV
...

Deploy your changed application to Heroku to see this in action.

Use a database

The add-on marketplace has a large number of data stores, from Redis and MongoDB providers, to Postgres and MySQL. In this step you will learn about the free Heroku Postgres add-on that is provisioned automatically on all Java app deploys.

A database is an add-on, and so you can find out a little more about the database provisioned for your app using the addons command in the CLI:

 heroku addons
=== warm-eyrie-9006 Configured Add-ons
heroku-postgresql:hobby-dev  HEROKU_POSTGRESQL_BLUE
papertrail:choklad
...

Listing the config vars for your app will display the URL that your app is using to connect to the database, DATABASE_URL:

 heroku config
=== warm-eyrie-9006 Config Vars
DATABASE_URL:               postgres://qplhasewkhqyxp:YXDPSRus9MrU4HglCPzjhOevee@ec2-54-204-47-58.compute-1.amazonaws.com:5432/dc9qsdnghia6v1
...

Heroku also provides a pg command that shows a lot more:

 heroku pg
== HEROKU_POSTGRESQL_BLUE_URL (DATABASE_URL)
Plan:        Hobby-dev
Status:      Available
Connections: 0
PG Version:  9.3.3
Created:     2014-08-08 13:54 UTC
Data Size:   6.5 MB
Tables:      0
Rows:        0/10000 (In compliance)
Fork/Follow: Unsupported
Rollback:    Unsupported

This indicates I have a hobby database (free), running Postgres 9.3.3, with a single row of data.

The example app you deployed already has database functionality, which you should be able to reach by visiting your app’s URL and appending /db. For example, if your app was deployed to https://wonderful-app-287.herokuapp.com/ then visit https://wonderful-app-287.herokuapp.com/db.

The code to access the database is straightforward. Here’s the getConnection() method that retrieves the environment variable DATABASE_URL set by the database add-on, and establishes a connection:

  private Connection getConnection() throws URISyntaxException, SQLException {
    URI dbUri = new URI(System.getenv("DATABASE_URL"));

    String username = dbUri.getUserInfo().split(":")[0];
    String password = dbUri.getUserInfo().split(":")[1];
    int port = dbUri.getPort();

    String dbUrl = "jdbc:postgresql://" + dbUri.getHost() + ":" + port + dbUri.getPath();

    return DriverManager.getConnection(dbUrl, username, password);
  }

And here’s a method to insert values into a table called tick:

  private void showDatabase(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    Connection connection = null;
    try {
      connection = getConnection();

      Statement stmt = connection.createStatement();
      stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ticks (tick timestamp)");
      stmt.executeUpdate("INSERT INTO ticks VALUES (now())");
      ResultSet rs = stmt.executeQuery("SELECT tick FROM ticks");

      String out = "Hello!\n";
      while (rs.next()) {
          out += "Read from DB: " + rs.getTimestamp("tick") + "\n";
      }

      resp.getWriter().print(out);
    } catch (Exception e) {
      resp.getWriter().print("There was an error: " + e.getMessage());
    } finally {
      if (connection != null) try{connection.close();} catch(SQLException e){}
    }
  }

This ensures that when you access your app using the /db route, a new row will be added to the tick table, and all the rows will then be returned so that they can be rendered in the output.

Deploy this to Heroku.

Now when you access your app’s /db route, you will see something like this:

Hello!
Read from DB: 2014-08-08 14:48:25.155241
Read from DB: 2014-08-08 14:51:32.287816
Read from DB: 2014-08-08 14:51:52.667683

Assuming that you have Postgres installed locally, use the heroku pg:psql command to connect to the remote database and see all the rows:

 heroku pg:psql
heroku pg:psql
psql (9.3.2, server 9.3.3)
SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256)
Type "help" for help.
=> SELECT * FROM ticks;
            tick
----------------------------
 2014-08-08 14:48:25.155241
 2014-08-08 14:51:32.287816
 2014-08-08 14:51:52.667683
 2014-08-08 14:51:53.1871
 2014-08-08 14:51:54.75061
 2014-08-08 14:51:55.161848
 2014-08-08 14:51:56.197663
 2014-08-08 14:51:56.851729
(8 rows)
=> \q

Read more about Heroku PostgreSQL.

A similar technique can be used to install MongoDB or Redis add-ons.

Next steps

You now know how to deploy an app, change its configuration, view logs, scale, and attach add-ons.

Here’s some recommended reading. The first, an article, will give you a firmer understanding of the basics. The second is a pointer to the main Java category here on Dev Center:

  • Read How Heroku Works for a technical overview of the concepts you’ll encounter while writing, configuring, deploying and running applications.
  • Read Deploying Java Apps on Heroku to understand how to take an existing Java app and deploy it to Heroku.
  • Visit the Java category to learn more about developing and deploying Java applications.