Skip to content

Commit

Permalink
[playframework#312] Docs: new sections on play.db.jpa.Blob usage and …
Browse files Browse the repository at this point in the history
…binary responses
  • Loading branch information
Peter Hilton committed May 28, 2011
1 parent a6b830d commit 69e8472
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 23 deletions.
75 changes: 52 additions & 23 deletions documentation/manual/controllers.textile
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ In all cases, Play extracts this data and builds a Map<String, String[]> which c
* The name portion of a name-value pair taken from the Query String
* The contents of a x-www-urlform-encoded body.

h3. Using the params map
h3. <a name="paramsmap">Using the params map</a>

The **params** object is available to any Controller class (it is defined in the **play.mvc.Controller** super class). This object contains all the HTTP parameters found for the current request.

Expand All @@ -83,7 +83,7 @@ bc. public static void show() {

But wait, there are better ways to do this :)

h3. From the action method signature
h3. <a name="methodsignature">From the action method signature</a>

You can retrieve HTTP parameters directly from the action method signature. The Java parameter’s name must be the same as the HTTP parameter’s.

Expand Down Expand Up @@ -125,15 +125,15 @@ If the HTTP parameter corresponding to the action method argument is not found,

h2. <a name="binding">Advanced HTTP to Java binding</a>

h3. Simple types
h3. <a name="simpletypes">Simple types</a>

All the native and common Java types are automatically bound:

**int**, **long**, **boolean**, **char**, **byte**, **float**, **double**, **Integer**, **Long**, **Boolean**, **Char**, **String**, **Byte**, **Float**, **Double**.

Note that if the parameter is missing in the HTTP Request, or if automatic conversion fails, Object types will be set to null and native types will be set to their default values.

h3. Date
h3. <a name="date">Date</a>

A date object can be automatically bound if the date’s string representation matches one of the following patterns:

Expand Down Expand Up @@ -182,11 +182,11 @@ bc. application.langs=fr
This property also affects how the dates are rendered in the templates using **${date.format()}**.


h3. Calendar
h3. <a name="calendar">Calendar</a>

The calendar binding works exactly as with the date, except that Play is choosing the Calendar object according to your locale. The **@Bind** annotation can also be used.

h3. Files
h3. <a name="file">File</a>

File upload is easy with Play. Use a **multipart/form-data** encoded request to post files to the server, and then use the **java.io.File** type to retrieve the file object:

Expand All @@ -199,7 +199,18 @@ bc. public static void create(String comment, File attachment) {

The created file has the same name as the original file. It’s stored in a temporary directory and deleted at the end of the request. So you have to copy it in a safe directory or it will be lost.

h3. Arrays or collections of supported types
The uploaded file's MIME type should normally be specified by the HTTP request’s @Content-type@ header. However, when uploading files from a web browser, this might not happen for uncommon types. In this case, you can map the file name’s extension to a MIME type, using the @play.libs.MimeTypes@ class.

bc. String mimeType = MimeTypes.getContentType(attachment.getName());

The @play.libs.MimeTypes@ class looks up the MIME type for the given file name’s extension in the file @$PLAY_HOME/framework/src/play/libs/mime-types.properties@

You can also add your own types to your application’s @conf/application.conf@ file. For example, to add a MIME type for GIMP images with the @.xcf@ extension, add the line:

bc. mimetype.xcf=application/x-gimp-image


h3. <a name="array">Arrays or collections of supported types</a>

All supported types can be retrieved as an Array or a collection of objects:

Expand Down Expand Up @@ -232,7 +243,7 @@ bc. ?client.name=John&client.phone=111-1111&client.phone=222-2222

would bind the client variable to a map with two elements. The first element with key **name** and value **John**, and the second with key **phone** and value **111-1111, 222-2222**.

h3. POJO object binding
h3. <a name="pojo">POJO object binding</a>

Play also automatically binds any of your model classes using the same simple naming convention rules.

Expand Down Expand Up @@ -282,7 +293,7 @@ h2. <a name="custombinding">Custom binding</a>

The binding system now supports more customization.

h3. @play.data.binding.As
h3. <a name="as">@play.data.binding.As</a>

The first thing is the new **@play.data.binding.As** annotation that makes it possible to contextually configure a binding. You can use it for example to specify the date format that must be used by the **DateBinder**:

Expand Down Expand Up @@ -310,7 +321,7 @@ bc. public static void update(@As(",") List<String> items) {

This binds a simple comma separated **String** as a **List**.

h3. @play.data.binding.NoBinding
h3. <a name="nobinding">@play.data.binding.NoBinding</a>

The new **@play.data.binding.NoBinding** annotation allows yous to mark non-bindable fields, resolving potential security issues. For example:

Expand All @@ -326,7 +337,7 @@ public static void editProfile(@As("profile") User user) {

In this case, the **isAdmin** field will never be bound from the **editProfile** action, even if an malicious user includes a **user.isAdmin=true** field in a fake form post.

h3. play.data.binding.TypeBinder
h3. <a name="typebinder">play.data.binding.TypeBinder</a>

The *@As* annotation also allows you to define a completely custom binder. A custom binder is subclass of **TypeBinder** that you define in your project. For example:

Expand All @@ -343,7 +354,7 @@ bc. public static void anyAction(@As(binder=MyCustomStringBinder.class) String n
...
}

h3. @play.data.binding.Global
h3. <a name="global">@play.data.binding.Global</a>

Alternatively, you can define a global custom binder that will apply for the corresponding type. For example, you define a binder for the **java.awt.Point** class like this:

Expand Down Expand Up @@ -376,7 +387,7 @@ bc. public static void show(Long id) {

The **render(…)** method emits a Result object and stops further method execution.

h3. Return some textual content
h3. <a name="text">Return some textual content</a>

The **renderText(…)** method emits a simple Result event which writes some text directly to the underlying HTTP Response.

Expand All @@ -394,7 +405,25 @@ bc. public static void countUnreadMessages() {
renderText("There are %s unread messages", unreadMessages);
}

h3. Execute a template
h3. <a name="binary">Return binary content</a>

To serve binary data, such as a "file stored on the server":jpa#file, use the @renderBinary@ method. For example, if you have a @User@ model with a @play.db.jpa.Blob photo@ property, add a controller method to load the model object and render the image with the stored MIME type:

bc. public static void userPhoto(long id) {
final User user = User.findById(id);
response.setContentTypeIfNotSet(user.photo.type());
java.io.InputStream binaryData = user.photo.get();
renderBinary(binaryData);
}

h3. <a name="attachment">Download a file as an attachment</a>

You can set an HTTP header to instruct the web browser to treat a binary response as an ‘attachment’, which generally results in the web browser downloading the file to the user’s computer. To do this, pass a file name as a parameter to the @renderBinary@ method, which causes Play to set the @Content-Disposition@ response header, providing a file name. For example, supposing the @User@ model from the previous example as a @photoFileName@ property:

bc. renderBinary(binaryData, user.photoFileName);


h3. <a name="template">Execute a template</a>

If the generated content is complex, you should use a template to generate the response content.

Expand Down Expand Up @@ -466,7 +495,7 @@ bc. public static void show(Long id) {
renderTemplate("Clients/showClient.html", id, client);
}

h3. Redirect to another URL
h3. <a name="redirect">Redirect to another URL</a>

The **redirect(…)** method emits a Redirect event that in turn generates an HTTP Redirect response.

Expand All @@ -475,7 +504,7 @@ bc. public static void index() {
}


h3. Action chaining
h3. <a name="chaining">Action chaining</a>

There is no equivalent to the Servlet API **forward**. An HTTP request can only invoke one action. If you need to invoke another action, you have to redirect the browser to the URL able to invoke that action. In this way, the browser URL is always consistent with the executed action, and the **Back/Forward/Refresh** management is much easier.

Expand Down Expand Up @@ -511,7 +540,7 @@ POST /clients Clients.create
* The browser then issues **GET /clients/3132**.
* …

h3. Customizing web encoding
h3. <a name="encoding">Customizing web encoding</a>

Play! emphasize the use of UTF-8, but there might be situations where some responses must use different encoding,
or that the whole app has to use other encoding.
Expand Down Expand Up @@ -543,7 +572,7 @@ A controller can define interception methods. Interceptors are invoked for all a

These methods have to be **static** but not **public**. You have to annotate these methods with a valid interception marker.

h3. @Before
h3. <a name="before">@Before</a>

Methods annotated with the **@Before** annotation are executed before each action call for this Controller.

Expand Down Expand Up @@ -597,7 +626,7 @@ bc. public class Admin extends Application {

The **unless** and **only** parameters are available for the **@After**, **@Before** and **@Finally** annotations.

h3. @After
h3. <a name="after">@After</a>

Methods annotated with the **@After** annotation are executed after each action call for this Controller.

Expand All @@ -617,7 +646,7 @@ bc. public class Admin extends Application {

}

h3. @Catch
h3. <a name="catch">@Catch</a>

Methods annotated with **@Catch** are called if another action method throws the specified exception. The thrown exception is passed as a parameter to the @Catch method.

Expand Down Expand Up @@ -662,7 +691,7 @@ bc. public class Admin extends Application {
}


h3. @Finally
h3. <a name="finally">@Finally</a>

Methods annotated with the **@Finally** annotation are always executed after each action call to this Controller.
@Finally-methods are called both after successful action calls and if an error occurred.
Expand Down Expand Up @@ -706,11 +735,11 @@ bc. public class Admin extends Application {
}


h3. Controller hierarchy
h3. <a name="hierarchy">Controller hierarchy</a>

If a Controller class is a subclass of another Controller class, interceptions are applied to the full Controller hierarchy.

h3. Adding more interceptors using the @With annotation
h3. <a name="with">Adding more interceptors using the @With annotation</a>

Because Java does not allow multiple inheritance, it can be very limiting to rely on the Controller hierarchy to apply interceptors. But you can define some interceptors in a totally different class, and link them with any controller using the **@With** annotation.

Expand Down
44 changes: 44 additions & 0 deletions documentation/manual/jpa.textile
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,50 @@ Or even count using a query:

bc. long userPostCount = Post.count("author = ?", connectedUser);


h2. <a name="files">Storing uploaded files with play.db.jpa.Blob</a>

You can use the @play.db.jpa.Blob@ type to store uploaded files in the file system (not in the database). On the server, Play stores the uploaded image in a file in the @attachments/@ folder, inside the application folder. The file name (a "UUID":http://en.wikipedia.org/wiki/Uuid) and MIME type are stored in a database attribute whose SQL type is @VARCHAR@.

The basic use case of uploading, storing and serving a file is extremely easy in Play. This is because the binding framework automatically binds a file uploaded from an HTML form to your JPA model, and because Play provides convenience methods that make serving binary data as easy as serving plain text. To store file uploads in your model, add a property of type @play.db.jpa.Blob@

bc. import play.db.jpa.Blob;

@Entity
public class User extends Model {

public String name;
public Blob photo;
}

To upload files, add a form to your view template, and use a file upload form control for the model’s @Blob@ property:

bc. #{form @addUser(), enctype:'multipart/form-data'}
<input type="file" name="user.photo">
<input type="submit" name="submit" value="Upload">
#{/form}

Then, in the controller, add an action that saves the upload in a new model object:

bc. public static void addUser(User user) {
user.save();
index();
}

This code does not appear to do anything other than save the JPA entity, because the file upload is handled automatically by Play. First, before the start of the action method, the uploaded file is saved in a sub-folder of @tmp/uploads/@. Next, when the entity is saved, the file is copied to the @attachments/@ folder, with a UUID as the file name. Finally, when the action is complete, the temporary file is deleted.

If you upload another file for the same user, this will be saved as a new file on the server, whose name is a new UUID. This means that the original file will now be orphaned. If you do not have unlimited disk space then you will have to implement your own scheme for cleaning up, perhaps with an "asynchronous job":jobs.

If the HTTP request did not specify the file’s correct MIME type, you can use file name extension mapping, as described in the "controller file upload":controllers#file section.

To save attachments in a different folder, specify a different path in the @application.conf@ file. This can be an absolute path, or a relative path to a folder inside the Play application folder:

bc. attachments.path=photos

To serve a stored file, pass @Blob.get()@ to the controller’s @renderBinary@ method, as described in the "controller binary response":controllers#binary section.



h2. <a name="save">Explicit save</a>

Hibernate maintains a cache of Objects that have been queried from the database. These Objects are referred to as persistent Objects as long as the EntityManager that was used to fetch them is still active. That means that any changes to these Objects within the bounds of a transaction are automatically persisted when the transaction is committed. In standard JPA, these updates are implicit within the transaction’s boundary; you don’t have to explicitly call any method to persist the values.
Expand Down

0 comments on commit 69e8472

Please sign in to comment.