Learning Play! Framework 2 — Save 50%
Start developing awesome web applications with this friendly, practical guide to the Play! Framework with this book and ebook.
A web application always has, at some point, the need to deal with multiple types of content. Common content types include JSON, XML, HTML, but there could also be images or even videos to be stored and streamed. Play! 2 provides a clean way of dealing with such content types with the help of body parsers.
We won't cover the implementation details of such body parsers, because it's purely based on a functional concept, Iteratee, and thus their implementations are in Scala only. However, we'll see how they are used and how we can gain benefits from them.
In this article by Andy Petrella author of Learning Play! Framework 2, we'll update and clean up a bit of what we have been doing so far in order to enable several workflows. So we will only be using examples we have learned up to now. The following is what will be achieved:
Make the Chat and Item classes persistent using Ebean
Create a link between an item and a user (a user's reply in a chat)
Introduce a new type, Image, that will be part of a chat as an attachment
Enable a user to connect
Browse all chat instances
Allow the connected user to reply in a chat
Allow the connected user to attach an image to a chat
Show examples of UIs
Create an action that outputs a requested image
Create an action that provides an Atom feed of all chats which have specific users getting involved (kind of like following)
(For more resources related to this topic, see here.)
In order to keep the article short and to the point, we'll only see the Java part. Keep in mind that the Scala version is little different for this level of detail.
Body parsing for better reactivity
As noted earlier, the way to manage content in Play! 2 is to use instances of body parsers. In brief, a body parser is a component that is responsible for parsing the body of an HTTP request as a stream to be converted into a predefined structure. This has a common sense ring to it, however their strength is in their way of consuming the stream—in a reactive fashion.
Reactivity, in this context, is meant to describe a process where an application won't block on a task that is actually idle. As a stream consumption task is idle when no bytes are incoming, a body parser should behave the same. It will read and construct an internal representation of the incoming bytes. But it can also decide at any time that it has read enough to terminate and return the representation. On the other hand, if no more bytes are coming into the stream, it can relax its thread in favor of another request; it pauses its work until new bytes are received.
Thinking about an HTTP request that is sending a bunch of XML content, the underlying action can use the XML-related body parser to handle it correctly (read reactively); that is, by parsing it and providing a DOM representation.
To understand what a body parser actually is, we'll first look at how they are used—in the actions. An action in Play! 2 represents the piece of software that is able to handle an HTTP request; therefore, they are the right place to use a body parser.
In the Java API, an action is allowed to be annotated with the Of annotation available in the BodyParser class. This annotation declares the expected type of request routed to it, and it requires a parameter that is the class of the parser that will be instantiated to parse the incoming request's body.
The following screenshot shows an example:
Isn't this helpful? We've gone from a request to a W3C document, in a single line. Functionally speaking, this works because an action is semantically a higher-order function that takes a body parser and generates a function that takes a request (and so its body) and results in an HTTP response (result). This result will then be used to construct the HTTP response by Play! 2.
In Java, it is not all that obvious how to create a higher-order function. A good way, however, to achieve this was to add an annotation. An annotation can be processed at runtime in order to execute the right body parser (in this case).
To illustrate this, we'll have a quick look at the Scala version:
With this Scala version, it is easy to see that an action is dealing with a function from a request to a response.
There are a plenty of predefined body parsers that can be used to handle our requests, and they are all defined in the BodyParser class as static inner classes. One can have a specific behavior to be applied on its expected request body, and even though a body parser has to be implemented in Scala, a Java coder can simply extend these current implementations. Actually, they're already providing enough control to cover all custom use cases.
So, we have in our hands tools to handle the following content types:
URL form encoded
Multipart (for uploading files)
As we can see from the previous list, there is, obviously, an implementation for the x-www-form-urlencoded content type. Indeed, this is the parser we've used so far to retrieve data from the client side. For example, using POST requests throughout HTML forms.
But wait, we never had to add such annotations to our actions, and, moreover, we've never looked in the parsed result. That's true, Play! 2, as a great framework, is already doing a lot of stuff for us. And that's because it's a web framework; it takes advantage of HTTP; in this case, using the content-type header.
Based on this hint, it seems obvious that Play! Framework 2 will look in this header to find the right parser to apply. So annotations are mandatory, but where did we use them previously? In the bindFromRequest method, of course. Let's see how.
We have used form instances, and we fed them some data through the client. Those instances were applied on the request using the bindFromRequest method, and this method's job was to look for data according to the provided content type. And, of course, this content type was set in the header by the HTML forms themselves.
Indeed, an HTTP GET will send data in the request URL (query string), where an HTTP POST will be sent with a body that contains all data encoded by default as URL parameters (that is, x-www-url-encoded).
So, we can now give an overview of what the bindFromRequest method does. When we ask a form to be filled in with data, this method will:
Gather data as URL-form encoded data, if any
Gather data from parts (if the content type is multipart-data)
Gather data as JSON-encoded, if any
Gather data from the query string (that's why GET requests were working as well)/p>
Fill in the form's data with all of them (and validate)
You might be wondering the worth of such annotations; the quick answer to that is they allow new types of parsers, but they can also enforce certain actions' requests to match a given content type.
Another advantage of such annotations is that they allow us to extend or narrow the length of the body that can be handled. By default, 100 K are accepted, and this can be either configured (parsers.text.maxLength=42K) or passed as an argument to the annotation.
With all of this in mind, we are now ready to implement these concepts in our code, and what we're going to do is to update our code base to create a kind of forum. A forum where one can log in, initiate a chat, reply to non-closed ones (based on their date), or even attach files to them.
Creating a forum
In this section, we'll refactor our existing application in order to enable it to act as a forum. And, chances are high that it won't be necessary to learn anything new; we'll just re-use the skills gathered so far; but we'll also use the parsing commodities that Play! 2 offers us.
Reorganizing and logging in
The very first thing we have to do is to enable a user to log in; this ability was already created in the Data controller. However, for that, we'll update our Application controller a bit, to create a new index action that will check whether a user is logged in or not.
So, index is now the new entry point of the application and can be routed from / in the routes file. And, it's solely meant to check if a user has logged in or not. This check is based on the session content, as we simply check whether a user's e-mail is present in the session.
We never see what a session can be in Play! 2, but we saw that Play! 2 is completely stateless. So, a session in Play! 2 is only an encrypted map of the value stored in the cookie. Thus it cannot be that big, and definitely cannot contain full data.
If the user is present, we redirect the request to the chatroom by calling redirect with the expected action. This will prevent the browser from posting the request again if the user reloads the page. This method is called POST-redirect-GET.
Otherwise, we respond with an Unauthorized HTTP response (401) that contains the HTML login page.
The two actions (shown in the next screenshot) are so simple that we won't cover them further, except for a single line: session().clear(). It is simply revoking the cookie's content, which will require the subsequent request to create a new one, which then doesn't contain the previously stored e-mail.
And finally, enter, which shows how a request's body can easily be handled using the relevant method: asFormUrlEncoded. It should look like that shown in the following screenshot:
Indeed, one would normally have to use a form to retrieve this information for us, which would do it for us (behind the scenes); but in this case we have only a single parameter to retrieve, so a form would be overkill.
So far, so good; we are now able to create a user, log in with it, and use a login page. To target having cleaner code, it would be worth splitting the Data controller code into several pieces (matter of a good separation of subject). Hence, the Users controller is created, in which will be placed the user-related actions taken out of Data.
Now, we'll move back to something we saw earlier but didn't cover—the routes. Chats.allChats() action call.
In the previous section, we were introduced to the Chats controller and its allChats action. If the names are self-descriptive, the underlying code isn't that much.
First of all, we're now dealing with Chat instances that must be persisted somewhere in a database, along with their underlying items.
But we'll also prepare for the next section, which relates to multipart data (for instance, it's helpful for file upload). That's why we'll add a brand new type, Image, which is also linked to Chat.
Having said that, it would be worth checking our new chat implementation:
Before we cover the Item and Image types, we'll first go to the Chats controller to see what's going on.
Finally, we can see our allChats action; it's simply rendering all existing instances within a template. Even the rest of the controller is simple; everything is done in templates, which are left as exercises (we're so good at them now!).
However, there's still the loadChat action that contains something related to this article:
Long chatId = Long.parseLong(queryString.get("chatid"));
This action handles requests asking to show a particular Chat instance, which is a resource and thus should be served using a GET request. This implies that the parameter value is stored in the query string (or in the URL itself) rather than in the request body.
Regarding query string access, it's more interesting to analyze the following line:
Map<String,String> queryString = request().queryString();
In fact, all actions contextually refer to a request object, which is accessible using the request() method. This request object declares a queryString() method that returns a map of string and an array of strings. What comes next is trivial; we just get chatid out of this map (ok... in a very unsafe way).
Until now, we have been able to log in and access the chatroom, where we can create or show chat instances. But we're still unable to reply to a chat. That's what will be tackled now.
For that, we need to create an action that will, based on a chat ID, post a new message linked to the logged in user, and then attach this message as an item of the underlying Chat instance.
For this, we must update the Item class with persistence information. Afterwards, we'll be able to update the Chats controller in order to create instances.
Ok, it's like a beefed-up POJO; let's jump into the action that will create Item instances.
The workflow to post a message for a user starts by enabling him/her to participate in a chat. This is done by loading it (using the loadChat action) where the user will be able to post a new message (an overview of the UI will be presented at the end of this article for illustration only).
The following screenshot shows how it can be done:
Observe how the user was recovered using the session.
Still, nothing cumbersome to review here, we've just re-used a lot of stuff we've already covered. The action receives a POST request in which information about the message is given, and then we can bind the request to itemForm and finally save to the database the item contained in the resulting form.
At most, we should notice that we're still free to encode the body as we want, and also that the chat ID is not a part of the form but a part of the action signature—that's because it is a part of the URL (routing).
We've almost finished our forum; the only thing needed is to enable users to post images.
Handling multipart content types
The HTTP protocol is ready to accept, from a client, a lot of data and/or large chunks of data, at once. A way to achieve this is to use a specific encoding type: multipart/form-data. Such requests will have a body that can hold several data pieces formatted differently and attributed with different names. So, Play! 2 is a web framework that fits into HTTP as much as possible; that's why it deals with such requests goods, and provides an API that hides almost all of the tricky parts.
In this section, we'll see how one could upload an image along with some caption text that will be attached to a specific chat.
Before diving into the workflow, let's first create the holding structure: Image.
This newly introduced type is not hard to understand as well; only two things should be pointed out:
The pic() method that relies on the filePath field to recover the file itself. It uses a File instance to memorize subsequent calls.
The enum type that prepares the action logic to filter the incoming files based on the given MIME type.
This logic could also be defined in the validate method.
These instances are always locked in with the connected user who uploaded it and will be added to a Chat instance. This will allow a chatroom to display all attached images with their caption beside the messages themselves.
Now we're ready to look at the file upload itself by paying some attention to the last action of the Chats controller, that is, receiveImage.
As we are used to simplifying the code (Play! 2 is there to ease our work, after all) and to get straight to the point, we reflected this in our receiveImage action..
In a very few lines, we declared a new action that expects requests to be multipart encoded containing at least two parts, where the first is a map of data (no matter how this map is encoded) to fill in imageForm (essentially a caption). The second will be the image part.
After binding the request with the form and verifying that no errors have occurred, we can move to the body content in order to recover the binary data that was sent along with its metadata: the file content, its content type, its length, and so on.
That was quite an intuitive thing to do – asking the body to be parsed as a multipart/multidata and and get it as an Http.MultipartFormData object, which has a getFile method that returns an Http.MultipartFormData.FilePart value. To understand why we didn't specify a body parser, recall that Play! 2 is able, most of the time, to discover which method fits best by itself. The Http.MultipartFormData. FilePart type is not only allowing us to recover the content as a file, but also its key in the multipart body, its filename header, and (especially) its content type.
Having all of these things in hand, we are now able to check the content-type validity against the image's enum, and to store the image by getting the file path of the provided file.
This file path will target the Temp directory of your machine. In the real world, the file should be relocated in a dedicated folder or maybe on an S3 repository.
Et voilà! We have now learned about some of the features that can provide a very simple forum. The following screenshot shows what it could look like (without any efforts on the design, of course). First, the forms to show and enter archived and active chats:
On entering an active chat, let's say the one named Today, we reach a page similar to the one shown next:
Using the Attach an image form, we can select an image on our filesystem to be sent to the server. The result obtained is shown as follows:
Until now, we have spoken about handling various content types coming from the outside world, but what about our application having to render content other than HTML? That's what we're about to see next.
|Start developing awesome web applications with this friendly, practical guide to the Play! Framework with this book and ebook.|
eBook Price: $29.99
Book Price: $49.99
In this section, we'll see how a Play! 2 server is able to render different resources in different ways rather than simply providing HTML pages.
The actions' body in Play! 2 not only have the responsibility of creating resources to be provided to the outside world, but also of declaring how this resource has to be rendered. Fortunately, there are a lot of boilerplates already written for our use in the default actions builder.
The so-called actions builder are the methods we have used almost blindly until now; that is to say, the static methods available in the play.mvc.Results.java class such as ok, redirect, badRequest, and unauthorized.
Indeed, these methods have been overloaded several times in order to accept several representations. The following are some examples:
Content: This takes content that is of the base type of classic string representations such as Html, Xml, and Txt. This is also the result-type of a rendered template.
String: This will be rendered as is, as a plain text content (an overloaded version of the method accepts the encoding as a second argument).
JsonNode: This is trivial. If we create an instance of such a class, we'll have our resource serialized as application/json.
InputStream: This is a convenient way to dump a stream into a response body (accepts chunks for an HTTP-chunked encoded connection).
File: This helps us avoid typing new FileInputStream(...) in InputStream. This accepts the file and will deal with the stream for us.
Knowing all this, we'll now enhance our forum a bit to not only show the persisted attached images but also to provide a dynamic Atom feed for all chats that users have participated in.
The previous screenshot shows our attached image being displayed.
To tackle this, we'll retrieve the empty Content controller we saw at the beginning of this article. And we'll add two actions, routed as shown in the following screenshot:
The former action asks for a specific image content, whereas the latter one is asking for an Atom feed for certain users.
Imaging all of the chat
So, it's now time to render our images back to the client and show them in their respective chatrooms. The following screenshot shows how to do it:
So trivial... take the ID, get the related image in the database, ask for its underlying file, and return it in an OK (HTTP 200) response.
Thus, we're now able to use this action in our HTML templates using a simple img tag that has its src attribute pointing to our new action, shown as follows:
With the image rendering done, let's now move to the Atom feed.
Atomizing the chats
This section is dedicated to the production of XML content.
In Java, we all know how painful it is to generate a DOM structure that has to be dumped as a string. Actually, Scala has a native syntax for XML, but it's still better (easier) to use templates for that.
Indeed, we used Scala templates for generating Html responses (remember Html derives from Content), but we could also generate Xml contents for the templates that are accordingly named. In other words, where myBeautifulContent.scala.html creates an Html response, myStructuredContent.scala.xml generates Xml content.
But first of all, we'll have to gather the data before applying them to an XML template. This is done in the code shown in the following screenshot:
Apart from database interactions to retrieve user and chat information, the points worth noting are the following:
We used an ok result builder that relies on a template to generate content
We don't let the XML's default content type to be returned (text/xml), but we override it by specifying the Atom one, application/atom+xml, using the as method on the ok response
Having prepared the data and the content type to be rendered, we can now look at the real representation: the template.
According to the action that uses the views.xml.content.atom.render method, the template must be located in the content package under the views one and must be named atom.scala.xml. And its content might be as shown in the following screenshot:
We were able to gracefully generate our Xml content using XML directly, which avoided the headaches with DOM manipulations.
The only noticeable thing in the previous screenshot is the first line (all of the others are just data manipulations for displaying purposes), which is declaring the necessary parameters directly after we find the first XML line. That's because of the XML specification that requires this meta-information to be positioned at the very first character.
With this small amount of effort and code, we're already done and can now see the result in an appropriate Atom viewer, shown as follows:
This client enables us to add our feed to Google Reader for instance.
In this article we built a forum-like web application with really basic features, but we are able to at least create chatrooms for particular topics, join them, participate with messages, or add images to them. All this without any pain or boilerplate, thanks to the content management features that Play! Framework 2 puts in our hands for free.
Indeed, in this article we were able to deal with complex routing involving several ways to provide information, using different HTTP methods and URLs with or without extra parameters. Such requests were used to feed the server with data very easily, by using a single API that is independent of how the data is represented. For instance, on both sides we were dealing with forms.
Body parsing was there to help us, and to facilitate resource binding with our constrained forms. Moreover, they are consuming data in such a way that even large data sets won't crash the server—they are consumed reactively.
At this stage, we're also able to send data to the client in whatever fashion we'd like. XML, JSON, HTML, and all such are now open doors for our web applications.
But, for now, our forum is switching statically between pages all the time (read: loading the full page), and sometimes requests that we go back and refresh the page to use it further, such as refreshing a chatroom to see other participants' new messages.
Resources for Article :
- Working with Drupal Audio in Flash (part 2) [Article]
- Play Framework: Introduction to Writing Modules [Article]
- Framework Comparison: Backbase AJAX framework Vs Other Similar Framework (Part 1) [Article]
|Start developing awesome web applications with this friendly, practical guide to the Play! Framework with this book and ebook.|
eBook Price: $29.99
Book Price: $49.99
About the Author :
Andy Petrella is first and foremost a Belgian mathematician who tried to find a way to apply his skills to the concrete world. One of them was programming. So, after graduating in Mathematics, he continued his study in Informatics at the University of Liège.
He quickly became interested in Geomatics because of the heterogeneous needs of this discipline, which led him to mainly work in the GIS field. Over there, he got the opportunity to sharpen his skills on distributed architecture for interoperable solutions.
Having found a new way to enjoy mathematics along with programming, he joined one of his friends and they decided to create NextLab (http://www.nextlab.be/), a company that offers the perfect context to push Play! 2 and Scala to the foreground through projects and customers.
Andy also loves to share his experiences, his enjoyment, and his discoveries through the co-creation of a user group called WAJUG (http://wajug.be/) dedicated to help Walloons to meet together and share ideas about information technology. In order to ensure a constant flow of information, he also writes his thoughts on his blog, SKA LA (http://ska-la.blogspot.be/).