The whole refactoring for better asynchronism management in Play 1.2,
is almost finished and available in the Play git master.
These new features cover several points I will introduce here:
- Promises
- A new Functional style library (play.libs.F)
- await(…)
- Continuations
- Response streaming
- WebSockets
- Pattern matching
First, it is already possible to achieve asynchronism with Play 1.1
using Java Future and waitFor(...) and suspend(...) controller
methods. However these primitives was not really easy to use. That's
why we've worked on a complete set of new coherent features for Play
1.2.
-- Promises:
In Play 1.2 we introduce Promise that is the Play's custom Future type
(Promise was named Task in previous commits on master) . In fact a
Promise<T> is also a Future<T> so you can use it as a standard Future.
But it has also a very interesting property: the ability to register
callback using onRedeem(…) that will be called as soon as the promised
value is available. It allows the framework to register itself on them
and to reschedule the request invocation as soon as possible.
Promises are used everywhere in Play in place of Future (for Jobs,
WS.async, etc…)
Promises can be combined in several way. For example:
- Promise p = Promise.waitAll(p1, p2, p3)
- Promise p = Promise.waitAny(p1, p2, p3)
- Promise p = Promise.waitEither(p1, p2, p3)
-- play.libs.F:
The Promise type is part of new library (play.libs.F) that introduces
several useful constructs coming from functional programming. These
constructs are used to handle complex cases that I will expose just
later. For those that are accustomed to functional programming we
provide:
- Option<T> (a T value that can be or not set)
- Either<A,B> (contains either a A value or a B value)
- Tuple<A,B> (contains both A and B values)
-- await(…):
So, now when your application code returns values that are not yet
available using a Promise<T>, you want to be able to ask Play to wait
for this promised result to be available before resuming your request.
It allows your code to say explicitly: "I'm waiting for a result that
will be available later", and so the framework to handle it as "Ok I
will stop your code, reuse the thread to serve other requests, and
resume your code as soon as the promised value you wait for is
available".
It is the point of the await(…) controller methods:
Promise<String> delayedResult = veryLongComputation(…);
await(delayedResult);
-- 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 in Play 1.2 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:
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:
public static void loopWithoutBlocking() {
for(int i=0; i<10; i++) {
System.out.println(i);
await("1s");
}
renderText("Loop finished");
}
And using only 1 thread (which is the default in development mode) to
process requests, Play 1.2 will be able to run concurrently these
loops for several requests at the same time.
-- 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:
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 1.2 will be able to
process simultaneously several requests using a single thread, sending
back the generated data to the client as soon as they are available.
-- 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:
new Socket("ws://localhost:9000/helloSocket?name=Guillaume")
On the Play side you declare a WS route:
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;
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:
public class MyWebSocket extends WebSocketController {
public static void echo() {
while(true) {
WebSocketEvent e = await(inbound.nextEvent());
if(e instanceof WebSocketFrame) {
WebSocketFrame frame = (WebSocketFrame)e;
if(!e.isBinary) {
outbound.send("Echo: %s", frame.textData);
}
}
}
}
}
-- Pattern Matching:
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 felt that we needed pattern matching in Java.
Unfortunately Java has not built-in pattern matching, and because of
the lack of function, it is difficult to add it as a library. Anyway
we've worked on a solution that is not so bad.
Our idea was to use the latest 'for loop' syntax to achieve basic
pattern matching for Java. Pattern matching must both check if your
object matches the required conditions and extract the interesting
value. The Pattern matching library for Play is part of the
play.libs.F library.
Let's see a first example: you have a reference of type Object and you
want to check that it is a string that starts by "command:". The
standard way would be:
Object o = anything();
if(o instanceof String && ((String)o).startsWith("command:")) {
String s = (String)o;
System.out.println(s.toUpperCase());
}
Using the Play pattern matching library, you can write it as:
for(String s: String.and(StartsWith("command:")).match(o)) {
System.out.println(s.toUpperCase());
}
The for loop is executed once, only if the condition is met, and it
automatically extracts the String value without the need of casting.
Because there is no explicit cast, all this thing is type safe, and
checked by the compiler.
We'll provide a good set of standard matchers, you can easily add
yours, and libraries like will WebSockets provide theirs.
So for example, we can rewrite our previous echo sample as:
public static void echo() {
while(true) {
WebSocketEvent e = await(inbound.nextEvent());
for(String msg: TextFrame.match(e)) {
outbound.send("Echo: %s", frame.textData);
}
}
}
A more complete example of WebSocket is available in the new Chat application:
--
I'm sure that all these new features will make of Play 1.2 the best
option to build complicated asynchronous Web applications in Java.
Thanks to all those that have contributed different parts of these new
features.
--
Guillaume Bort, http://guillaume.bort.fr
Greate job !
Daniel
> --
> You received this message because you are subscribed to the Google Groups "play-framework" group.
> To post to this group, send email to play-fr...@googlegroups.com.
> To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.
>
>
> --
> You received this message because you are subscribed to the Google Groups
> "play-framework" group.
> To post to this group, send email to play-fr...@googlegroups.com.
> To unsubscribe from this group, send email to
> play-framewor...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/play-framework?hl=en.
>
--
Guillaume Bort, http://guillaume.bort.fr
For anything work-related, use g...@zenexity.fr; for everything else,
write guillau...@gmail.com
About the features themselves, they seem to look like Scala quite a bit. As someone who is using Play with Scala, I'm not really sure if that's a good thing for me. I'll wait for others' opinions though...
Ok, but you can already do the same with WebSocket right?
Isn’t there an error on the pattern matching example? Wouldn’t it be that?
for(String s: ClassOf(String.class).and(StartsWith("command:")).match(o)) {
System.out.println(s.toUpperCase());
}
And, as others said, I wonder why you do not completely shift to Scala
instead of trying to bypass Java limitations… There are so many things
in Play! that just bypass Java limitations: public attributes on
models (easy to write as variables declarations with scala), groovy as
an expression language in the templates (since Java does not provide
Options nor a convenient collection API), and now the functionnal
library. All that is very very interesting since it gives Java
programmers more expressivity and power, but why don’t you just focus
on Scala?
About scala, from my point of view it is still too early to switch completely to it. I think that scala has the potential to become the new java in the next years but for not yet.
--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To post to this group, send email to play-fr...@googlegroups.com.
To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.
I don't want to be a pain, but I tested a bit with Apachebench and I noticed a few millisecond (1-3 ms) longer response times from the current 1.2 version vs. 1.1.1 (installed from the zip file). The tested apps were quite simple. Just FYI. I understand that 1.2 isn't ready yet...Of course I'm happy with all the activity with Play development (and I thank you for that), so the point is not to complain, just give some feedback, which I hope isn't totally worthless.
--
On Wed, Apr 6, 2011 at 10:30 AM, v6ak <v6...@volny.cz> wrote:
> That looks great. But I've a little question about WebSockets support.
> Since there are browsers that don't support it, it is good idea to
> implement an alternative. (Well, Play! can probably implement some of
> these alternatives, but it is not the point of this post. I'm about to
> do it myself and maybe provide other ways for premium uses only, since
> there is more overhead.)
>
> So, I'd implement it myself and it would be great to be able to
> implement it in the same Controller. There can be two ways:
> * implement both of them in the WebSocketController (IMHO unclean)
> * implement both of them in ordinar Controller (unsure if it works)
> What is the best way?
>
> Regards
> Vít Šesták 'v6ak'
>
> On Feb 11, 3:25 pm, Guillaume Bort <guillaume.b...@gmail.com> wrote:
>> Hi all,
>>
>> The whole refactoring for better asynchronism management in Play 1.2,
>> is almost finished and available in the Play git master.
>>
>> These new features cover several points I will introduce here:
>>
>> - Promises
>> - A new Functional style library (play.libs.F)
>> - await(...)
>> - Continuations
>> - Response streaming
>> - WebSockets
>> - Pattern matching
>>
>> First, it is already possible to achieve asynchronism with Play 1.1
>> using Java Future and waitFor(...) and suspend(...) controller
>> methods. However these primitives was not really easy to use. That's
>> why we've worked on a complete set of new coherent features for Play
>> 1.2.
>>
>> -- Promises:
>>
>> In Play 1.2 we introduce Promise that is the Play's custom Future type
>> (Promise was named Task in previous commits on master) . In fact a
>> Promise<T> is also a Future<T> so you can use it as a standard Future.
>> But it has also a very interesting property: the ability to register
>> callback using onRedeem(...) that will be called as soon as the promised
>> value is available. It allows the framework to register itself on them
>> and to reschedule the request invocation as soon as possible.
>>
>> Promises are used everywhere in Play in place of Future (for Jobs,
>> WS.async, etc...)
>>
>> Promises can be combined in several way. For example:
>>
>> - Promise p = Promise.waitAll(p1, p2, p3)
>> - Promise p = Promise.waitAny(p1, p2, p3)
>> - Promise p = Promise.waitEither(p1, p2, p3)
>>
>> -- play.libs.F:
>>
>> The Promise type is part of new library (play.libs.F) that introduces
>> several useful constructs coming from functional programming. These
>> constructs are used to handle complex cases that I will expose just
>> later. For those that are accustomed to functional programming we
>> provide:
>>
>> - Option<T> (a T value that can be or not set)
>> - Either<A,B> (contains either a A value or a B value)
>> - Tuple<A,B> (contains both A and B values)
>>
>> -- await(...):
>>
>> So, now when your application code returns values that are not yet
>> available using a Promise<T>, you want to be able to ask Play to wait
>> for this promised result to be available before resuming your request.
>> It allows your code to say explicitly: "I'm waiting for a result that
>> will be available later", and so the framework to handle it as "Ok I
>> will stop your code, reuse the thread to serve other requests, and
>> resume your code as soon as the promised value you wait for is
>> available".
>>
>> It is the point of the await(...) controller methods:
>>
>> Promise<String> delayedResult = veryLongComputation(...);
>> await(delayedResult);
>>
>> -- 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 in Play 1.2 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:
>>
>> 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:
>>
>> public static void loopWithoutBlocking() {
>> for(int i=0; i<10; i++) {
>> System.out.println(i);
>> await("1s");
>> }
>> renderText("Loop finished");
>>
>> }
>>
>> And using only 1 thread (which is the default in development mode) to
>> process requests, Play 1.2 will be able to run concurrently these
>> loops for several requests at the same time.
>>
>> -- 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:
>> it is easy to achieve using await(...) and continuations.
> --
> You received this message because you are subscribed to the Google Groups "play-framework" group.
> To post to this group, send email to play-fr...@googlegroups.com.
> To unsubscribe from this group, send email to play-framewor...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/play-framework?hl=en.
>
>
--
Guillaume Bort, http://guillaume.bort.fr
For anything work-related, use g...@zenexity.fr; for everything else,
write guillau...@gmail.com
On Fri, Apr 15, 2011 at 2:32 PM, Tony Xiao <tony...@gmail.com> wrote:
> Are there any additional references on how to use the libs.F matcher library
> in addition to those ones provided in the chat application example/
>