Play Framework: Data Validation Using Controllers

Exclusive offer: get 50% off this eBook here
Play Framework Cookbook

Play Framework Cookbook — Save 50%

Over 60 incredibly effective recipes to take you under the hood and leverage advanced concepts of the Play framework

$26.99    $13.50
by Alexander Reelsen | July 2011 | Java Open Source

The Play framework is the new kid on the block of Java frameworks. By breaking the existing standards it tries not to abstract away from HTTP as with most web frameworks, but tightly integrates with it. This means quite a shift for Java programmers. Understanding the concepts behind this shift and its impact on web development with Java are crucial for fast development of Java web applications.

In this article by Alexander Reelsen, author of Play Framework Cookbook, we will cover:

  • URL routing using annotation-based configuration
  • Basics of caching
  • Using HTTP digest authentication
  • Generating PDFs in your controllers

 

Play Framework Cookbook

Play Framework Cookbook

Over 60 incredibly effective recipes to take you under the hood and leverage advanced concepts of the Play framework

        Read more about this book      

(For more resources related to this subject, see here.)

Introduction

This article will help you to keep your controllers as clean as possible, with a well defined boundary to your model classes. Always remember that controllers are really only a thin layer to ensure that your data from the outside world is valid before handing it over to your models, or something needs to be specifically adapted to HTTP.

URL routing using annotation-based configuration

If you do not like the routes file, you can also describe your routes programmatically by adding annotations to your controllers. This has the advantage of not having any additional config file, but also poses the problem of your URLs being dispersed in your code.

You can find the source code of this example in the examples/chapter2/annotationcontroller directory.

How to do it...

Go to your project and install the router module via conf/dependencies.yml:

require:
- play
- play -> router head

Then run playdeps and the router module should be installed in the modules/ directory of your application. Change your controller like this:

@StaticRoutes({
@ServeStatic(value="/public/", directory="public")
})
public class Application extends Controller {

@Any(value="/", priority=100)
public static void index() {
forbidden("Reserved for administrator");
}

@Put(value="/", priority=2, accept="application/json")
public static void hiddenIndex() {
renderText("Secret news here");
}

@Post("/ticket")
public static void getTicket(String username, String password) {
String uuid = UUID.randomUUID().toString();
renderJSON(uuid);
}
}

How it works...

Installing and enabling the module should not leave any open questions for you at this point. As you can see in the controller, it is now filled with annotations that resemble the entries in the routes.conf file, which you could possibly have deleted by now for this example. However, then your application will not start, so you have to have an empty file at least.

The @ServeStatic annotation replaces the static command in the routes file. The @StaticRoutes annotation is just used for grouping several @ServeStatic annotations and could be left out in this example.

Each controller call now has to have an annotation in order to be reachable. The name of the annotation is the HTTP method, or @Any, if it should match all HTTP methods. Its only mandatory parameter is the value, which resembles the URI—the second field in the routes. conf. All other parameters are optional. Especially interesting is the priority parameter, which can be used to give certain methods precedence. This allows a lower prioritized catchall controller like in the preceding example, but a special handling is required if the URI is called with the PUT method. You can easily check the correct behavior by using curl, a very practical command line HTTP client:

curl -v localhost:9000/

This command should give you a result similar to this:

> GET / HTTP/1.1
> User-Agent: curl/7.21.0 (i686-pc-linux-gnu) libcurl/7.21.0
OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.18
> Host: localhost:9000
> Accept: */*
>

< HTTP/1.1 403 Forbidden
< Server: Play! Framework;1.1;dev
< Content-Type: text/html; charset=utf-8
< Set-Cookie: PLAY_FLASH=;Path=/
< Set-Cookie: PLAY_ERRORS=;Path=/
< Set-Cookie: PLAY_SESSION=0c7df945a5375480993f51914804284a3bb
ca726-%00___ID%3A70963572-b0fc-4c8c-b8d5-871cb842c5a2%00;Path=/
< Cache-Control: no-cache
< Content-Length: 32
<

<h1>Reserved for administrator</h1>

You can see the HTTP error message and the content returned. You can trigger a PUT request in a similar fashion:

curl -X PUT -v localhost:9000/

> PUT / HTTP/1.1
> User-Agent: curl/7.21.0 (i686-pc-linux-gnu) libcurl/7.21.0
OpenSSL/0.9.8o zlib/1.2.3.4 libidn/1.18
> Host: localhost:9000
> Accept: */*
>

< HTTP/1.1 200 OK
< Server: Play! Framework;1.1;dev
< Content-Type: text/plain; charset=utf-8
< Set-Cookie: PLAY_FLASH=;Path=/
< Set-Cookie: PLAY_ERRORS=;Path=/
< Set-Cookie: PLAY_SESSION=f0cb6762afa7c860dde3fe1907e8847347
6e2564-%00___ID%3A6cc88736-20bb-43c1-9d43-42af47728132%00;Path=/
< Cache-Control: no-cache
< Content-Length: 16

Secret news here

As you can see now, the priority has voted the controller method for the PUT method which is chosen and returned.

There's more...

The router module is a small, but handy module, which is perfectly suited to take a first look at modules and to understand how the routing mechanism of the Play framework works at its core. You should take a look at the source if you need to implement custom mechanisms of URL routing.

Mixing the configuration file and annotations is possible

You can use the router module and the routes file—this is needed when using modules as they cannot be specified in annotations. However, keep in mind that this is pretty confusing. You can check out more info about the router module at http://www.playframework.org/modules/router.

Basics of caching

Caching is quite a complex and multi-faceted technique, when implemented correctly. However, implementing caching in your application should not be complex, but rather the mindwork before, where you think about what and when to cache, should be. There are many different aspects, layers, and types (and their combinations) of caching in any web application. This recipe will give a short overview about the different types of caching and how to use them.

You can find the source code of this example in the chapter2/caching-general directory.

Getting ready

First, it is important that you understand where caching can happen—inside and outside of your Play application. So let's start by looking at the caching possibilities of the HTTP protocol. HTTP sometimes looks like a simple protocol, but is tricky in the details. However, it is one of the most proven protocols in the Internet, and thus it is always useful to rely on its functionalities.

HTTP allows the caching of contents by setting specific headers in the response. There are several headers which can be set:

  • Cache-Control: This is a header which must be parsed and used by the client and also all the proxies in between.
  • Last-Modified: This adds a timestamp, explaining when the requested resource had been changed the last time. On the next request the client may send an If-Modified- Since header with this date. Now the server may just return a HTTP 304 code without sending any data back.
  • ETag: An ETag is basically the same as a Last-Modified header, except it has a semantic meaning. It is actually a calculated hash value resembling the resource behind the requested URL instead of a timestamp. This means the server can decide when a resource has changed and when it has not. This could also be used for some type of optimistic locking.

So, this is a type of caching on which the requesting client has some influence on. There are also other forms of caching which are purely on the server side. In most other Java web frameworks, the HttpSession object is a classic example, which belongs to this case.

Play has a cache mechanism on the server side. It should be used to store big session data, in this case any data exceeding the 4KB maximum cookie size. Be aware that there is a semantic difference between a cache and a session. You should not rely on the data being in the cache and thus need to handle cache misses.

You can use the Cache class in your controller and model code. The great thing about it is that it is an abstraction of a concrete cache implementation. If you only use one node for your application, you can use the built-in ehCache for caching. As soon as your application needs more than one node, you can configure a memcached in your application.conf and there is no need to change any of your code.

Furthermore, you can also cache snippets of your templates. For example, there is no need to reload the portal page of a user on every request when you can cache it for 10 minutes.

This also leads to a very simple truth. Caching gives you a lot of speed and might even lower your database load in some cases, but it is not free. Caching means you need RAM, lots of RAM in most cases. So make sure the system you are caching on never needs to swap, otherwise you could read the data from disk anyway. This can be a special problem in cloud deployments, as there are often limitations on available RAM.

The following examples show how to utilize the different caching techniques. We will show four different use cases of caching in the accompanying test. First test:

public class CachingTest extends FunctionalTest {

@Test
public void testThatCachingPagePartsWork() {
Response response = GET("/");
String cachedTime = getCachedTime(response);
assertEquals(getUncachedTime(response), cachedTime);

response = GET("/");
String newCachedTime = getCachedTime(response);
assertNotSame(getUncachedTime(response), newCachedTime);
assertEquals(cachedTime, newCachedTime);
}

@Test
public void testThatCachingWholePageWorks() throws Exception {
Response response = GET("/cacheFor");
String content = getContent(response);
response = GET("/cacheFor");
assertEquals(content, getContent(response));
Thread.sleep(6000);
response = GET("/cacheFor");
assertNotSame(content, getContent(response));
}

@Test
public void testThatCachingHeadersAreSet() {
Response response = GET("/proxyCache");
assertIsOk(response);
assertHeaderEquals("Cache-Control", "max-age=3600", response);
}

@Test
public void testThatEtagCachingWorks() {
Response response = GET("/etagCache/123");
assertIsOk(response);
assertContentEquals("Learn to use etags, dumbass!", response);

Request request = newRequest();

String etag = String.valueOf("123".hashCode());
Header noneMatchHeader = new Header("if-none-match", etag);
request.headers.put("if-none-match", noneMatchHeader);

DateTime ago = new DateTime().minusHours(12);
String agoStr = Utils.getHttpDateFormatter().format(ago.
toDate());
Header modifiedHeader = new Header("if-modified-since",
agoStr);
request.headers.put("if-modified-since", modifiedHeader);

response = GET(request, "/etagCache/123");
assertStatus(304, response);
}

private String getUncachedTime(Response response) {
return getTime(response, 0);
}

private String getCachedTime(Response response) {
return getTime(response, 1);
}

private String getTime(Response response, intpos) {
assertIsOk(response);
String content = getContent(response);
return content.split("\n")[pos];
}
}

The first test checks for a very nice feature. Since play 1.1, you can cache parts of a page, more exactly, parts of a template. This test opens a URL and the page returns the current date and the date of such a cached template part, which is cached for about 10 seconds. In the first request, when the cache is empty, both dates are equal. If you repeat the request, the first date is actual while the second date is the cached one.

The second test puts the whole response in the cache for 5 seconds. In order to ensure that expiration works as well, this test waits for six seconds and retries the request.

The third test ensures that the correct headers for proxy-based caching are set.

The fourth test uses an HTTP ETag for caching. If the If-Modified-Since and If-None- Match headers are not supplied, it returns a string. On adding these headers to the correct ETag (in this case the hashCode from the string 123) and the date from 12 hours before, a 302 Not-Modified response should be returned.

How to do it...

Add four simple routes to the configuration as shown in the following code:


GET / Application.index
GET /cacheFor Application.indexCacheFor
GET /proxyCache Application.proxyCache
GET /etagCache/{name} Application.etagCache

The application class features the following controllers:

public class Application extends Controller {

public static void index() {
Date date = new Date();
render(date);
}

@CacheFor("5s")
public static void indexCacheFor() {
Date date = new Date();
renderText("Current time is: " + date);
}

public static void proxyCache() {
response.cacheFor("1h");
renderText("Foo");
}
@Inject
private static EtagCacheCalculator calculator;

public static void etagCache(String name) {
Date lastModified = new DateTime().minusDays(1).toDate();
String etag = calculator.calculate(name);
if(!request.isModified(etag, lastModified.getTime())) {
throw new NotModified();
}
response.cacheFor(etag, "3h", lastModified.getTime());
renderText("Learn to use etags, dumbass!");
}
}

As you can see in the controller, the class to calculate ETags is injected into the controller. This is done on startup with a small job as shown in the following code:

@OnApplicationStart
public class InjectionJob extends Job implements BeanSource {

private Map<Class, Object>clazzMap = new HashMap<Class, Object>();

public void doJob() {
clazzMap.put(EtagCacheCalculator.class, new
EtagCacheCalculator());
Injector.inject(this);
}

public <T> T getBeanOfType(Class<T>clazz) {
return (T) clazzMap.get(clazz);
}
}

The calculator itself is as simple as possible:

public class EtagCacheCalculator implements ControllerSupport {

public String calculate(String str) {
return String.valueOf(str.hashCode());
}
}

The last piece needed is the template of the index() controller, which looks like this:

Current time is: ${date}
#{cache 'mainPage', for:'5s'}
Current time is: ${date}
#{/cache}

How it works...

Let's check the functionality per controller call. The index() controller has no special treatment inside the controller. The current date is put into the template and that's it. However, the caching logic is in the template here because not the whole, but only a part of the returned data should be cached, and for that a #{cache} tag used. The tag requires two arguments to be passed. The for parameter allows you to set the expiry out of the cache, while the first parameter defines the key used inside the cache. This allows pretty interesting things. Whenever you are in a page where something is exclusively rendered for a user (like his portal entry page), you could cache it with a key, which includes the user name or the session ID, like this:

#{cache 'home-' + connectedUser.email, for:'15min'}
${user.name}
#{/cache}

This kind of caching is completely transparent to the user, as it exclusively happens on the server side. The same applies for the indexCacheFor() controller. Here, the whole page gets cached instead of parts inside the template. This is a pretty good fit for nonpersonalized, high performance delivery of pages, which often are only a very small portion of your application. However, you already have to think about caching before. If you do a time consuming JPA calculation, and then reuse the cache result in the template, you have still wasted CPU cycles and just saved some rendering time.

The third controller call proxyCache() is actually the most simple of all. It just sets the proxy expire header called Cache-Control. It is optional to set this in your code, because your Play is configured to set it as well when the http.cacheControl parameter in your application.conf is set. Be aware that this works only in production, and not in development mode.

The most complex controller is the last one. The first action is to find out the last modified date of the data you want to return. In this case it is 24 hours ago. Then the ETag needs to be created somehow. In this case, the calculator gets a String passed. In a real-world application you would more likely pass the entity and the service would extract some properties of it, which are used to calculate the ETag by using a pretty-much collision-safe hash algorithm. After both values have been calculated, you can check in the request whether the client needs to get new data or may use the old data. This is what happens in the request.isModified() method.

If the client either did not send all required headers or an older timestamp was used, real data is returned; in this case, a simple string advising you to use an ETag the next time. Furthermore, the calculated ETag and a maximum expiry time are also added to the response via response.cacheFor().

A last specialty in the etagCache() controller is the use of the EtagCacheCalculator. The implementation does not matter in this case, except that it must implement the ControllerSupport interface. However, the initialization of the injected class is still worth a mention. If you take a look at the InjectionJob class, you will see the creation of the class in the doJob() method on startup, where it is put into a local map. Also, the Injector.inject() call does the magic of injecting the EtagCacheCalculator instance into the controllers. As a result of implementing the BeanSource interface, the getBeanOfType() method tries to get the corresponding class out of the map. The map actually should ensure that only one instance of this class exists.

There's more...

Caching is deeply integrated into the Play framework as it is built with the HTTP protocol in mind. If you want to find out more about it, you will have to examine core classes of the framework.

More information in the ActionInvoker

If you want to know more details about how the @CacheFor annotation works in Play, you should take a look at the ActionInvoker class inside of it.

Be thoughtful with ETag calculation

Etag calculation is costly, especially if you are calculating more then the last-modified stamp. You should think about performance here. Perhaps it would be useful to calculate the ETag after saving the entity and storing it directly at the entity in the database. It is useful to make some tests if you are using the ETag to ensure high performance. In case you want to know more about ETag functionality, you should read RFC 2616.

You can also disable the creation of ETags totally, if you set http.useETag=false in your application.conf.

Use a plugin instead of a job

The job that implements the BeanSource interface is not a very clean solution to the problem of calling Injector.inject() on start up of an application. It would be better to use a plugin in this case.

Play Framework Cookbook Over 60 incredibly effective recipes to take you under the hood and leverage advanced concepts of the Play framework
Published: August 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources related to this subject, see here.)

Using HTTP digest authentication

As support for HTTP, basic authentication is already built-in with Play. You can easily access request.user and request.password in your controller as using digest authentication is a little bit more complex. To be fair, the whole digest authentication is way more complex.

You can find the source code of this example in the chapter2/digest-auth directory.

Getting ready

Understanding HTTP authentication in general is quite useful, in order to grasp what is done in this recipe. For every HTTP request the client wants to receive a resource by calling a certain URL. The server checks this request and decides whether it should return either the content or an error code and message telling the client to provide needed authentication. Now the client can re-request the URL using the correct credentials and get its content or just do nothing at all.

When using HTTP basic authentication, the client basically just sends some user/password combination with its request and hopes it is correct. The main problem of this approach is the possibility to easily strip the username and password from the request, as there are no protection measures for basic authentication. Most people switch to an SSL-encrypted connection in this case in order to mitigate this problem. While this is perfectly valid (and often needed because of transferring sensitive data), another option is to use HTTP digest authentication. Of course digest authentication does not mean that you cannot use SSL. If all you are worrying about is your password and not the data you are transmitting, digest authentication is just another option.

In basic authentication the user/password combination is sent in almost cleartext over the wire. This means the password does not need to be stored as cleartext on the server side, because it is a case of just comparing the hash value of the password by using MD5 or SHA1. When using digest authentication, only a hash value is sent from client to server. This implies that the client and the server need to store the password in cleartext in order to compute the hash on both sides.

How to do it...

Create a user entity with these fields:

@Entity
public class User extends Model {

public String name;
public String password; // hashed password
public String apiPassword; // cleartext password
}

Create a controller which has a @Before annotation:

public class Application extends Controller {

@Before
static void checkDigestAuth() {
if (!DigestRequest.isAuthorized(request)) {
throw new UnauthorizedDigest("Super Secret Stuff");
}
}

public static void index() {
renderText("The date is " + new Date());
}
}

The controller throws an UnauthorizedDigest exception, which looks like this:

public class UnauthorizedDigest extends Result {

String realm;

public UnauthorizedDigest(String realm) {
this.realm = realm;
}

@Override
public void apply(Request request, Response response) {
response.status = Http.StatusCode.UNAUTHORIZED;
String auth = "Digest realm=" + realm + ", nonce=" +
Codec.UUID();
response.setHeader("WWW-Authenticate", auth);
}
}

The digest request handles the request and checks the authentication:

class DigestRequest {

private Map<String,String>params = new HashMap<String,String>();
private Request request;

public DigestRequest(Request request) {
this.request = request;
}
public booleanisValid() {
...
}

public booleanisAuthorized() {
User user = User.find("byName", params.get("username")).
first();
if (user == null) {
throw new UnauthorizedDigest(params.get("realm"));
}

String digest = createDigest(user.apiPassword);
return digest.equals(params.get("response"));
}

private String createDigest(String pass) {
...
}

public static booleanisAuthorized(Http.Request request) {
DigestRequest req = new DigestRequest(request);
return req.isValid() && req.isAuthorized();
}
}

How it works...

As you can see, all it takes is four classes. The user entity should be pretty clear, as it only exposes three fields, one being a login and two being passwords. This is just to ensure that you should never store a user's master password in cleartext, but use additional passwords if you implement some cleartext password dependant application.

The next step is a controller, which returns a HTTP 403 with the additional information requiring HTTP digest authentication. The method annotated with the Before annotation is always executed before any controller method as this is the perfect place to check for authentication. The code checks whether the request is a valid authenticated request. If this is not the case an exception is thrown. In Play, every Exception which extends from Result actually can return the request or the response.

Taking a look at the UnauthorizedDigest class you will notice that it only changes the HTTP return code and adds the appropriate WWW-Authenticate header. The WWWAuthenticate header differs from the one used with basic authentication in two ways. First it marks the authentication as "Digest", but it also adds a so-called nonce, which should be some random string. This string must be used by the client to create the hash and prevents bruteforce attacks by never sending the same user/password/nonce hash combination.

The heart of this recipe is the DigestRequest class, which actually checks the request for validity and also checks whether the user is allowed to authenticate with the credentials provided or not. Before digging deeper, it is very useful to try the application using curl and observing what the headers look like. Call curl with the following parameters:

curl --digest --user alex:test -v localhost:9000

The response looks like the following (unimportant output and headers have been stripped):

> GET / HTTP/1.1
> Host: localhost:9000
> Accept: */*
>

< HTTP/1.1 401 Unauthorized
< WWW-Authenticate: Digest realm=Super Secret Stuff, nonce=3ef81
305-745c-40b9-97d0-1c601fe262ab
< Content-Length: 0
<
* Connection #0 to host localhost left intact
* Issue another request to this URL: 'HTTP://localhost:9000'

> GET / HTTP/1.1
> Authorization: Digest username="alex", realm="Super Secret Stuff",
nonce="3ef81305-745c-40b9-97d0-1c601fe262ab", uri="/", response="6e
97a12828d940c7dc1ff24dad167d1f"
> Host: localhost:9000
> Accept: */*
>

< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Content-Length: 20
<
This is top secret!

Curl actually issues two requests. The first returns a 403 "not authorized" error, but also the nonce, which is used together with the username and password in the second request to create the response field inside the WWW-Authenticate header. As the client also sends the nonce and the username inside the header, the server can reconstruct the whole response on the server side. This means it is actually stateless and the server does not need to store any data between two requests.

Looking at the DigestRequest class, it is comprised of three core methods: isValid(), isAuthorized(), and createDigest(). The isValid() method checks whether a request contains all the needed data in order to be able to compute and compare the hash. The isAuthorized() method does a database lookup of the user's cleartext password and hands it over to the createDigest method, which computes the response hash and returns true if the computed hash with the local password is the same as the hash sent in the request. If they are not, the authentication has to fail.

The static DigestRequest.isAuthorized() method is a convenient method to keep the code in the controller as short as possible.

There are two fundamental disadvantages in the preceding code snippet. First, it is implementation dependent, because it directly relies on the user entity and the password field of this entity. This is not generic and has to be adapted for each implementation. Secondly, it only implements the absolute minimum subset of HTTP digest authentication. Digest authentication is quite complex if you want to support it with all its variations and options. There are many more options and authentication options, hashing algorithms, and optional fields which have to be supported in order to be RFC-compliant. You should see this only as a minimum starting point to get this going. Also this should not be thought of as secure, because without an additional header called "qop", every client will switch to a less secure mode. You can read more about that in RFC2069 and RFC2617.

There's more...

You can also verify this recipe in your browser by just pointing it to http://localhost:9000/. An authentication window requiring you to enter username and password will popup.

Get more info about HTTP digest authentication

As this recipe has not even covered five percent of the specification, you should definitely read the corresponding RFC at http://tools.ietf.org/html/rfc2617 as well as RFC2069 at http://tools.ietf.org/html/rfc2617.

Generating PDFs in your controllers

Generating binary content is a standard procedure in every web application, be it a dynamic generated image such as a CAPTCHA or user-specific document such as an invoice or an order confirmation. Play already supports the renderBinary() command in the controller to send binary data to the browser, however this is quite low level. This recipe shows how to combine the use of Apache FOP – which allows creation of PDF data out of XML-based templates – and the Play built-in templating mechanism to create customized PDF documents in real time.

You can find the source code of this example in the chapter2/pdf directory.

Getting ready

As there is already a PDF module included in Play, you should make sure you disable it in your application in order to avoid clashes. This of course only applies, if it has already been enabled before.

How to do it...

First you should download Apache FOP from http://www.apache.org/dyn/closer. cgi/xmlgraphics/fop and unpack it into your application. Get the ZIP file and unzip it so that there is a fop-1.0 directory in your application depending on your downloaded version.

Now you have to copy the JAR files into the lib/ directory, which is always included in the classpath when your application starts.

cp fop-1.0/build/fop.jar lib/
cp fop-1.0/lib/*.jar lib/
cp fop-1.0/examples/fo/basic/simple.fo app/views/Application/index
.form lib/commons*

Make sure to remove the commons JAR files from the lib directory, as Play already provides them. In case of using Windows, you would have to use copy and del as commands instead of the Unix commands cp and rm. Instead of copying these files manually you could also add the entry to conf/dependencies.yml. However, you would have to exclude many dependencies manually, which can be removed as well.

Create a dummy User model, which is rendered in the PDF:

public class User {
public String name = "Alexander";
public String description = "Random: " +
RandomStringUtils.randomAlphanumeric(20);
}

You should now replace the content of the freshly copied app/views/Application/ index.fo file to resemble something from the user data like you would do it in a standard HTML template file in Play:

<fo:block font-size="18pt"
...
padding-top="3pt">
${user.name}
</fo:block>

<fo:block font-size="12pt"
...
text-align="justify">
${user.description}
</fo:block>

Change the application controller to call renderPDF() instead of render():

import static pdf.RenderPDF.renderPDF;

public class Application extends Controller {

public static void index() {
User user = new User();
renderPDF(user);
}
}

Now the only class that needs to be implemented is the RenderPDF class in the PDF package:

public class RenderPDF extends Result {

private static FopFactoryfopFactory = FopFactory.
newInstance();
private static TransformerFactorytFactory =
TransformerFactory.newInstance();
private VirtualFiletemplateFile;

public static void renderPDF(Object... args) {
throw new RenderPDF(args);
}

public RenderPDF(Object ... args) {
populateRenderArgs(args);
templateFile = getTemplateFile(args);
}

@Override
public void apply(Request request, Response response) {
Template template = TemplateLoader.load(templateFile);

String header = "inline; filename=\"" + request.
actionMethod + ".pdf\"";
response.setHeader("Content-Disposition", header);

setContentTypeIfNotSet(response, "application/pdf");

try {
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF,
response.out);
Transformer transformer = tFactory.
newTransformer();
Scope.RenderArgsargs = Scope.RenderArgs.current();
String content = template.render(args.data);
InputStream is = IOUtils.toInputStream(content);
Source src = new StreamSource(is);
javax.xml.transform.Result res = new SAXResult(fop.
getDefaultHandler());
transformer.transform(src, res);
} catch (FOPException e) {
Logger.error(e, "Error creating pdf");
} catch (TransformerException e) {
Logger.error(e, "Error creating pdf");
}
}

private void populateRenderArgs(Object ... args) {
Scope.RenderArgsrenderArgs = Scope.RenderArgs.current();
for (Object o : args) {
List<String> names = LocalVariablesNamesTracer.
getAllLocalVariableNames(o);
for (String name : names) {
renderArgs.put(name, o);
}
}
renderArgs.put("session", Scope.Session.current());
renderArgs.put("request", Http.Request.current());
renderArgs.put("flash", Scope.Flash.current());
renderArgs.put("params", Scope.Params.current());
renderArgs.put("errors", Validation.errors());
}

private VirtualFilegetTemplateFile(Object ... args) {
final Http.Request request = Http.Request.current();
String templateName = null;
List<String>renderNames = LocalVariablesNamesTracer.getAll
LocalVariableNames(args[0]);
if (args.length> 0 &&args[0] instanceof String
&&renderNames.isEmpty()) {
templateName = args[0].toString();
} else {
templateName = request.action.replace(".", "/") +
".fo";
}

if (templateName.startsWith("@")) {
templateName = templateName.substring(1);
if (!templateName.contains(".")) {
templateName = request.controller + "." +
templateName;
}
templateName = templateName.replace(".", "/") + ".fo";
}

VirtualFile file = VirtualFile.search(Play.templatesPath,
templateName);
return file;
}
}

How it works...

Before trying to understand how this example works, you could also fire up the included example of this application under examples/chapter2/pdf and open http://localhost:9000/ which will show you a PDF that includes the user data defined in the entity.

When opening the PDF, an XML template is rendered by the Play template engine and later processed by Apache FOP. Then it is streamed to the client. Basically, there is a new renderPDF() method created, which does all this magic. This method is defined in the pdf.RenderPDF class. All you need to hand over is a user to render.

The RenderPDF is only a rendering class, similar to the DigestRequest class in the preceding recipe. It consists of a static renderPDF() method usable in the controller and of three additional methods.

The getTemplateFile() method finds out which template to use. If no template was specified, a template with the name as the called method is searched for. Furthermore it is always assumed that the template file has a .fo suffix. The VirtualFile class is a Play helper class, which makes it possible to use files inside archives (like modules) as well. The LocalVariablesNamesTracer class allows you to get the names and the objects that should be rendered in the template.

The populateRenderArgs() method puts all the standard variables into the list of arguments which are used to render the template, for example, the session or the request.

The heart of this recipe is the apply() method, which sets the response content type to application/pdf and uses the Play built-in template loader to load the .fo template. After initializing all required variables for ApacheFOP, it renders the template and hands the rendered string over to the FOP transformer. The output of the PDF creation has been specified when calling the FopFactory. It goes directly to the output stream of the response object.

There's more...

As you can see, it is pretty simple in Play to write your own renderer. You should do this whenever possible, as it keeps your code clean and allows clean splitting of view and controller logic. You should especially do this to ensure that complex code such as Apache FOP does not sneak in to your controller code and make it less readable.

This special case poses one problem. Creating PDFs might be a long running task. However, the current implementation does not suspend the request.

More about Apache FOP

Apache FOP is a pretty complex toolkit. You can create really nifty PDFs with it; however, it has quite a steep learning curve. If you intend to work with it, read the documentation under http://xmlgraphics.apache.org/fop/quickstartguide.html and check the examples directory (where the index.fo file used in this recipe has been copied from).

Using other solutions to create PDFs

There are many other solutions, which might fit your needs better than one based on xsl-fo. Libraries such as iText can be used to programmatically create PDFs. Perhaps even the PDF module available in the module repository will absolutely fit your needs.

Summary

In this article we saw how with the help of a well defined boundary to your model classes you can keep your controllers as clean as possible. Specifically we covered:

  • URL routing using annotation-based configuration
  • Basics of caching
  • Using HTTP digest authentication
  • Generating PDFs in your controllers

In the next article we will take a look at object binding and validation and rendering JSON output.


Further resources related to this subject:


Play Framework Cookbook Over 60 incredibly effective recipes to take you under the hood and leverage advanced concepts of the Play framework
Published: August 2011
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

About the Author :


Alexander Reelsen

Alexander Reelsen is a software engineer living in Munich, Germany, where he has been working on different software systems, for example a touristic booking engine, a campaign management and messaging platform and a b2b ecommerce portal. He has been using the play framework since 2009 and was immediately astonished by the sheer simplicity of this framework, while still being pure java. Other interests include scaling shared-nothing web architectures and nosql databases.

Being rather a system engineer most of the time when he started playing around with Linux at the age of 14, Alexander got to know software engineering during his studies and decided web applications are more interesting than system administration.

If not doing something hacky, he enjoys playing a good game of basketball or streetball.
Sometimes he even twitters at http://twitter.com/spinscale and can be reached anytime at alexander@reelsen.net

Books From Packt


Agile Web Application Development with Yii1.1 and PHP5
Agile Web Application Development with Yii1.1 and PHP5

Python 3 Web Development Beginner's Guide
Python 3 Web Development Beginner's Guide

AJAX and PHP: Building Modern Web Applications 2nd Edition
AJAX and PHP: Building Modern Web Applications 2nd Edition

Building Dynamic Web 2.0 Websites with Ruby on Rails
Building Dynamic Web 2.0 Websites with Ruby on Rails

Google Web Toolkit 2 Application Development Cookbook
Google Web Toolkit 2 Application Development Cookbook

Ext GWT 2.0: Beginner's Guide
Ext GWT 2.0: Beginner's Guide

Grok 1.0 Web Development
Grok 1.0 Web Development

Apache Wicket Cookbook
Apache Wicket Cookbook


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
p
f
8
L
M
S
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software