Nicolas Hurion

Building tools to help people.

Deploy a Vaadin Application on Heroku

I’ve been playing with Vaadin for some times now, doing small projects to test things with it. I could always build and run them locally, but having my experiments available on internet wasn’t very easy unit the cloud offering started appearing.

Heroku is one of those cloud application platform. It’s a place where you can host small web application in java or other technologies for free and you can scale them up easily, for a fee. Deploying on heroku is done using git. You do a git push to heroku and it will take your code, compile it and then run it. You don’t install an application server, you declare in a file a web program to launch. Meaning you need an embedded server like tomcat or jetty to run a java web-app on heroku.

Fortunately, colleagues of mine already made a nice piece of software that allow to easily have an embedded tomcat configured for Vaadin, it’s called Embed for Vaadin.

The thing is, Vaadin rely heavily on session, and heroku does not allow sticky session. Meaning that if you want to scale to more than one dyno you have to store your session somewhere, either in a database or in an external memory cache system like memcached Turns out someone already worked on storing tomcat sessions in memcached and released a very nice tool for that: memcached-session-manager

Mixing all those together, I ended up creating vaadin-for-heroku.

Here is how to create a project with just 3 files and deploy it on heroku.

(pom.xml) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?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.example</groupId>
    <artifactId>helloworld</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>eu.hurion.vaadin.heroku</groupId>
            <artifactId>vaadin-for-heroku</artifactId>
            <version>0.4</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.4</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Next create the Vaadin application:

/src/main/java/com/example/HelloWorld.java (HelloWorld.java) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.example;

import com.vaadin.Application;
import com.vaadin.ui.*;

import static eu.hurion.vaadin.heroku.VaadinForHeroku.forApplication;
import static eu.hurion.vaadin.heroku.VaadinForHeroku.herokuServer;

public class HelloWorld extends Application {

    @Override
    public void init() {
        final Window window = new Window();
        setMainWindow(window);
        window.setContent(buildContent());
    }

    private ComponentContainer buildContent() {
        final VerticalLayout layout = new VerticalLayout();
        final Label label = new Label("Hello world!");
        layout.addComponent(label);
        return layout;
    }

    public static void main(final String[] args) {
       herokuServer(forApplication(HelloWorld.class)).start();
    }
}

And then, create the Procfile that will tell to heroku what to launch.

(Procfile) download
1
web:    java $JAVA_OPTS -cp target/classes:target/dependency/* com.example.HelloWorld

Now everything is in place. If you want to try it locally first, you need to install and launch memcached. Internet will help you with that. Then, you need foreman

1
gem install foreman

Once foreman is installed, you can use it to start the application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ foreman start
20:50:18 web.1  | started with pid 1185
20:50:19 web.1  | SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
20:50:19 web.1  | SLF4J: Defaulting to no-operation (NOP) logger implementation
20:50:19 web.1  | SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
20:50:19 web.1  | Oct 15, 2012 8:50:19 PM org.apache.coyote.AbstractProtocol init
20:50:19 web.1  | INFO: Initializing ProtocolHandler ["http-bio-5000"]
20:50:19 web.1  | Oct 15, 2012 8:50:19 PM org.apache.catalina.core.StandardService startInternal
20:50:19 web.1  | INFO: Starting service Tomcat
20:50:19 web.1  | Oct 15, 2012 8:50:19 PM org.apache.catalina.core.StandardEngine startInternal
20:50:19 web.1  | INFO: Starting Servlet Engine: Apache Tomcat/7.0.29
20:50:20 web.1  | Oct 15, 2012 8:50:20 PM de.javakaffee.web.msm.MemcachedSessionService startInternal
20:50:20 web.1  | INFO: MemcachedSessionService starts initialization... (configured nodes definition localhost:11211, failover nodes null)
20:50:20 web.1  | 2012-10-15 20:50:20.385 INFO net.spy.memcached.MemcachedConnection:  Added {QA sa=localhost/127.0.0.1:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0} to connect queue
20:50:20 web.1  | 2012-10-15 20:50:20.391 INFO net.spy.memcached.MemcachedConnection:  Connection state changed for sun.nio.ch.SelectionKeyImpl@19c8ef56
20:50:20 web.1  | Oct 15, 2012 8:50:20 PM de.javakaffee.web.msm.RequestTrackingHostValve <init>
20:50:20 web.1  | INFO: Setting ignorePattern to .*\.(png|gif|jpg|css|js)$
20:50:20 web.1  | Oct 15, 2012 8:50:20 PM de.javakaffee.web.msm.MemcachedSessionService setLockingMode
20:50:20 web.1  | INFO: Setting lockingMode to NONE
20:50:20 web.1  | Oct 15, 2012 8:50:20 PM de.javakaffee.web.msm.MemcachedSessionService createTranscoderFactory
20:50:20 web.1  | INFO: Creating transcoder factory de.javakaffee.web.msm.JavaSerializationTranscoderFactory
20:50:20 web.1  | Oct 15, 2012 8:50:20 PM de.javakaffee.web.msm.MemcachedSessionService startInternal
20:50:20 web.1  | INFO: MemcachedSessionService finished initialization, sticky false, operation timeout 1000, with node ids [] and failover node ids []
20:50:20 web.1  | Oct 15, 2012 8:50:20 PM org.apache.coyote.AbstractProtocol start
20:50:20 web.1  | INFO: Starting ProtocolHandler ["http-bio-5000"]

If you open a browser and go to http://localhost:5000/ you should see your beautiful application running.

Now, commit these files

1
2
3
$ git init
$ git add .
$ git commit -m init

Create the application on heroku;

1
2
3
4
$ heroku create --stack cedar
Creating powerful-caverns-6988... done, stack is cedar
http://powerful-caverns-6988.herokuapp.com/ | git@heroku.com:powerful-caverns-6988.git
Git remote heroku added

add the memcache add-on;

1
2
$ heroku addons:add memcache
----> Adding memcache to powerful-caverns-6988... done, v2 (free)

And then you deploy by pushing the code to heroku.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
$ git push heroku master
Counting objects: 13, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (13/13), 1.66 KiB, done.
Total 13 (delta 0), reused 0 (delta 0)

-----> Heroku receiving push
-----> Java app detected
-----> Installing OpenJDK 1.6...done
-----> Installing Maven 3.0.3... done
-----> Installing settings.xml... done
-----> executing /app/tmp/repo.git/.cache/.maven/bin/mvn -B -Duser.home=/tmp/build_2y9sgtj8cijrj -Dmaven.repo.local=/app/tmp/repo.git/.cache/.m2/repository -s /app/tmp/repo.git/.cache/.m2/settings.xml -DskipTests=true clean install
       [INFO] Scanning for projects...
       [INFO]
       [INFO] ------------------------------------------------------------------------
       [INFO] Building helloworld 1.0.0-SNAPSHOT
       [INFO] ------------------------------------------------------------------------
     ...
       [INFO] ------------------------------------------------------------------------
       [INFO] BUILD SUCCESS
       [INFO] ------------------------------------------------------------------------
       [INFO] Total time: 37.211s
       [INFO] Finished at: Mon Oct 15 20:14:05 UTC 2012
       [INFO] Final Memory: 14M/490M
       [INFO] ------------------------------------------------------------------------
-----> Discovering process types
       Procfile declares types -> web
-----> Compiled slug size: 55.1MB
-----> Launching... done, v7
       http://powerful-caverns-6988.herokuapp.com deployed to Heroku

To git@heroku.com:powerful-caverns-6988.git
 * [new branch]      master -> master

Your application is now up and running at the URLG given when you created the application. If you don’t like the generated url, don’t worry, you can change it later from the heroku website.

A more complete example can be found here.