cc by-sa flurdy

Play! + Heroku

Play framework
Heroku

Develop quickly & deploy easily to the Cloud

(Ubuntu + Git + IntelliJ + Apache + Java or Scala)


Contents

  1. Aim
  2. Why
  3. Pre-requisites
  4. Install Play
  5. Install IntelliJ
  6. Install Heroku
  7. Create project
  8. Develop application
  9. Deploy to Heroku
  10. Add ons
  11. Load balancing with Apache httpd
  12. Scala & Play 2.x
  13. Extensions
  14. References
  15. Help
  16. Feedback
  17. Revision
wont fix t-shirt
shirts.flurdy.com

For Play! 2.x & Scala, please read my Play! 2.x guide.

Aim

Why

What is Play

The Play! framework is a web application framework that can be used with Java or Scala. And it makes it easy, quick but also powerful. By purposfully not adhering to the outdated j2ee spec, Play's api is much cleaner and RESTful. Its feature of dynamic class reloading makes development very quick.

What is Heroku

Heroku is a cloud based application platform or PAAS (Platform as a Service). Or a "Polyglot Cloud Application Platform" according to James Ward. It runs on top of Amazon AWS ec2, but you do not need to know anything about that. The interface is so easy and quick to use which is why I like it. The big bonus is that if you only run one "web server" (dyno) per application it is free. Only when you need to scale or add special features do you need to pay anything. And then it is mostly pay as you go, so not locked in for the year. Another neat feature of Heroku is the use of Git as deployment tool.

Pre-requisites

Ubuntu

This guide will assume Ubuntu is your OS. However any Linux distro will be fine, Mac OSX will also be quite similar. Windows will be different but with similar steps.

Java

Since Oracle decided to disallow security updates for Java 6 on Ubuntu, I will assume OpenJDK 7.

sudo aptitude install openjdk-7-jdk

You can alternatively install Oracle Java 7 via a PPA.

Git

Heroku's deployment model uses Git so while you don't need to be a git guro, you do need to use it.

sudo aptitude install git gitk

Install Play!

The Debian Play module seems outdated, and I have not been able to find a good PPA, so there is no .deb packge to install. However installing manually is easy. You will have to upgrade it manully in the future however.

Download the latest 1.x version from www.playframework.org/download (currently 1.2.4)

wget http://download.playframework.org/releases/play-1.2.4.zip

Unzip and move to a library position

unzip play-1.2.4.zip;
sudo mv play-1.2.4 /usr/local/lib/;
cd /usr/local/lib;
sudo ln -s play-1.2.4 play;

Add play to your path by editing /etc/environment

sudo vi /etc/environment

Append play path to the PATH property

PATH="/usr/local/..../usr/games:/usr/local/lib/play"

To delay relogging in to activate the /etc/environment changes

export PATH="$PATH:/usr/local/lib/play"

To test if it works

play version

Install IntelliJ

If you can afford the Ultimate edition (or get work to pay for it) then great as it has from version 11 built in support for Play. However I can not afford it so I will describe the Community edition.

Download IntelliJ from www.jetbrains.com/idea/download/

Unpack it and move to an appliation location

tar xzf ideaIC-11.tar.gz;
sudo mkdir -p /opt/jetbrains;
sudo mv idea-IC-111.69 /opt/jetbrains/;
cd /opt/jetbrains;
sudo ln -s idea-IC-111.69 idea

You can now start IntelliJ with:

/opt/jetbrains/idea/bin/idea.sh

Or add /opt/jetbrains/idea/bin folder to your PATH variable like we did with Play, or create a Unity launcher.

Install Heroku

Basically we are following step 1-3 of the Heroku quickstart article

Register

First sign up to Heroku.

Install toolbelt

To use Heroku's command line tool called Toolbelt you need to add a PPA repository (toolbelt.herokuapp.com/ubuntu) and install heroku-toolbelt. Either do it via Ubuntu Software Center, Synaptic or by the commands below:

sudo apt-add-repository 'deb http://toolbelt.herokuapp.com/ubuntu ./';
curl http://toolbelt.herokuapp.com/apt/release.key | sudo apt-key add -;
sudo aptitude update;
sudo aptitude install heroku-toolbelt;

Log into Heroku

On initial login it will ask you to upload a SSH public key. If you have one already you can reuse it, or you can manually create one before hand, or Heroku will create one for you.

heroku auth:login

Create project

As detailed in Plays tutorial

play new pizzeria

Then set up version control.

cd pizzeria;
git init;
git add .;
git commit -m "Initial"

Create an IntelliJ project.

play idealize

Now you can open the project in IntelliJ IDEA. (You probably want to add *.iml, *.ipr and .idea to the .gitignore file)

Develop application

As Play continously reloads classes while in development mode, we start the Play application first. (again similar to Play's tutorial)

play run

If you go to localhost:9000 you will see the initial page for your application.

Front & about pages

Next we will then set up a controller and some views

In your IDEA open app/controllers/Application.java. Notice the index method:

...
  public static void index() {
    render();
  }
...

Since this controller method just calls render() without parameters it will render the view app/views/Application/index.html. If you open this file:

#{extends 'main.html' /}
#{set title:'Home' /}

#{welcome /}

First line tells you this is a subset of another template main.html. Then sets the title of the page. The #{welcome /} shows a custom tag which is all the welcome blahbla you see on the front page. Modify this file to:

#{extends 'main.html' /}
#{set title:'Pizzeria' /}

<h1>Pizzeria</h1>
<ul>
  <li><a href="@{Application.about}">About</a></li>
</ul>

If you refresh your browser window (localhost:9000), you will now get an error. This is because the URL in the list above is not yet implemented.

Open app/controllers/Application.java. Add this method:

...
  public static void about() {
    render();
  }
...

Again a very simple method, and you need to create the view at app/views/Application/about.html.

#{extends 'main.html' /}
#{set title:'About the Pizzeria' /}

<h1>About the Pizzeria</h1>
<p>
  Lorem ipsum dolor sit amet, arcu et leo lacinia etiam
  viverra fames, facilisi inceptos ultricies vitae tristique
  phasellus, leo ut proin cum dictumst ultricies ultricies
</p>

You can now reload the front page, and even click on the about page link.

Routes

The url shown and in the links are not so "tidy", and to make them more RESTlike you need to edit the conf/routes file. Modify it so it is similar to:

...
# Home page
GET    /                      Application.index
GET    /index.html            Application.index
GET    /about.html            Application.about
...

You can read more about controllers, template views and routes in the play documentation.

Pizza order form

More interesting than the above mostly static templates are forms and binding to objects within Play. We will create a pizza order form, and relevant controllers for dealing with the order. (You may apply TDD for this development using Play's built in testing support).

Create a new controller at app/controllers/PizzaOrder.java.

package controllers;
import play.mvc.Controller;
import play.Logger;

public class PizzaOrder extends Controller {
  public void showOrderForm(){
    render("PizzaOrder/orderform.html");
  }
}

The showOrderForm method is a simple template redirection to show the form. This time we are actually specifying the template name.

Create the template app/views/PizzaOrder/orderform.html

<h1>Pizza order</h1>
#{form @PizzaOrder.orderPizza()}
  <fieldset>
    <legend>Choose pizza</legend>
    #{select 'pizzaType', value:'pepperoni'}
      #{option 'margherita'}Margherita{/option}
      #{option 'hawaiian'}Hawaiian{/option}
      #{option 'pepperoni'}Pepperoni{/option}
      #{option 'quattroformaggi'}Quattro formaggi{/option}
    #{/select}
  </fieldset>
  <fieldset>
    <legend>Size</legend>
    <input type="radio" name="size" id="sizeMedium"
        value="Medium" checked="checked" />
    <label for="sizeMedium">Medium</label>
    <br/>
    <input type="radio" name="size" id="sizeLarge"
         value="Large" />
    <label for="sizeLarge">Large</label>
  </fieldset>
  <fieldset>
    <legend>Delivery address</legend>
    <input type="text" name="address" />
  </fieldset>
  <button type="submit">Order pizza<button>
#{/form}

(We are using Play 1.x's Groovy based templated script and tagging to simplefy the form generation).

Modify app/controllers/PizzaOrder.java by adding two methods:

...
  public void orderPizza(
      String pizzaType, String size, String address){
    Logger.info("Ordering pizza: " pizzaType);
    
    // business order logic
    
    pizzaOrdered(pizzaType);
  }

  public void pizzaOrdered(String pizzaType){
    render(pizzaType);
  }
...

The orderPizza method is the real business logic method. It receives form data, binds it to java objects and processes this. (Actually we mostly ignore the data, but a real application would obviously not). Then it calls the pizzaOrdered method for showing a confirmation template. Play will detect this and issue a HTTP redirect to this method, so that you are protected against multiple accidental orders.

Create the template app/views/PizzaOrder/pizzaOrdered.html

<h1>Pizza ordered</h1>
<p>
  The ${pizzaType} pizza will arrive within 40 minuttes!
</p>

We need to tidy up the URLs. We need one (and perhaps two aliases) to show the form, a POST handler for receiving the form, and a confirmation page. Open conf/routes:

...
GET /order/              PizzaOrder.showOrderForm
GET /order/index.html      PizzaOrder.showOrderForm
GET /order/new.html      PizzaOrder.showOrderForm
POST /order/              PizzaOrder.orderPizza
GET /pizzaOrdered.html      PizzaOrder.pizzaOrdered
...

Ps. A cleaner way when the form expands is to create a POJO binding instead of individual method parameters.

Ps2. We could also have applied validation.

Send email

A more usefull order flow is perhaps if we are notified of the order! Play provides an easy way of sending email. And enables a mocking class for email in development mode.

Open conf/application.properties. We will keep the mock feature enabled, but set production mode to connect to relay via GMail.

...
# Mail configuration
# ~~~~~
# Default is to use a mock Mailer
mail.smtp=mock

%prod.mail.smtp.host=smtp.gmail.com
%prod.mail.smtp.user=yourusername@gmail.com
%prod.mail.smtp.pass=yourpassword
%prod.mail.smtp.channel=ssl
...

Open app/controllers/PizzaOrder.java, modify the orderPizza method and add a call to PizzaOrderMailer.

...
  public void orderPizza(
      String pizzaType, String size, String address){
...
    PizzaOrderMailer.sendOrder(pizzaType, size, address);
...
    pizzaOrdered(pizzaType);
  }
...

Create app/notifiers/PizzaOrderMailer.java.

package notifiers;
import play.mvc.Mailer;

public class PizzaOrderMailer extends Mailer {
  public static void sendOrder(
      String pizzaType, String size, String address){
    setSubject("Pizza order");
    addRecipient("[email protected]");
    setFrom("Pizzeria Website <[email protected]>");
    send(pizzaType, size, address);
  }
}

The actual email sent can be plain text but in our case it will be html based. Like normal pages it is a template. Open app/views/PizzaOrderMailer/sendOrder.html:

<html>
<body>
  <pre>
    Pizza order
    
    Pizza type: ${pizzaType}
    Pizza size: ${size}
    Delivery address: ${address}
  <pre>
</body>
</html>

Error pages

You can customise the error pages (404 & 500 only). Simply edit the files in app/views/errors. For further fine grained errors (410, 403, 302, etc) you will need an Apache server in front.

Deploy to Heroku

To create a dyno for this application:

heroku create -s cedar

This will create a server for you to deploy to. It will respond with a domain name that your application will run on. Something like blue-moon-123.herokuapp.com.


Previously you needed to create a Procfile, to describe launch parameters, such as port number etc. This is no longer needed, unless you need to customise this.

Make sure everything is added and commited to git:

git commit -am "kitchen sink commit"

Then to deploy your appliction to the dyno created by Heroku:

git push heroku master

Now either open a browser and type in the dyno's domain name or execute:

heroku open

Voila!

Add ons

Heroku offers a range of add-ons to add to your application. Most are free or at least free initially and only start to charge once you application requires heavy load or further scaling.

Custom domain name

The URL blue-moon-123.herokuapp.com is fine to use while testing and developing. But in many production situation it is not ideal. So you can add the domain name add-on, which will listen to requests of that name if the dns aliases to blue-moon-123.herokuapp.com.

More practically in my situation is that this enables the Play application to know it is listening to your real domain name. And so that you can put an Apache httpd server infront as a load balancer while still proxying to blue-moon-123....

heroku domains:add myapp.example.com

(Note Heroku now includes Custom domain addon by default).

(Note2 in Play's conf/application.conf you can also hard code the production domain name under %prod.application.baseUrl).

Send email

If you need more customisation than the mentioned integration with GMail, like not involving Google for example, among several choices is SendGrid add-on.

Logging

Several choices including the plain Logging add-on.

Database

There are multiple databases available as add-ons. From standard RDBMS like PostgreSQL and MySQL (with Amazon RDS as an option as well) to NoSQL DBs like MongoDB, Redis and CouchDB to in memory caching solutions like Memcache.

And many more.

Apache httpd in front of Play applications

You do not need to have an Apache Httpd server in front your Play application(s). Especially if you have added the Custom Domain add-on.

However I do so that I can switch between Heroku and applications hosted elsewhere. And enable smooth load balancing especially during rollout staging. The fact I already run a httpd server means no increased cost for me.

Play also document how to configure lighthttpd as proxy infront of Play, I will however use Apache.

Reverse proxy

As part of my AWS ec2 guides I detail how to relay traffic from between Apache Httpd and Tomcat servers. And in this situation we need to do the same.

I am asuming you already have an Apache server up and running. If not, follow my AWS ec2 Apache Httpd and Tomcat servers guide. In that I detail how to set up the servers. I also describe how to relay traffic from between Apache Httpd and Tomcat servers and in this situation we need to do something similar.

You need to install and enable the Reverse proxy module.

sudo aptitude install libapache2-mod-proxy-html;
sudo a2enmod proxy_html

Ensure you are not exposing your server as a normal proxy, by stating ProxyRequests Off.

sudo vi /etc/apache2/mods-enabled/proxy.conf ...
ProxyRequests Off
...

Open you virtual host and modify to something like these proxy lines

sudo vi /etc/apache2/sites-available/example.com
ServerName www.example.com
...br/> <IfModule mod_proxy.c>
  ProxyPreserveHost On
  ProxyPass /pagenottoproxy.html !
  ProxyPass / http://blue-moon-123.herokuapp.com/
  ProxyPassReverse / http://blue-moon-123.herokuapp.com/
</IfModule mod_proxy.c>
...
</VirtualHost>

Activate changes with

sudo apachectl -t;
sudo service apache2 reload

Load balancing with Apache httpd

The main benefit with having a httpd server in front of Play applications is the ability to load balance smoothly.

Enable the balancer module.

sudo a2enmod proxy_balancer

Modify your virtual host file similar to this:

sudo vi /etc/apache2/sites-available/example.com
ServerName www.example.com
...
<Location /balancer-manager>
  SetHandler balancer-manager
  Order Deny,Allow
  Deny from all
</Location>
<IfModule mod_proxy.c>
  ProxyPreserveHost On
  ProxyPass /pagenottoproxy.html !
  <Proxy balancer://mycluster>
    BalancerMember http://blue-moon-123.herokuapp.com
    BalancerMember http://apps.example.com:9000 status=+H
  </Proxy>
  ProxyPass / balancer://mycluster/
  ProxyPassReverse / http://blue-moon-123.herokuapp.com/
  ProxyPassReverse / http://apps.example.com:9000/
</IfModule mod_proxy.c>
...
</VirtualHost>

In this example we use Heroku but also the application is deployed to a different server on port 9000.

The stats=+H of the second balancer member indicate that this member will only be used when no other can be reached. So this configuration is a fail-over load balancer.

Do remember Heroku will do load balancing for you. But this configuration is nice for staging rollout, and for simple switchover to alternative hosting.

Rollout staging

For a smooth rollout you can use Heroku and load balancing. Note when deploying Play it will initially be unvailable due to when running in Production mode it will pre-compile all classes, so a smooth rollout makes a difference.

  1. Deploy your current version to a 2nd Heroku server, e.g. young-nebula-123.herokuapp.com
  2. Test 2nd server is up and running by goin to young-nebula-123...
  3. Configure your Apache server to load balance as detailed above
    1. Replace apps.example.com:9000 with young-nebula-123.herokuapp.com/
    2. Make the 2nd server the default (not the +H one)
    3. Test all okay
  4. Upgrade the 1st server by pushing new code
  5. Test upgraded version is up and running (by going directly to blue-moon-123.herokuapp.com)
  6. Make the 1st server the default balancer member again
  7. Remove the 2nd server from load balancer
  8. Shutdown the 2nd Heroku server

With Heroku's pricing model you only need to pay for those few minuttes the second server is running.

Scala & Play 2.x

I have a seperate document for Play1 2.0 + Scala + Heroku





Extensions

Next possible steps could be:

References

I wont fix your computer

Help

If you need assistance, please use these channels:

Or hire me (www.eray.co.uk) for:

Feedback

Revision