playlogo

I was having a strange issue with localization in a Play Framework application. I followed the simple steps detailed on the official documentation but with no luck.

When experimenting, I figured out that the locale used is the default locale of the JVM. In my case, my default locale is French so I only had French in my application. But on Heroku, the default locale is English and thus the application was only working in English.

The trick consists in adding an implicit lang to your template views. In fact, Scala import an implicit lang with the lowest priority being the one coming from the JVM, if you want to get the language parsed from the Accept-Languages http header, you need to add an implicit as below :

@(title: String)(content: Html)(implicit lang: Lang)

With this little trick, your calls to localization will use the locale extracted from the http request as expected.

Sorry english readers, the following post is only available in french !

tl;dr : application Play (java ou scala) sur un serveur Centos (ou Red Hat) derrière un serveur nginx.

Script de démarrage/arrêt du daemon.

Lancer une application Play en dev est assez aisé : $ play run.

Maintenant, si vous avez besoin de déployer votre application en production, il est plus confortable de considérer votre application comme un service à part entière du système. Il se peut aussi que vous ayez besoin de spécifier un port et une interface particulière pour pouvoir utiliser nginx comme front-end (en reverse proxy). Et évidement, à l'instar d'un environnement de développement, vous pourriez avoir besoin de compresser le JS et le CSS, et également de régler finement les en-têtes HTTP pour gérer correctement le cache.

Tout d'abord le script de lancement du daemon :

Je me suis inspiré des deux gists ci-dessous. Le premier, pour RedHat, utilise le binaire de play pour lancer le processus principal. Cette méthode facile à utiliser en développement semble assez limité en paramètre (tout du moins, très peu documentée). La seconde méthode, pour Debian, donne un bonne exemple d'une configuration un peu plus élaborée de l'exécution du daemon. Les paramètres en début de fichier parlent d'eux-mêmes, je vous laisse les découvrir et reste à votre disposition pour toute question.

Source :

Configuration de nginx

Sur un serveur web, il est souvent pratique de pouvoir faire tourner plusieurs applications utilisant des langages, des librairies, des frameworks, etc... hétérogènes (sur mon serveur, il y a l'application Play en scala, une application Nodejs en Coffescript et une application PHP/MySQL utilisant Apache). C'est là que nginx intervient. Configuré en reverse-proxy, c'est lui qui écoutera sur le port 80 de votre serveur et en fonction du nom de domaine relayera les requêtes HTTP à l'application que vous avez choisis. Il vous suffit de configurer votre application en écoute sur un port libre en localhost et d'indiquer à nginx qu'en fonction du nom de domaine (à la manière des VirtualHost d'Apache), il doit transmettre ses requêtes sur ce port.

Vous noterez une section fournissant un traitement particulier pour les fichiers statiques (js, css, png, jpg, etc...). En effet, je ne trouvais pas la configuration par défaut de play très satisfaisante pour diffuser ces fichiers. Il conviendra donc de copier tous les fichiers dans un dossier particulier au lancement de l'application (il faut également pensé à y placer les fichiers "compilé" des scripts utilisant un pre-processeurs : coffeescript, SASS/SCSS, LESS, etc...). Tous les fichiers dans ce dossier auront une durée de cache maximale afin de minimiser le nombre de requêtes au serveur (la requête la plus rapide est celle qui n'est pas faite). Cette technique a aussi l'avantage de décharger la JVM du traitement de ses fichiers, ils sont directement retourné par nginx.

Compilation des fichiers statiques

Les pré-processeurs (Coffeescript, SASS, LESS, etc...) sont d'un grand confort pour le développement de la partie front-end d'une application web. Toutefois, il y a quelques détails qui ne faut pas négliger afin d'optimiser au maximum la distribution en HTTP de ces fichiers.

Prenons comme exemple un fichier .coffee (Coffeescript). Lorsqu'en développement vous lancer votre serveur, play se charge de re-compiler automatiquement le fichier afin de desservir un fichier javascript au navigateur qui a demandé le fichier. Dans notre environnement, les fichiers statiques se trouvent tous dans un dossier static/. Il faut donc "traduire" ce fichier en javascript et le placer dans ce dossier (en respectant l'arborescence utilisée en développement) : coffee -o static/javascripts/ -c app/assets/javascripts/*.coffee.

Ensuite, comme optimisation souvent préconnisée, il convient de "minifier" ces fichiers afin qu'ils soient le plus léger possible pendant le transport sur le réseau (qui peut-être une faible connexion 3g). Dans mon cas, j'ai utilisé yuicompressor qui fonctionne bien. Il en existe certainement des mieux, je n'ai pas testé, mais celui-là fonctionne comme je veux.

Note : comme amélioration possible, il faudrait dans cette partie là renommer les fichiers par un nom de fichier contenant un hash représentant le contenu de ce fichier afin de palier à tout problème d'invalidation du cache.

Dans le futur

Pour parfaire le déploiement de l'application, sur un VCS git par exemple, il serait pratique d'automatiser le redémarrage du serveur sur un hook post-commit. Pour éviter également une coupure de service, il faudrait également, lancer la nouvelle application sur un autre port et switcher sur la nouvelle instance uniquement avec un reload de nginx. Mais bon tout ça peut faire l'objet d'un autre article. :)

Mise à jour du 29 juillet 2013 : correction du script de lancement du daemon.

MBP Keyboard

I am actually working on a little side project with my friend Thomas Debarochez. We are using Play!Framework and Compass and we deploy it to Heroku.

One issue that came when we first deployed the application with Compass to Heroku was that the default scala buildpack does not include sass and compass binaries.

Following this post on the Play!Framework mailing list, I forked and patched the buildpack to install sass and compass on the environment.

You can find it here and use it with the following command :

heroku config:set BUILDPACK_URL=https://github.com/CedricGatay/heroku-buildpack-scala

I hope someone will find this useful !

Wicket

At Code-Troopers, we like to work with the frameworks we love. One of them is Apache Wicket, and it happens to some of us doing some Play! Framework or Ruby on Rails (either for a client project, a side project or giving back OSS love).

One thing really great is this two frameworks is the central route system, one file allows to group all the routes handled by your application. Wicket does not provide such way of grouping routes, you can manually mount routes into your application or annotate your page classes.

Wicket routes mount library

This small library project available on Github allows to group mounts into a central file. To use it, simply add its dependency to your pom.xml (artifact is available on Maven central):

<dependency>
    <groupId>com.code-troopers</groupId>
    <artifactId>wicket-route-mount</artifactId>
    <version>0.1</version>
</dependency>
This dependency will transitively gets wicket-auth-roles (if there is a special need for a version without this dependency, it could be done easily).

Usage

To use it, simply create a routes.conf file at the root of the sources in your project (typically src/main/resources/) respecting the following format :

# mountPoint        class                           roles
/home               codetroopers.HomePage           
/secured            codetroopers.SecuredPage        USER
/user/${mode}/#{id} codetroopers.UserPage           ADMIN,USER

The files content is the following :

  • Mount path : using standard Wicket syntax (${requiredParam} and #{optionalParam} are available)
  • Page class : fully qualified name of the page class to mount
  • Roles (optional) : comma separated list of roles required to access the page
IntelliJ IDEA can do completion for class names in this file (you just need to hit the ctrl+space shortcut twice)