In this section, we will deploy your application on Heroku for free. We will even use the free Redis instance available to store our session and cache.
The first thing we need to do to create a Heroku application is to download the command-line tools available at https://toolbelt.heroku.com.
On Mac, you can also install it with brew
command:
> brew install heroku-toolbelt
Create an account on Heroku and use heroku login
to link the toolbelt to your account:
> heroku login Enter your Heroku credentials. Email: [email protected] Password (typing will be hidden): Authentication successful.
Then, go to your application root and type heroku create appName --region eu
. Replace appName
with a name of your choice. If you don't provide a name, it will be generated automatically:
> heroku create appname --region eu Creating appname... done, region is eu https://appname.herokuapp.com/ | https://git.heroku.com/appname.git Git remote heroku added
If you have already created an application with the UI, then go to your application root and simply add the remote heroku git:remote -a yourapp
.
What these commands do is add a Git remote called heroku
to our Git repository. The process of deploying on Heroku is just pushing one of your branches to Heroku. The Git hooks installed on the remote will take care of the rest.
If you type git remote -v
command, you should see the heroku
version:
> git remote -v heroku https://git.heroku.com/appname.git (fetch) heroku https://git.heroku.com/appname.git (push) origin https://github.com/Mastering-Spring-MVC-4/mastering-spring-mvc4-code.git (fetch) origin https://github.com/Mastering-Spring-MVC-4/mastering-spring-mvc4-code.git (push)
We need two ingredients to run a Gradle application with Heroku: a task in our build file called stage
and a tiny file that contains the command used to run our application, called ProcFile
.
The Gradle build pack will automatically try to run the ./gradlew stage
command on the root of your application.
You can get more information on the Gradle build pack at https://github.com/heroku/heroku-buildpack-gradle.
We do not have a "stage" task yet. Add the following code to your build.gradle
file:
task stage(type: Copy, dependsOn: [clean, build]) { from jar.archivePath into project.rootDir rename { 'app.jar' } } stage.mustRunAfter(clean) clean << { project.file('app.jar').delete() }
This will define a task called stage
, which will copy the jar generated by Spring Boot at the root of the application and call it app.jar
.
The jar be much easier to find this way. The stage
task depends on the clean
task and the build
task, which means that both of them will be executed before the stage task starts.
By default, Gradle will try to optimize the task dependency graph. So, we must provide a hint and force the clean
task to be run before stage
.
Finally, we add a new instruction to the already existing clean
task, which is to delete the generated app.jar
file.
Now, if you run ./gradlew stage
, it should run the tests and put the packaged app at the root of the project.
When Heroku detects a Gradle application, it will automatically run a container with Java 8 installed. So, we have very little configuration to take care of.
We will need a file containing the shell command used to run our application. Create a file named Procfile
at the root of your application:
web: java -Dserver.port=$PORT -Dspring.profiles.active=heroku,prod -jar app.jar
There are several things to note here. First, we declare our application as a web application. We also redefine the port on which our application will run using an environment variable. This is very important as your app will cohabit with many others and only one port will be allocated to each one.
Finally, you can see that our application will run using two profiles. The first is the prod
profile, which we created in the previous chapter, to optimize the performance, and a new heroku
profile that we will create in a moment.
We do not want to put sensible information, such as our Twitter app keys, into source control. So, we have to create some properties that will read those from the application environment:
spring.social.twitter.appId=${twitterAppId} spring.social.twitter.appSecret=${twitterAppSecret}
For this to work, you have to configure the two environment variables, which we discussed earlier, on Heroku. You can do this with the toolbelt:
> heroku config:set twitterAppId=appId
Alternatively, you can go to your dashboard and configure the environment in the settings tab:
Visit https://devcenter.heroku.com/articles/config-vars for more information.
It is now time to run our application on Heroku!
If you haven't already done so, commit all your changes to your master branch. Now, simply push your master branch to the heroku
remote with git push heroku master
. This will download all the dependencies and build your application from scratch, so it can take a little time:
> git push heroku master Counting objects: 1176, done. Delta compression using up to 8 threads. Compressing objects: 100% (513/513), done. Writing objects: 100% (1176/1176), 645.63 KiB | 0 bytes/s, done. Total 1176 (delta 485), reused 1176 (delta 485) remote: Compressing source files... done. remote: Building source: remote: remote: -----> Gradle app detected remote: -----> Installing OpenJDK 1.8... done remote: -----> Building Gradle app... remote: WARNING: The Gradle buildpack is currently in Beta. remote: -----> executing ./gradlew stage remote: Downloading https://services.gradle.org/distributions/gradle-2.3-all.zip ... remote: :check remote: :build remote: :stage remote: remote: BUILD SUCCESSFUL remote: remote: Total time: 2 mins 36.215 secs remote: -----> Discovering process types remote: Procfile declares types -> web remote: remote: -----> Compressing... done, 130.1MB remote: -----> Launching... done, v4 remote: https://appname.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy.... done. To https://git.heroku.com/appname.git * [new branch] master -> master
Once the application has been built, it will automatically run. Type heroku logs
to see the latest logs or heroku logs -t
to tail them.
You can see your application running in the console and if all goes as planned, you will be able to connect to http://yourapp.herokuapp.com. As shown in the following screenshot:
We are live! It's time to tell your friends!
To activate Redis in our application, we can choose between a few alternatives. The Heroku Redis add-on is the beta version. It is entirely free with 20 MB of storage, analytics, and logs.
Visit https://elements.heroku.com/addons/heroku-redis for more details.
At this stage, you will have to provide your credit card details to proceed.
To install the Redis add-on for your application, type the following:
heroku addons:create heroku-redis:test
Now, that we have activated the add-on, an environment variable called REDIS_URL
will be available when our application will be running on Heroku.
You can check that the variable is defined with the heroku config
command:
> heroku config === masterspringmvc Config Vars JAVA_OPTS: -Xmx384m -Xss512k -XX:+UseCompressedOops REDIS_URL: redis://x:[email protected]:6439
Since the RedisConnectionFactory
class does not understand URIs, we need to tweak it a little bit:
@Configuration @Profile("redis") @EnableRedisHttpSession public class RedisConfig { @Bean @Profile("heroku") public RedisConnectionFactory redisConnectionFactory() throws URISyntaxException { JedisConnectionFactory redis = new JedisConnectionFactory(); String redisUrl = System.getenv("REDIS_URL"); URI redisUri = new URI(redisUrl); redis.setHostName(redisUri.getHost()); redis.setPort(redisUri.getPort()); redis.setPassword(redisUri.getUserInfo().split(":", 2)[1]); return redis; } @Bean @Profile({"cloud", "heroku"}) public static ConfigureRedisAction configureRedisAction() { return ConfigureRedisAction.NO_OP; } }
We now have two Heroku-specific beans in the RedisConfig
class. These beans will only be active if both the redis
and heroku
profiles are active.
Note that we also deactivated some Spring Session configuration.
Spring Session will normally listen to events associated to destroyed session keys via the Redis Pub/Sub interface.
It will automatically try to configure the Redis environment to activate listeners on startup. In a secured environment like ours, adding listeners is not permitted unless you have an admin access.
These redis listeners are not really important in our case, so we can safely disable this behavior. For more information, visit http://docs.spring.io/spring-session/docs/current/reference/html5/#api-redisoperationssessionrepository-sessiondestroyedevent.
We need to modify our Procfile
file so that Heroku runs our application with the redis
profile:
web: java -Dserver.port=$PORT -Dspring.profiles.active=heroku,redis,prod -jar app.jar
Commit your change and push the code to Heroku.