This guide will show you how to deploy a Spring MVC Hibernate application to Heroku and bind it to the free Postgres database service.
Sample code is available on github along with this document. Edits and enhancements are welcome. Just fork the repository, make your changes and send us a pull request.
- Java, Maven, Git, and the Heroku client (as described in the basic Java quickstart)
- An installed version of Postgres to test locally
If you don't already have a Spring MVC Hibernate app, the easiest way to create one is with Spring Roo. Spring Roo is a RAD tool that lets you quickly build Spring MVC applications with a complete model, view and controller layer, including relational database integration.
If you don't want to install Spring Roo, you can clone the sample app:
:::term
$ git clone https://github.com/heroku/devcenter-spring-mvc-hibernate.git
Cloning into petclinic...
remote: Counting objects: 205, done.
remote: Compressing objects: 100% (100/100), done.
remote: Total 205 (delta 93), reused 205 (delta 93)
Receiving objects: 100% (205/205), 98.55 KiB, done.
Resolving deltas: 100% (93/93), done.
This will check out the completed app. To step back to the starting point, do:
:::term
$ git revert starting-point
Now you can skip forward to "Modify Database Configuration".
Install Spring Roo if you don't already have it. Then create a directory for your app and generate the app using the clinic.roo
script:
:::term
$ mkdir petclinic && cd petclinic
$ roo script --file clinic.roo
____ ____ ____
/ __ \/ __ \/ __ \
/ /_/ / / / / / / /
/ _, _/ /_/ / /_/ /
/_/ |_|\____/\____/ 1.1.4.RELEASE [rev f787ce7]
Welcome to Spring Roo. For assistance press TAB or type "hint" then hit ENTER.
project --topLevelPackage com.springsource.petclinic
Created ROOT/pom.xml
Created SRC_MAIN_JAVA
Created SRC_MAIN_RESOURCES
...
Updated SRC_MAIN_RESOURCES/log4j.properties
Script required 41 second(s) to execute
By default, the generated app sets up the Hypersonic in-memory database. However, it is strongly recommended to use the same database locally as in production. So we will switch the application over to using Postgres with this Roo command:
:::term
$ roo persistence setup --provider HIBERNATE --database POSTGRES
...
Updated SRC_MAIN_RESOURCES/META-INF/spring/database.properties
Please update your database details in src/main/resources/META-INF/spring/database.properties.
Updated ROOT/pom.xml [removed dependency org.hsqldb:hsqldb:1.8.0.10; added dependency postgresql:postgresql:8.4-702.jdbc3]
Updated SRC_MAIN_RESOURCES/META-INF/spring/applicationContext.xml
Updated SRC_MAIN_RESOURCES/META-INF/persistence.xml
...
Finally, let's create a git repo and commit this baseline of our application:
:::term
$ git init
$ echo target > .gitignore
$ git add .
$ git commit -m init
The web app generated by Spring Roo expects you to set database connection properties in the file src/main/resources/META-INF/spring/database.properties
. However, it is not a good idea to hardcode database configuration into a file that is part of your project. Instead, we will edit the Spring configuration to read the configuration from an environment variable.
Heroku automatically provisions a small database when you create a Java application and sets the DATABASE_URL
environment variable to a URL of the format
postgres://user:password@hostname/path
You can also provision a larger database service yourself using the heroku addons
command. Either way, the database connection information will be stored in the DATABASE_URL
variable.
Create a new URI spring bean initialized with this environment variable by adding this to src/main/resources/META-INF/spring/applicationContext.xml
:
:::xml
<bean class="java.net.URI" id="dbUrl">
<constructor-arg value="${DATABASE_URL}"/>
</bean>
Edit the dataSource
section in src/main/resources/META-INF/spring/applicationContext.xml
and replace the property place holders with the following:
:::xml
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource">
<property name="driverClassName" value="${database.driverClassName}"/>
<property name="url" value="#{ 'jdbc:postgresql://' + @dbUrl.getHost() + @dbUrl.getPath() }"/>
<property name="username" value="#{ @dbUrl.getUserInfo().split(':')[0] }"/>
<property name="password" value="#{ @dbUrl.getUserInfo().split(':')[1] }"/>
...
To run your app locally set the DATABASE_URL variable in your local environment to point to your local postgres database, for example:
:::term
$ export DATABASE_URL=postgres://scott:tiger@localhost/myapp
Jetty Runner lets you easily execute your web app as a standard Java application (without having to deploy it to a container). It's a simple jar that you can copy down from the central Maven repository to the target directory as part of you build. We'll use the maven-dependency-plugin
to do this by adding the following plugin configuration at the end of the plugins
section of pom.xml
:
:::xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>copy</goal></goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-runner</artifactId>
<version>7.4.5.v20110725</version>
<destFileName>jetty-runner.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
You declare how you want your application executed in Procfile
in the project root. Create this file with a single line:
:::term
web: java $JAVA_OPTS -jar target/dependency/jetty-runner.jar --port $PORT target/*.war
Let's run the app locally first to test that it all works. You must have a Postgres database up and running and accessible on the DATABASE_URL
you specified above.
:::term
$ mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building petclinic 0.1.0.BUILD-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
...
[INFO] --- maven-dependency-plugin:2.3:copy (default) @ petclinic ---
[INFO] Configured Artifact: org.mortbay.jetty:jetty-runner:7.4.5.v20110725:jar
[INFO] Copying jetty-runner-7.4.5.v20110725.jar to /Users/jjoergensen/dev/tmp/spring-roo-petclinic/target/dependency/jetty-runner.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 20.466s
[INFO] Finished at: Mon Aug 29 20:56:03 PDT 2011
[INFO] Final Memory: 9M/81M
[INFO] ------------------------------------------------------------------------
:::term
$ java -jar target/dependency/jetty-runner.jar target/*.war
Go to http://localhost:8080 and test it out by creating a new record.
Commit your changes to Git:
:::term
$ git add .
$ git commit -m "Ready to deploy"
Create the app on the Cedar stack:
:::term
$ heroku create --stack cedar
Creating high-lightning-129... done, stack is cedar
http://high-lightning-129.herokuapp.com/ | [email protected]:high-lightning-129.git
Git remote heroku added
Deploy your code:
:::term
Counting objects: 227, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (117/117), done.
Writing objects: 100% (227/227), 101.06 KiB, done.
Total 227 (delta 99), reused 220 (delta 98)
-----> 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_1jems2so86ck4 -s .m2/settings.xml -DskipTests=true clean install
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building petclinic 0.1.0.BUILD-SNAPSHOT
[INFO] ------------------------------------------------------------------------
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 36.612s
[INFO] Finished at: Tue Aug 30 04:03:02 UTC 2011
[INFO] Final Memory: 19M/287M
[INFO] ------------------------------------------------------------------------
-----> Discovering process types
Procfile declares types -> web
-----> Compiled slug size is 62.7MB
-----> Launching... done, v5
http://pure-window-800.herokuapp.com deployed to Heroku
Congratulations! Your web app should now be up and running on Heroku. Open it in your browser with:
:::term
$ heroku open