Lesson Wednesday

If your homepage is configured to display information from a database, you will receive a 500 server error should you try and view the homepage without a DB connected and properly configured. Static pages that do not require a DB connection should still be accessible by typing out the URL.

Introduction

Now that we've learned quite a bit of Java, Spark, and Postgres, we’ll want to find a way to deploy our applications to a live server for others to use and for us to show them off. Differently than static html, css and js files that we can push to a simple webserver or even use gh-pages, we’ll need a different kind of server access to deploy our Java, Spark, and Handlebars-backed apps.

Heroku is a service that hosts web applications. Their free tier is rather generous, allows you to test your applications in a production environment, and works well with Java/Spark-backed apps. Even better: They supports Postgres databases - exactly what we need to publish our database-driven applications!

The publication process is a bit of an endeavor, so follow these instructions closely. If you notice any substantial differences between our lesson and the Heroku publication process, please let us know so we can update these instructions - Heroku is often updated, so it may change at short notice.

Initial Setup

Before we can begin deploying projects, we'll need several things: The Heroku CLI (command line interface) software (formerly known as the Heroku Toolbelt), a free Heroku account, and Maven software.

Installing Heroku CLI

The Heroku CLI should already be installed on Epicodus machines, but you’ll need to install it in order to publish apps from your personal machine.

See these guides on how to set up Heroku CLI on your personal machine.

Installing Maven

Next, we need to install a tool called Maven. Maven is very similar to Gradle, the app we use to compile, test and run our Java-backed applications. Gradle can be used to deploy to Heroku’s environment, but using Maven is simpler and more reliable for this particular job, especially where Spark, Velocity, and Postgres are concerned. Like Gradle, Maven manages Java dependencies and runs tasks for us.

Mac Installation

If we have Homebrew installed on our machine (all classroom Macs already do) we can simply type:

$ brew install maven

Windows Installation

If you are on a Windows machine, see this guide on how to install Maven in your Windows environment.

Creating a Heroku Account

Next, we we need to create a Heroku account. Head to heroku.com and create a free account.

Once you have completed the steps outlined above, you are ready to get your local project ready for deployment!

Configuring Maven

Next, we need to ensure Maven is configured correctly. Because are switching from Gradle to Maven, we need to ensure Maven is set up correctly before running any commands. Maven must know which dependencies our project requires, just like Gradle. Maven loads this information from a file called pom.xml, which is Maven's equivalent of Gradle's build.gradle file.

Create a file called pom.xml in the root directory of your project folder.

Open it in Atom, and copy in the following code:

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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.mycompany.app</groupId>
  <artifactId>my-app</artifactId>
  <version>1.0</version>

  <dependencies>
<!-- We need to list out ALL dependencies our project needs. If you have dependencies not listed here, but sure to add them -->
    <dependency>
      <groupId>com.sparkjava</groupId>
      <artifactId>spark-core</artifactId>
      <version>2.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.velocity</groupId>
      <artifactId>velocity</artifactId>
      <version>1.7</version>
    </dependency>
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>9.4.1208</version>
    </dependency>
    <dependency>
      <groupId>org.sql2o</groupId>
      <artifactId>sql2o</artifactId>
      <version>1.5.4</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
          <archive>
            <!-- Change this to YOUR main class name if different. Usually, it will be App. -->
            <manifest>
              <mainClass>App</mainClass>
            </manifest>
          </archive>
        </configuration>
      </plugin>
      <plugin>
        <groupId>com.heroku.sdk</groupId>
        <artifactId>heroku-maven-plugin</artifactId>
        <version>0.4.4</version>
        <configuration>
          <jdkVersion>1.8</jdkVersion>
          <!-- Change this to YOUR application name! -->
          <appName>name-of-app</appName>
          <processTypes>
            <web>java -jar target/my-app-1.0-jar-with-dependencies.jar</web>
          </processTypes>
        </configuration>
      </plugin>
    </plugins>
  </build>

</project>

If you take a look through this file, you’ll see that it contains all the same information a build.gradle file does.

The two most important areas are:

  • <dependencies>, where our dependencies are listed. If your application has additional dependencies, be sure to add them here.

  • <mainClass>, where the name of our app's main class is provided. In the code above, we use App as our <mainClass> name, because our App class in the App.java file is responsible for launching our applications. But if your app's main class is named something other than App, you'll need to change your <mainClass> information accordingly.

NOTE: You can include JUnit by adding it to your Maven dependencies. That said, I would recommend you remove them for deployment, as you would also need to configure a test database. The choice is yours. Deployment should be the final stage of your app's development lifecycle anyway.

Remember this app name, as you’ll be typing it frequently later on!

Updating our Java Code

Next, we need to make two small changes to our App.java file in order to deploy our app to Heroku correctly.

First, open App.java and make it match the following example:

App.java
...

public class App {
  static int getHerokuAssignedPort() {
    ProcessBuilder processBuilder = new ProcessBuilder();
    if (processBuilder.environment().get("PORT") != null) {
      return Integer.parseInt(processBuilder.environment().get("PORT"));
    }
    return 4567; //return default port if heroku-port isn't set (i.e. on localhost)
  }
  public static void main(String[] args) {

    port(getHerokuAssignedPort());
    staticFileLocation("/public");
[..]

...

Great. Now Heroku knows which port to use, and our application will still run correctly on localhost. We are already halfway there!

Next, it's time to create our Heroku app and Postgres database.

Creating a New Heroku App

Now, run the following commands in the command line tool (must have Heroku CLI installed):

$ heroku login

Once logged in, you can access your Heroku account from your command line.

Run the following commands:

$ heroku create name-of-app

Here, make sure to insert the app name you provided in your pom.xml file where name-of-app appears, for simplicity’s sake.

After running this command, you should see something like this:

Creating ⬢ name-of-app... done
https://name-of-app.herokuapp.com/ | https://git.heroku.com/name-of-app.git

You now have a Heroku app. Sweet!

Adding a Postgres Database to our Heroku App

Let’s add a Postgres database to our new Heroku app next. Run the following command in your terminal:

$ heroku addons:create heroku-postgresql:hobby-dev --app name-of-app

You should see this:

$ heroku addons:create heroku-postgresql:hobby-dev --app name-of-app
Creating heroku-postgresql:hobby-dev on ⬢ name-of-app... free
Database has been created and is available
 ! This database is empty. If upgrading, you can transfer
 ! data from another database with pg:copy
Created postgresql-corrugated-80870 as DATABASE_URL
Use heroku addons:docs heroku-postgresql to view documentation

This is installing a hobby-developer level Postgres database for the app of our choice. Nice. The hobby-dev database is free.

Make note of the name Heroku gives your database, in this example it is postgresql-corrugated-80870, but yours will be different. We will need this information soon.

Retrieving and Using User Credentials

Next, we need to retrieve the credentials for this Postgres database and change our existing DB.java (or other file) to match, as we clearly can’t use the same credentials we use for our local project. Our Database is live on the internet and publicly accessible, so we have to make sure we take extra steps to protect it.

Point your browser to heroku.com, log in, find your app, and click on Resources > Heroku Postgres::Database

Then, click on View Credentials. Copy the (very long) URI from the page:

Example URI taken from Heroku:

postgres://stdhhdzdeynsis:4[email protected]ec2-23-21-76-49.compute-1.amazonaws.com:5432/df2ubtmuhc32s7

Next, open the files that connect to the database. Your existing code should look a bit like this:

String connectionString = "jdbc:postgresql://localhost:5432/jadle_test"; //!
Sql2o sql2o = new Sql2o(connectionString, null, null); //!

Comment this out (keep it so you can still run your app locally!), and change it to (using YOUR credentials):

String connectionString = "jdbc:postgresql://ec2-23-21-76-49.compute-1.amazonaws.com:5432/df2ubtmuhc32s7"; //!
    Sql2o sql2o = new Sql2o(connectionString, "stdhhdzdeynsis", "43a1b82999c0f772dbbd8f7602f0fa50c75b0c3e0f7b0c2caa36637a9569de10"); //!

Let's dissect this a bit:

  • jdbc:postgresql:// -- Lets Java know we are using jdbc (java database connectivity) for Postgres
  • ec2-23-21-76-49.compute-1.amazonaws.com -- URI for the DB server
  • 5432 -- port number for the DB server
  • df2ubtmuhc32s7 -- route to the DB on the server
  • stdhhdzdeynsis -- username
  • 43a1b82999c0f772dbbd8f7602f0fa50c75b0c3e0f7b0c2caa36637a9569de10 -- password

Cause of many failures: The URL you may copy points to postgres:// but your URI for SQL2o needs to point to postgresql://

Be sure to recreate this sequence exactly, and make sure you don’t insert any spaces, additional special characters, etc.

If you are using a test database for your app, you will need to add an additional database, and add the correct information for your test database where necessary.

Save and commit.

Test Deploying Our App without a Database

Before we move on to the final step, lets do a test deploy our setup so we may fix any issues before we create our database schema.

In order to deploy your app, in your command line, tell Maven to deploy your app:

$ mvn heroku:deploy

You should see dependencies downloading, and a long line of output before finally seeing:

[INFO] -----> Done
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 24.534 s
[INFO] Finished at: 2017-04-12T14:41:16-07:00
[INFO] Final Memory: 29M/328M
[INFO] ------------------------------------------------------------------------

Yay! Go to the URL in the output and you should see your page! We don’t have a database hookup yet but that comes next.

Troubleshooting

If you don’t see the response depicted above the first time, don’t panic!

First, check your dependencies. Is there something you need you not mentioning in your pom.xml ?

If you have tests, make sure you are including JUnit as a dependency and your tests pass. If you keep receiving test errors despite those fixes, try deleting the local /target folder (similar to the build folder) and trying again, as it sometimes gets hung up here.

Recreating our Database on Heroku Postgres

We can log in to our app’s psql terminal with the following command:

$ heroku psql --app your-app-name

Cool, things look familiar! You have a standard psql terminal prompt. You might be tempted to think you can simply try CREATE DATABASE hair_salon and make your DB manually, but that would be too easy: Heroku does not let you run commands like CREATE DATABASE. If you try that, you receive permissions errors. Whomp whomp.

Instead of manually creating tables, we will push our database schema from our local machine up to Heroku. This is not the same as importing sql, so the process is different. We are going to take a snapshot of our locally-loaded database, then overwrite the remote one with this snapshot, instead of restoring from a static file.

(Note: If your database schema is already ready and complete on your local machine, you can skip this next step. You MUST delete all data though, or you will receive an annoying error.)

Either quit out of the Heroku psql terminal with \quit, or open a separate terminal window and access your local Postgres database by typing psql. Create your database as you need. Alternatively, restore a database with psql database_name < infile.sql in your terminal as you have done previously.

Next, we'll push our local database to Heroku Postgres from our regular Terminal (not psql).

Run the following command, replacing postgresql-corrugated-80870with your Heroku database name, and your-app-name with the name of your application):

$ heroku pg:push local_database_name postgresql-corrugated-80870 --app your-app-name

Again, you’ll see a lot of output. You may see something like this:

[...]
pg_restore: [archiver (db)] Error from TOC entry 2386; 0 0 COMMENT EXTENSION plpgsql
pg_restore: [archiver (db)] could not execute query: ERROR:  must be owner of extension plpgsql
    Command was: COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
pg_restore: creating TABLE "public.clients"
pg_restore: creating SEQUENCE "public.clients_id_seq"
pg_restore: creating SEQUENCE OWNED BY "public.clients_id_seq"
pg_restore: creating TABLE "public.stylists"
[...]
pg_restore: setting owner and privileges for CONSTRAINT "public.clients_pkey"
pg_restore: setting owner and privileges for CONSTRAINT "public.stylists_pkey"
WARNING: errors ignored on restore: 1

You can ignore this error. It is related to our local machine’s permissions, which are different than on our remote Postgres. Your app should work fine regardless.

Now, try running your app!

Troubleshooting

If you receive a 500 server error when trying to access routes that require information from the DB, double check the following:

  • Login Credentials: Any formatting errors in your URI? Your URI needs to be pointing to jdbc:postgresql:// NOT jdbc:postgres://
  • Heroku Logs: Any useful information in your heroku logs? Click on your app when you log into Heroku, then, in the top right corner click more and select view logs to see your logs. There may be error codes in there that you can Google.

If still no joy, double check your app runs locally, then make any fixes, and try again.

Congrats on your first deployment!