A Play Framework server setup

What a wonderful surprise was that Play 2.0.x can't be deployed to application servers. Yes, there's a plugin, but I'm not really fond on depending in some guy's project for my business critical applications and the generated WAR wasn't really helpful since you still need to run a different instance of your application server for each Play application because you can only deploy to root ("/") and not as a specific path ("/myapp/").

Netty, although I don't get the whole .io domains thing, is what the guys at Play decided to use, so, for those of you who use Apache HTTP you can drop the AJP plugin and install the proxy_mod and proxy_http_mod.

When setting up a server, be it Tomcat, JBoss or Glassfish, my main target is flexibility and ease of configuration. I hate having to modify 30 different logger.xml files because some guy doesn't know about symlinks, or having to perform 30 manual operations to perform a release. Also, the start/stop process has to be as easy and centralized as possible since you never know who is the guy restarting your cluster at 4:00am is. Finally, deployments have to be as light and fast as possible.

With those objectives in mind, this is what I came up with:

$tree /home/play
|-- bin
| |-- all        
(script to start/stop all applications)
| |-- play       (script to start/stop the foo applications)

| |-- deploy     (script to deploy the applicationa)
|-- etc
| |-- application.conf (common Play configuration file used by all applications)

| `-- logger.xml       (common Logger configuration file used by all apps)

|-- lib
|   |-- 2.0.2 
|   |    |-- akka-actor.jar
|   |    |-- akka-slf4j.jar
|   |    |-- ...
|   |    `-- xml-apis.jar
|   |-- 2.0.3

|   |    |-- akka-actor.jar
|   |    |-- akka-slf4j.jar
|   |    |-- ...
|   |    `-- xml-apis.jar|   
    `-- play -> lib
|-- logs (Folder for log files from the different applications)
|   |-- foo.log
|   |-- bar.log
|   `-- api.log
`-- webapps (Folder which contains our applications)
    |-- bar
    | |-- 0.1-SNAPSHOT    
    | | |-- joda-time-2.1.jar    
    | | |-- foo_2.9.1-1.0-SNAPSHOT.jar    
    | | |-- foo-common-0.1-SNAPSHOT.jar
    | `-- current -> 0.1-SNAPSHOT
    |-- api    
    | |-- 0.1-SNAPSHOT
    | | |-- joda-time-2.1.jar    
    | | |-- foo-api_2.9.1-1.0-SNAPSHOT.jar    
    | | |-- foo-common-0.1-SNAPSHOT.jar
    | `-- current -> 0.1-SNAPSHOT    
    `-- foo      
      |-- 0.1-SNAPSHOT
      | |-- joda-time-2.1.jar      
      | |-- foo-common-0.1-SNAPSHOT.jar      
      | |-- foo-bar_2.9.1-1.0-SNAPSHOT.jar
      `-- current -> 0.1-SNAPSHOT

There are two tricky parts here: creating the startup scripts with the correct classpath and separating Play's jar files from yours. Since my projects dependencies were few it was easy to simply copy them over from the target/stage folder. Another solution is to generate an empty Play application, run the stage option and copy the files generated there.

The startup scripts, however you do them, you have to provide the correct options to Java:

This will work with JDK 6 and JDK 7 which are the only ones which support this classpath definitions. Since we're running many Play instances, we need to provide different ports with the http.port options. To configure Play to use our common application.conf (which holds the configuration for the database connection) we use the config.file options. Finally, to provide different logger configurations to Logback we use the logger.file option.

What is accomplished by this setup:

  • Very Unix like structure (bin, logs, etc)
  • Easy deploy (simply replace the current symlink)
  • Easy patching (replace one of your jars and restart)
  • Easy servers control (bash scripts)