24. Heroku: Seeding the Cloud

Deploying your application into the cloud is a great way to scale from from "wouldn't it be cool if.." to giving interviews to Forbes, Fast Company, and Jimmy Fallon. Heroku makes it super easy to provision everying you need, including a Neo4j Add-on. With a few simple adjustments, your Spring Data Neo4j application is ready to take that first step into the cloud.

To deploy your Spring Data Neo4j web application to Heroku, you'll need:

For reference, the following sections detail the steps taken to make the Spring Data Neo4j Todos example ready for deployment to Heroku.

24.1 Create a Self-Hosted Web Application

Usually, a Spring MVC application is bundled into a war and deployed to an application server like Tomcat. But Heroku can host any kind of java application. It just needs to know what to launch. So, we'll transform the war into a self-hosted servlet using an embedded Jetty server, then add a startup script to launch it.

First, we'll add the dependencies for Jetty to the pom.xml:

Example 24.1. Jetty dependencies - pom.xml

<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-webapp</artifactId>
    <version>7.4.4.v20110707</version>
</dependency>
<dependency>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jsp-2.1-glassfish</artifactId>
    <version>2.1.v20100127</version>
</dependency>
      

Then we'll change the scope of the servlet-api artifact from provided to compile. This library is normally provided at runtime by the application container. Since we're self-hosting, it needs to be included directly. Make sure the servlet-api dependency looks like this:

Example 24.2. servlet-api dependencies - pom.xml

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    <scope>compile</scope>
</dependency>
      

We could provide a complicated command-line to Heroku to launch the app. Instead, we'll simplify the command-line by using the appassembler-maven-plugin to create a launch script. Add the plugin to your pom's build/plugins section:

Example 24.3. appassembler-maven-plugin configuration pom.xml

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.1.1</version>
<executions>
  <execution>
    <phase>package</phase>
    <goals><goal>assemble</goal></goals>
    <configuration>
      <assembleDirectory>target</assembleDirectory>
      <extraJvmArguments>-Xmx512m</extraJvmArguments>
      <programs>
        <program>
          <mainClass>Main</mainClass>
          <name>webapp</name>
        </program>
      </programs>
    </configuration>
  </execution>
</executions>
</plugin>
      

Finally, switch the packaging from war to jar. That's it for the pom.

Now that the application is ready to be self-hosted, create a simple Main to bootstrap Jetty and host the servlet.

Example 24.4. src/main/java/Main.java

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
public class Main {
    public static void main(String[] args) throws Exception {
        String webappDirLocation = "src/main/webapp/";
        String webPort = System.getenv("PORT");
        if(webPort == null || webPort.isEmpty()) {
            webPort = "8080";
        }
        Server server = new Server(Integer.valueOf(webPort));
        WebAppContext root = new WebAppContext();
        root.setContextPath("/");
        root.setDescriptor(webappDirLocation+"/WEB-INF/web.xml");
        root.setResourceBase(webappDirLocation);
        root.setParentLoaderPriority(true);
        server.setHandler(root);
        server.start();
        server.join();
    }
}
      

Notice the use of environment variable "PORT" for discovering which port to use. Heroku and the Neo4j Add-on use a number of environment variable to configure the application. Next, we'll modify the Spring application context to use the Neo4j variables for specifying the connection to Neo4j itself.

In the SDN Todos example, src/main/resources/META-INF/spring/applicationContext-graph.xml was modified to look like this:

Example 24.5. Spring Data Neo4j REST configuration - applicationContext-graph.xml

<neo4j:config graphDatabaseService="graphDatabaseService"/>
<bean id="graphDatabaseService"
    class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase">
    <constructor-arg index="0" value="${NEO4J_REST_URL}" />
    <constructor-arg index="1" value="${NEO4J_LOGIN}" />
    <constructor-arg index="2" value="${NEO4J_PASSWORD}" />
</bean>

Before provisioning at Heroku, test the application locally. First make sure you've got Neo4j server running locally, using default configuration. Then set the following environment variables:

Example 24.6. environment variables

export NEO4J_REST_URL=http://localhost:7474/db/data
export NEO4J_LOGIN=""
export NEO4J_PASSWORD=""

Now you can launch the app by running sh target/bin/webapp. If running the SDN Todos example, you can test it by running ./bin/todos list. That should return an empty JSON array, since no todos have been created yet.

For details about the todos script, see the readme included with the example.

24.2 Deploy to Heroku

With a self-hosted application ready, deploying to Heroku needs a few more steps. First, create a Procfile at the top-level of the project, which will contain a single line identifying the command line which launches the application.

The contents of the Procfile should contain:

Example 24.7. Procfile

web: sh target/bin/webapp

Example 24.8. deploy to heroku

# Initialize a local git repository, adding all the project files
  git init
  git add .
  git commit -m "initial commit"

# Provision a Heroku stack, add the Neo4j Add-on and deploy the appication

  heroku create --stack cedar
  heroku addons:add neo4j
  git push heroku master


[Note]Note

Note that the stack must be "cedar" to support running Java. Check that the process is running by using heroku ps, which should show a "web.1" process in the "up" state. Success!

For the SDN Todos application, you can try out the remote application using the -r switch with the bin/todo script like this:

Example 24.9. Session with todo script

./bin/todo -r mk "tweet thanks for the good work @mesirii @akollegger"
./bin/todo -r list

To see the Neo4j graph you just created through Heroku, use heroku config to reveal the NEO4J_URL environment variable, which will take you to Neo4j's Webadmin.