Showing posts with label play framework. Show all posts
Showing posts with label play framework. Show all posts

Wednesday, October 30, 2013

Password-protect Play2 Framework Webapp

There are numerous extensions to bring authentication and access control to your Play based website. For example the Deadbolt 2 authorization system lets you define access rights per controller and method. Or you can roll your own, as this step by step guide shows.

What to do if you just want basic http authentication for the whole site? As of today there's no such thing built in. (If this changes, let me know...)

If you're serving your Play pages through Apache as reverse proxy, you're lucky.

Going through a reverse proxy is a good idea anyway:
  • You get the option for load balancing and failover: run multiple instances.
  •  Run multiple Play sites on the same machine, on whatever port number, and expose them all on port 80 to the outside.
 Your apache website definition looks simething like this:
<VirtualHost *:80>
  ServerName my-play-app.example.com
  ProxyPreserveHost on
  <Location />
    ProxyPass http://192.168.56.100:9000/ connectiontimeout=9999999 timeout=9999999
    ProxyPassReverse http://192.168.56.100:9000/

    AuthType Basic
    AuthName "whatever" 
    AuthUserFile /etc/apache2/sites-available/.htpasswd-mysite
    Require valid-user
  </Location>
</VirtualHost>
I'm running my Play app in a virtual machine (192.168.56.100) on standard port 9000. The connection timeout is there so that long running tasks are not aborted by the proxy.

Create the password file as usual:
cd /etc/apache2/sites-available/
htpasswd -c .htpasswd-mysite newuser

And then refresh Apache, and you're done:
service apache2 reload


Wednesday, January 16, 2013

i18n of a Play Framework 2.0.4 Java App / Website

This post shows how I internationalized the social surfing website and app surfr.co that is built with Play. Feel free to take from it what serves you, or comment on what could be done better.

My setup:
  • Play Framework 2.0.4 using Java
  • A standard website where the language code is in the url (example: /en/pagename), and ajax requests where it's not.
  • I'm only using 2-letter language codes like "en", no countries like "en_US".
What you need:
  • Basic knowledge of Play.
  • Small to medium-sized website/app. I don't recommend it for large sites.

Some things are a bit different for Play using Scala, or for Play version 2.1 (which is not released yet). But parts may still be of use for you.

The documenation is at http://www.playframework.org/documentation/2.0.4/JavaI18N

Step 1: Define the languages.

In conf/application.conf add/edit the variable:
application.langs="en,de,ru"

Note:
  • I had to put the values in double-quotes. No quotes as in the documentation failed.
  • Order the languages by priority.

Step 2: Add the texts

As in the documentation create the files conf/messages, conf/messages.de and conf/messages.ru.
Create the files as UTF-8 files so that any text works, not just western. And don't add the BOM because Play 2.0.4 can't handle such files. On Windows you can create the files with Notepad: "save as..." and choose UTF-8. 

Note: 
  • If a text is not translated to a certain language then it always falls back to the default language.
  • Use 2 single quotes instead of 1. Example: Sam''s Pizzaland. Standard Java message file rules apply.
  • It doesn't seem to be supported to separate content by file. All goes into the one messages file. As I said... small/medium sized sites only.

Step 3: Figure out that the default handling isn't suitable

Now you're ready to use the texts in Java code and in templates. 
Messages.get("my.message.key")
Messages.get(new Lang("en"), "my.message.key")
The problem: Either you pass the user's language around everywhere (no way), or you settle with the built-in language selection (maybe). That is: using the first language that the user's browser settings mention for which you have translations.
For me, that was not acceptable. My website lets the user change the language. Unfortunately, Play does not offer a simple way to overwrite the lookup of the default language. I read that it will be supported in version 2.1, and that there are easier ways for overriding in the Scala version. So here's what I did.

Step 4: Implement language selection and lookup

I intercept each request to figure out the best language.

Create a new packageless (ouch) class named Global (ouch).
public class Global extends GlobalSettings {
    @Override
    public Action onRequest(final Request request, Method actionMethod) {
        I18n.defineBestLang(request);
        return super.onRequest(request, actionMethod);
    }
}

Note: it should theoretically be possible to name the class differently and put it into a package by configuring application.global= in the application.conf file, but it did not work for me.

The defineBestLang() method goes by this priority:
  1. Read from the url. My web urls (not ajax urls) contain the language in the form of /en/pagename.
  2. Read from cookie. For the app I'm using a 'lang' cookie.
  3. The browser's language settings, just like default Play. Something like Lang.preferred(request.acceptLanguages())
  4. Hardcoded default language.

It then stores the information in a ThreadLocal. Yes it works, I'm using just 1 thread per request. You can also use it as a start and pass it around where needed.

At this point the language is always available in controllers, templates, services. 
My I18n class also has static methods for getting the language code, and String and Html text for a certain key for the "best" language.
In order to not include that I18n class in all templates, I added it to the Build.scala file: 
templatesImport += "mypackage.I18n"

Step 5: Use the text

In a template I can now instead of @Messages.get("my.message.key") just use @I18n.text("my.message.key") for a pure text or @I18n.html("my.message.key") for html formatting.

Step 6: Routing

The front page has 2 entries in the routes file. For example the front page:
GET     /                             controllers.ControllerFactory.getApplication.index()
GET     /$lang<[a-z]{2}>   controllers.ControllerFactory.getApplication.indexI18n(lang:String)

public Result index() {
    return i18nRedirect();
}
public Result indexI18n(String lang) {
    return ok(views.html.Surfrco.index.render());
}
protected Result i18nRedirect() {
    return redirect("/"+ I18n.code()+request().uri());
}

The guest's language is auto-detected, and he's redirected to his language. This way I'm not serving duplicate content on multiple URLs. Currently I'm doing the same with other content pages (2 routes, with and without language) but it's not really necessary as long as no one links there.

Step 7: Client side i18n

My server-side messages file contains all texts as used in Java code, plus static webpage content. The client side only needs a couple phrases in JavaScript. That's why I've decided against streaming the whole messages file to the client. Instead I've created 3 files (UTF-8 again) messages.en.js etc. and serve only the one to the client:

The file's content is of the form:
function initMessages(m) {
m['my.text.key']="My Text";
...
}
if (typeof srfMessages == 'undefined') srfMessages = {};
initMessages(srfMessages);

And elsewhere is a very simple function to retrieve texts:

function t(key) {
    return srfMessages[key];
}

Note that in contrast to the server side, there is no fallback here to the default language.

And that's how i18n works for surfr.co.

Friday, November 16, 2012

Play framework: develop with custom domain and port

What it's about

This howto shows you how to use a custom host name and port number when developing with the play framework v2.

Why would you do that? 

In my case it's because I want the development environment to be as close to the final production environment as it can be.

Assumptions

You have play v2 installed, you start it command line, and you're developing with remote debugging in IntelliJ or Eclipse or whatever.
Your desired host name is example.com with port 80.

Steps

If your host name is not in the dns then add a hosts entry. It must point to your local ip address (such as 192.168.1.1), not to the loopback address 127.0.0.1.


By default, Skype listens on port 80 for incoming connections. Either shut down Skype completely for the time of development, or configure it to NOT listen on port 80. Just shutting it down for the moment of starting the embedded play server (Netty) isn't enough, because after a while Skype gets a hold if the port again.









In the console, type "play debug" as usual. But this time run with "run 80 -Dhttp.address=example.com".

Test it in the browser.

Final notes

There is no configuration within play for the port or host. If you deploy your app then you probably have that kind of config in Tomcat or so.

More info:
http://www.playframework.org/documentation/2.0.4/ProductionConfiguration
http://www.playframework.org/documentation/2.0.4/HTTPServer

As of now (version 2.0.4) running on a certain port (such as run 80) does not complain when another application or service is occupying that port already.