I gave a talk on Play in Practice at the SF Scala meetup recently. Thanks to Stackmob for hosting us and providing pizza.
I went into describing how to implementing CQRS in Play, but there was a fairly long question and answer section about Play as well. I couldn't go into detail on some of the answers and missed some others, so I'll fill in the details here.
Video
Slides
Core API
The core API is Action, which take in a
Request
and return a Result
. The Request
is immutable, but you can
wrap it with extra
information, which you'll typically do with action composition.
2.1.1 introduced EssentialAction, which uses (RequestHeader => Iteratee[Array[Byte], Result])
instead of Action's
(Request => Result)
and makes building Filters
easier.
Again, Play's core is simple. About as simple as you can get.
Streaming
Streaming is handled by Iteratees, which can be a confusing topic for many people. There are good writeups here and here. lila is the best application to look at for streaming, especially for sockets and hubs.
Having good streaming primitives is something that I didn't get into that much in the talk, but is still vitally important to "real time web" stuff.
Filters
If you want to do anything that you'd consider as part of a "servlet pipeline", you use Filters, which are designed to work with streams.
An example of a good Filter is to automatically uncompress an asset – here's an example that uses an Enumeratee:
class GunzipFilter extends EssentialFilter {
def apply(next: EssentialAction) = new EssentialAction {
def apply(request: RequestHeader) = {
if (request.headers.get("Content-Encoding").exists(_ == "gzip")) {
Gzip.gunzip() &>> next(request)
} else {
next(request)
}
}
}
}
Note that this only does uncompression: Automatic streaming gzip compression of templates is not available "out of the box" in 2.1.2, but it should be available in Play 2.2.
Templating
Play comes packaged with its own template language, Twirl, but you're not required to use it. There is an integration into Scalate that gives you Mustache, Jade, Scaml and SSP. There's also an example project that shows how to integrate Play with Freemarker.
One thing that Play doesn't address directly is how to set up a structure for page layouts. Play provides you with index.scala.html and main.scala.html, but doesn't provide you with any more structure than that. If you set up a header and footer and allow for subdirectories to use their own templates, you can minimize the amount of confusion in the views.
There's an example in RememberMe, and this is the approach that lila takes as well.
Another thing is that Play's default project template is intentionally minimal. If you use Backbone and HTML5 templates, then a custom giter8 template like mprihoda/play-scala may suit you better.
JSON
Play's JSON API is very well done, and is a great way to pass data around without getting into the weeds or having to resort to XML. It goes very well with case classes.
The documentation isn't bad, but Pascal Voitot (the author of play-json) has a series of blog posts that go the extra mile: reading JSON with JsPath, writing JSON formats, transforming JSON, and even defining JSON macros.
Forms
Form handling is one of those things that is never intuitive for me. The documentation helps, but really if you want to know how to do validation, using the sample forms application is the best way to pick things up. There are many useful nuggets that aren't explicitly discussed in the documentation. In particular, the ability to make custom constraints is extremely useful.
Routing
There's only one routing API replacement that I know of, Play Navigator, a routing DSL for REST services. However, you can use custom data types in the routing table using QueryStringBindable and PathBindable, and save yourself some "string2foo" conversion.
Asynchronous Operation
Talking about Akka (and the other async code) in Play is tricky for a couple of reasons.
The first reason is that "async" involves a number of different concepts, all of which are complex and worthy of blog posts in themselves. Sadek Drobi gives a nice overview, and there's an exhaustive mailing list discussion about the asynchronous code in Play works.
The second bit of trickiness is that Play 2.0 and Play 2.1 async features do not work in the quite the same way.
Play 2.0 uses Akka for almost everything internally.
Play 2.1 does not use Akka to handle incoming requests, or iteratees, or internal code. It uses scala.concurrent.Future
instead with its own thread pools.
Play 2.1 also uses a default thread pool, which is Akka backed – ActorSystem("play")
–
and is used for the application code, i.e. the stuff inside Action
.
This is important, because blog posts like James Ward's Optimizing Play 2 for Database Driven Apps are only applicable to Play 2.0, not 2.1. For 2.1, use the thread pools documentation.
In addition to the "play" actor system, there's a Play Akka plugin. The Akka plugin is actually packaged with
Play itself, and you can find it under play.api.libs.concurrent.Akka
.
So, if Play already uses Akka under the hood, then why define an Akka plugin?
I believe it's because the Akka plugin defines a distinct ActorSystem("application")
that can be used for backend
tasks like sending email, and can be configured without impacting the "play" ActorSystem. The Akka plugin provides a
useful default and enforces seperation between Play's actors and the application's actors.
CQRS
Given that most of the CQRS talks I've read have been from the enterprise perspective, it was nice to talk about CQRS in the context of functional programming and statelessness.
Message passing is something that is typically mentioned in inter process communication, or in message oriented middleware. Akka – a message passing architecture on the thread level – allows us to build "zero coupling" systems . As message passing patterns, CQRS and DDD are a good set of idioms to think about domain logic together, especially since they already assume eventual consistency and indeterminate time.
Authentication
If you're using Scala, there are two good authentication options, RememberMe (ahem) and SecureSocial. SecureSocial has better documentation and has been around longer, but RememberMe has better security resistance to some attacks. I'm working to integrate RememberMe's functionality into SecureSocial, but you'll want to check out both of them.
There's also a pure Java authentication option: Play Authenticate. I haven't used this, but the code looks reasonable.
If you'd rather go it alone or need a basic starter application, you may find Play20StartApp useful (password reset, account confirmation, etc.)
Authorization
Deadbolt 2 is the best known authorization framework. You can use things like Shiro, but you're better off with something specifically designed for Play.
Security
Play does fairly well on security compared to other frameworks. For example, it will set a CORS header to protect against clickjacking, will sign the session cookie with an HMAC to protect against broken authentication, supports SSL, etc.
However, there are some things that Play doesn't do.
Play doesn't encrypt the session cookie, so you shouldn't store any sensitive information in there.
Play won't protect you from replay attacks, as Play is stateless by default. You can specify a nonce or request counter to counteract this, and RememberMe uses a token based approach for persistent login cookies.
Play won't protect you against injection attacks. You can specify value classes to validate your input against raw strings.
Play won't protect you against security misconfiguration. You should have a release checklist.
Play won't protect you from insecure cryptography practices. Education helps, but there's a lot of misinformation out there as well; watch this video (and slides) and be wary of things you read on Stack Overflow and Hacker News.
Play won't protect you from failure to restrict URL access; that's up to the authorization framework.
Play does have cross site request forgery protection, but it will only be effective if you enable the filter and explicitly pass the CSRF helper function in through every single form. There is an authenticity token approach as well, though I haven't used it.
Most importantly, Play won't tell you about how web application security fails. I recommend The Tangled Web as an excellent overview on how web applications are stitched together out of different technologies, and how to secure them.
Logging
The underlying logger for Play is Logback. Logback is one of the few hardcoded dependencies in Play, which has caused some issues. Fortunately, Play uses Logback through the SLF4J logging API, but there's no option built into Play to allow Logback to be swapped out easily. There are reports of people swapping out Logback for other logging frameworks, but I haven't tried them.
There have also been issues with the logging configuration conflicting in places or being unclear. One thing that has
tripped people up repeatedly is that all the logging configuration
must be done in one place. You can't
have some logging configuration in application.conf
and some configuration in logger.xml.
While Play uses SLF4J under the hood, it doesn't expose SLF4J functionality in play.api.Logger
. In fact,
there are only two method signatures for logging:
def error(message: => String) : Unit
def error(message: => String, error: => Throwable) : Unit
This doesn't really cover the way I like to log, and it doesn't provide even the features that are available in SLF4J, such as parameterized logging. My own answer was to ignore the Play logging API entirely and write a Logging wrapper directly against SLF4J (with kestrel combinators, natch), but you may want to use something out of the box.
For example, Typesafe Logging, uses SLF4J and provides you with this:
def error(message: String): Unit
def error(message: String, params: AnyRef*): Unit
def error(message: String, t: Throwable): Unit
def error(marker: Marker, message: String): Unit
def error(marker: Marker, message: String, params: AnyRef*): Unit
def error(marker: Marker, message: String, t: Throwable): Unit
Or you can use loglady, which uses the Python API style with printf syntax:
def error(message: String, params: Any*) : Unit
def error(thrown: Throwable, message: String, params: Any*) : Unit
WAR packaging
I said in the Q&A that I didn't think you could package Play 2 applications as WAR files. Well, it turns out that there is a plugin available, and it works with Servlet 3.0 and 2.5 containers (Tomcat 6/7, Jetty 7/8/9, JBoss 5/6/7, etc). You may need to tweak the logger to work in the container correctly.
I don't know how Play's performance is affected by running inside a servlet container; let me know if it works for you.
Asset Packaging
Javascript assets in Play are minified using Google Closure – this happens automatically on play dist
. They also can be gzipped using a custom SBT script.
This makes a good enough solution for most people. If you are really intent on minimizing your asset overhead, you should consider putting your assets on a static file server backed by HAProxy, or putting them on CDN.
Email is one of those things that I think should be divorced as much as possible from Play. It's backend and async by nature, and this makes it something that is best handled through Akka.
akka-email is available on Github and gives you a starting place to build up a message passing infrastructure for email.
Metrics
Instrumenting applications is important. Sadly, every metrics solution has its own API, so you can't easily switch between them. However, there's no shortage of options.
- New Relic recently came out with support for Play 2.
- Ostrich, the Twitter metrics library.
- Metrics, with the metrics-scala from Erik Van Oosten, cross-compiled for multiple versions. This is what I use.
- Pillage, which has a Scala option (I have not tried this).
- statsd module for Play 2.
The Typesafe Console is the best monitoring tool to use if you are using Akka, but that depends on having a Typesafe subscription if you want to use it in production.
Load and Stress Testing
Determining a load plan is hard, and involves some amount of educated guessing. Fortunately, most applications simply don't get that much load, even ones you'd think would be busy.
Gatling and wrk are good ways of stressing a system, but they don't reflect normal user behavior. Apache JMeter is very good at modelling random user behavior, but is clunky. A good and arguably the most realistic load test is to hire a couple of hundred users from Mechanical Turk to pound on the site at once, but this may not be very convenient.
Deployment
There are a number of different ways to deploy Play projects. Using play dist
gets you most of the way, but you may want to deploy with Ansible or Chef or Fabric. Or you can use upstart or even git hooks.
If you just want to push changes to a staging server as they happen, you can do this with rsync -avz --delete -e ssh $deployed_code staging:/opt/play-app
, although this isn't so great for production.
Java Support
The Java and Scala APIs are very similar. However, there are a couple of notable differences, which come out of Java's lack of closure support:
- The Java API does not support Iteratees.
- The Java API does not have an implicit execution context.
The play.libs.F
library goes a fair way to providing Scala's functional programming constructs in Java.
More?
If you have suggestions or want to point something out, please email me at [email protected], and I'll fill out this post with more details.
Comments