Skip to content

Commit

Permalink
Documentation for 1.2
Browse files Browse the repository at this point in the history
  • Loading branch information
guillaumebort committed Apr 4, 2011
1 parent dc3346d commit 0f513ea
Show file tree
Hide file tree
Showing 30 changed files with 705 additions and 135 deletions.
Binary file added documentation/images/chat-websocket.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/images/cheatsheet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/images/dependencies.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/images/eclipse-test-runner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/images/evolutions-conflict.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/images/evolutions-inconsistent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/images/evolutions-resolve.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/images/evolutions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/images/h2console.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions documentation/manual/5things.textile
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ File upload management is very simple with Play.

The HTML form:

bc. <form action="@{Article.uploadPhoto()}" method="POST" enctype="multipart/form-data">
bc. #{form @uploadPhoto(), enctype:'multipart/form-data'}
<input type="text" name="title" />
<input type="file" id="photo" name="photo" />
<input type="submit" value="Send it..." />
</form>
#{/}

And the Java code:

Expand Down
6 changes: 4 additions & 2 deletions documentation/manual/ajax.textile
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
h1. Ajax in the Play framework

The Play framework allows you to easily perform Ajax requests, and is shipped with "jQuery":http://jquery.com by default.
This section describes how to effectively use "jQuery":http://jquery.com within the framework.
The Play framework allows you to easily perform Ajax requests, and is shipped with "jQuery":http://jquery.com by default. This section describes how to effectively use "jQuery":http://jquery.com within the framework.

The Play framework also comes with the handy **jsAction** tag to transparently get a method definition from the controller.

Expand Down Expand Up @@ -43,3 +42,6 @@ bc. $.post(listAction, function(data) {
$('#result').html(data);
});

p(note). **Continuing the discussion**

Handle %(next)"Internationalization":i18n%.
157 changes: 157 additions & 0 deletions documentation/manual/asynchronous.textile
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
h1. Asynchronous programming with HTTP

This section explains how to deal with asynchonism in a Play application to achieve typical long-polling, streaming and other comet-style applications that can scale to thousand of concurrent connections.

h2. Suspending HTTP requests

Play is intended to work with very short requests. It uses a fixed thread pool to process requests queued by the HTTP connector. To get optimum results, the thread pool should be as small as possible. We typically use the optimum value of **nb of processors + 1** to set the default pool size.

That means that if a request is very long (for example waiting for a long computation) it will block the thread pool and penalize your application responsiveness. Of course you could add more threads to the pool, but it will result in wasted resources, and anyway the pool size will never be infinite.

Think for example about a chat application where browsers send a blocking HTTP request that waits for a new message to display. These requests can be very very long (typically several seconds) and will block the thread pool. If you plan to allow 100 users to connect simultaneously to your chat application you will need to provision at least 100 threads. OK it’s feasible. But what about 1,000 users? Or 10,000?

To resolve these use cases, Play allows you to temporarily suspend a request. The HTTP request will stay connected, but the request execution will be popped out of the thread pool and tried again later. You can either tell Play to try the request later after a fixed delay, or wait for a @Promise@ value to be available.

p(note). **Tip**. You can see a real example in samples-and-tests/chat.

For example, this action will launch a very long job and wait for its completion before returning the result to the HTTP response:

bc. public static void generatePDF(Long reportId) {
Promise<InputStream> pdf = new ReportAsPDFJob(report).now();
InputStream pdfStream = await(pdf);
renderBinary(pdfStream);
}

Here we use @await(…)@ to ask Play to suspend the request until the @Promise<InputStream>@ value is redeem.

h3. Continuations

Because the framework needs to recover the thread you were using in order to use it to serve other requests, it has to suspend your code. In the previous Play version the await(…) equivalent was waitFor(…). And waitFor was suspending your action, and then recalling it later from the beginning.

To make it easier to deal with asynchronous code we have introduced continuations. Continuations allow your code to be suspended and resumed transparently. So you write your code in a very imperative way, as:

bc. public static void computeSomething() {
Promise<String> delayedResult = veryLongComputation(…);
String result = await(delayedResult);
render(result);
}

In fact here, your code will be executed in 2 steps, in 2 different threads. But as you see it, it's very transparent for your application code.

Using @await(…)@ and continuations, you could write a loop:

bc. public static void loopWithoutBlocking() {
for(int i=0; i<=10; i++) {
Logger.info(i);
await("1s");
}
renderText("Loop finished");
}

And using only 1 thread (which is the default in development mode) to process requests, Play is able to run concurrently these loops for several requests at the same time.

h2. HTTP response streaming

Now, because you are able to loop without blocking the request, perhaps you want to send data to the browser as soon you have part of the result available. It is the point of the "Content-Type:Chunked" HTTP response type. It allows to send your HTTP response is several times using multiples chunks. The browser will receive these chunk as soon as they are published.

Using @await(…)@ and continuations, you can now achieve that using:

bc. public static void generateLargeCSV() {
CSVGenerator generator = new CSVGenerator();
response.contentType = "text/csv";
while(generator.hasMoreData()) {
String someCsvData = await(generator.nextDataChunk());
response.writeChunk(someCsvData);
}
}

Even if the CSV generation take 1 hour, Play is able to process simultaneously several requests using a single thread, sending back the generated data to the client as soon as they are available.

h2. Using WebSockets

WebSockets are a way to open a 2 way communication channel between a browser and your application. On the browser side, you open a socket using a "ws://" url:

bc. new Socket("ws://localhost:9000/helloSocket?name=Guillaume")

On the Play side you declare a WS route:

bc. WS /helloSocket MyWebSocket.hello

MyWebSocket is a WebSocketController. A WebSocket controller is like a standard HTTP controller but handle different concepts:

* It has a request object, but no response object.
* It has access to the session but for read only.
* It doesn't have renderArgs, routeArgs, nor flash scope.
* It can read params only from the route pattern or from the QueryString.
* It has 2 communication channels: inbound and outbound.

When the client connects to the **ws://localhost:9000/helloSocket** socket, Play will invoke the @MyWebSocket.hello@ action method. Once the @MyWebSocket.hello@ action method exit, the socket is closed.

So a very basic socket example would be;

bc. public class MyWebSocket extends WebSocketController {

public static void hello(String name) {
outbound.send("Hello %s!", name);
}

}

Here when the client connects to the socket, it receive the 'Hello Guillaume' message, and then Play closes the socket.

Of course usually you don't want to close the socket immediately. But it is easy to achieve using @await(…)@ and continuations.

For example a basic Echo server:

bc. public class MyWebSocket extends WebSocketController {

public static void echo() {
while(inbound.isOpen()) {
WebSocketEvent e = await(inbound.nextEvent());
if(e instanceof WebSocketFrame) {
WebSocketFrame frame = (WebSocketFrame)e;
if(!e.isBinary) {
if(frame.textData.equals("quit")) {
outbound.send("Bye!");
disconnect();
} else {
outbound.send("Echo: %s", frame.textData);
}
}
}
if(e instanceof WebSocketClose) {
Logger.info("Socket closed!");
}
}
}

}

In the previous example, the imbricated 'if' and 'cast' soup was tedious to write and error prone. And here Java sucks. Even for this simple case it is not easy to handle. And for more complicated cases where you combine several streams, and have even more event types, it becomes a nightmare.

That's why we have introduced a kind of basic pattern matching in Java in the "play.libs.F":libs#FunctionalprogrammingwithJava library.

So we can rewrite our previous echo sample as:

bc. public static void echo() {
while(inbound.isOpen()) {
WebSocketEvent e = await(inbound.nextEvent());

for(String quit: TextFrame.and(Equals("quit")).match(e)) {
outbound.send("Bye!");
disconnect();
}

for(String msg: TextFrame.match(e)) {
outbound.send("Echo: %s", frame.textData);
}

for(WebSocketClose closed: SocketClosed.match(e)) {
Logger.info("Socket closed!");
}
}
}

p(note). **Continuing the discussion**

Next, doing %(next)"Ajax request":ajax%.
2 changes: 1 addition & 1 deletion documentation/manual/cache.textile
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,4 @@ memcached.2.host=127.0.0.1:11212

p(note). **Continuing the discussion**

Now we’ll check how to perform operations outside any HTTP request using %(next)"Jobs":jobs%.
Learn about %(next)"Sending emails":emails%.
5 changes: 4 additions & 1 deletion documentation/manual/dependency.textile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
h1. Dependency management
h1. Dependencies management

Play’s dependency management system allows you to express your application’s external dependencies in a single **dependencies.yml** file.

Expand Down Expand Up @@ -274,3 +274,6 @@ repositories:
- org.drools -> *


p(note). **Continuing the discussion**

Next: %(next)"Database evolutions":evolutions%.
4 changes: 0 additions & 4 deletions documentation/manual/deployment.textile
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,3 @@ h2. <a name="stax">Stax cloud hosting platform</a>
Easy deployment to the "Stax cloud hosting platform":http://www.stax.net: again nothing could be easier. Install the Stax module and deploy within seconds.

Refer to the "module documentation":http://www.playframework.org/modules/stax-1.0.1/home for more information.

p(note). **Continuing the discussion**

Next: %(next)"Production deployment":production%.
20 changes: 11 additions & 9 deletions documentation/manual/emails.textile
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ email.addTo("[email protected]");
email.setFrom([email protected]", "Nicolas");
email.setSubject("Test email with inline image");
// embed the image and get the content id
URL url = new URL("http://www.zenexity.fr/wp-content/themes/zenexity/images/logo.png");
URL url = new URL("http://www.zenexity.fr/wp-content/themes/images/logo.png");
String cid = email.embed(url, "Zenexity logo");
// set the html message
email.setHtmlMsg("<html>Zenexity logo - <img src=\"cid:"+cid+"\"></html>");
// set the alternative message
email.setTextMsg("Your email client does not support HTML messages, too bad :(");
email.setTextMsg("Your email client does not support HTML, too bad :(");

For more information see the "Commons Email documentation":http://commons.apache.org/email/userguide.html.

Expand Down Expand Up @@ -76,8 +76,11 @@ The template for the lostPassword method could look like this:

**app/views/Mails/lostPassword.html**

bc. <html><body><head>...</head><body><img src="mycompany.com/images"/><p>Hello ${user.name},<br/>
Your new password is <b>${newpassword}</b>.
bc. <html>
<body><head>...</head><body>
<img src="mycompany.com/images"/>
<p>
Hello ${user.name}, Your new password is <b>${newpassword}</b>.
</p>
</body>
</html>
Expand All @@ -95,7 +98,7 @@ The template for the lostPassword method could look like this:

**app/views/Mails/lostPassword.txt**

bc.Hello ${user.name},
bc. Hello ${user.name},

Your new password is ${newpassword}.

Expand All @@ -107,15 +110,15 @@ h3. Links to your application in e-mail

Your can include links to your application in e-mails like this:

bc.@@{application.index}
bc. @@{application.index}

If you send mails from Jobs you have to configure **application.baseUrl** in **application.conf**.
**application.baseUrl** must be a valid external baseurl to your application.

If the website playframework.org where to send you an e-mail from inside a Job, its configuration
would look like this:

bc.application.baseUrl=http://www.playframework.org/
bc. application.baseUrl=http://www.playframework.org/

h2. <a name="smtp">SMTP configuration</a>

Expand Down Expand Up @@ -161,7 +164,6 @@ mail.smtp.user=yourGmailLogin
mail.smtp.pass=yourGmailPassword
mail.smtp.channel=ssl


p(note). **Continuing the discussion**

Next: %(next)"Security guide":security%.
Now we shall move on to %(next)"Testing the application":test%.
Loading

0 comments on commit 0f513ea

Please sign in to comment.