-
Notifications
You must be signed in to change notification settings - Fork 60
/
MoreBigPipeExamples.java
151 lines (133 loc) · 7.76 KB
/
MoreBigPipeExamples.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package controllers;
import com.fasterxml.jackson.databind.JsonNode;
import com.ybrikman.ping.javaapi.bigpipe.BigPipe;
import com.ybrikman.ping.javaapi.bigpipe.HtmlPagelet;
import com.ybrikman.ping.javaapi.bigpipe.HtmlStreamHelper;
import com.ybrikman.ping.javaapi.bigpipe.JsonPagelet;
import com.ybrikman.ping.javaapi.bigpipe.Pagelet;
import com.ybrikman.ping.javaapi.bigpipe.PageletRenderOptions;
import data.Response;
import helper.FakeServiceClient;
import data.FakeServiceClient$;
import play.libs.F;
import play.mvc.Controller;
import play.mvc.Result;
import play.twirl.api.Html;
import javax.inject.Inject;
/**
* A few more BigPipe examples
*/
public class MoreBigPipeExamples extends Controller {
private final FakeServiceClient serviceClient;
@Inject
public MoreBigPipeExamples(FakeServiceClient serviceClient) {
this.serviceClient = serviceClient;
}
/**
* Renders the exact same page as WithBigPipe#index, but this time with server-side rendering. This will render all
* pagelets server-side and send them down in-order. The page load time will be longer than with out-of-order
* client-side rendering (albeit still faster than not using BigPipe at all), but the advantage is that server-side
* rendering does not depend on JavaScript, which is important for certain use cases (e.g. older browsers, search
* engine crawlers, SEO).
*
* @return
*/
public Result serverSideRendering() {
// Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls
// will be fast, some medium, and some slow.
F.Promise<Response> profilePromise = serviceClient.fakeRemoteCallMedium("profile");
F.Promise<Response> graphPromise = serviceClient.fakeRemoteCallMedium("graph");
F.Promise<Response> feedPromise = serviceClient.fakeRemoteCallSlow("feed");
F.Promise<Response> inboxPromise = serviceClient.fakeRemoteCallSlow("inbox");
F.Promise<Response> adsPromise = serviceClient.fakeRemoteCallFast("ads");
F.Promise<Response> searchPromise = serviceClient.fakeRemoteCallFast("search");
// Convert each Promise into a Pagelet which will be rendered as HTML as soon as the data is available.
Pagelet profile = new HtmlPagelet("profile", profilePromise.map(views.html.helpers.module::apply));
Pagelet graph = new HtmlPagelet("graph", graphPromise.map(views.html.helpers.module::apply));
Pagelet feed = new HtmlPagelet("feed", feedPromise.map(views.html.helpers.module::apply));
Pagelet inbox = new HtmlPagelet("inbox", inboxPromise.map(views.html.helpers.module::apply));
Pagelet ads = new HtmlPagelet("ads", adsPromise.map(views.html.helpers.module::apply));
Pagelet search = new HtmlPagelet("search", searchPromise.map(views.html.helpers.module::apply));
// Use BigPipe to compose the pagelets and render them immediately using a streaming template. Note that we're using
// ServerSide rendering in this case.
BigPipe bigPipe = new BigPipe(PageletRenderOptions.ServerSide, profile, graph, feed, inbox, ads, search);
return ok(HtmlStreamHelper.toChunks(views.stream.withBigPipe.apply(bigPipe, profile, graph, feed, inbox, ads, search)));
}
/**
* Instead of rendering each pagelet server-side with Play's templating, you can send back JSON and render each
* pagelet with a client-side templating library such as mustache.js
*
* @return
*/
public Result clientSideTemplating() {
// Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls
// will be fast, some medium, and some slow.
F.Promise<JsonNode> profilePromise = serviceClient.fakeRemoteCallJsonMedium("profile");
F.Promise<JsonNode> graphPromise = serviceClient.fakeRemoteCallJsonMedium("graph");
F.Promise<JsonNode> feedPromise = serviceClient.fakeRemoteCallJsonSlow("feed");
F.Promise<JsonNode> inboxPromise = serviceClient.fakeRemoteCallJsonSlow("inbox");
F.Promise<JsonNode> adsPromise = serviceClient.fakeRemoteCallJsonFast("ads");
F.Promise<JsonNode> searchPromise = serviceClient.fakeRemoteCallJsonFast("search");
Pagelet profile = new JsonPagelet("profile", profilePromise);
Pagelet graph = new JsonPagelet("graph", graphPromise);
Pagelet feed = new JsonPagelet("feed", feedPromise);
Pagelet inbox = new JsonPagelet("inbox", inboxPromise);
Pagelet ads = new JsonPagelet("ads", adsPromise);
Pagelet search = new JsonPagelet("search", searchPromise);
// Use BigPipe to compose the pagelets and render them immediately using a streaming template
BigPipe bigPipe = new BigPipe(PageletRenderOptions.ClientSide, profile, graph, feed, inbox, ads, search);
return ok(HtmlStreamHelper.toChunks(views.stream.clientSideTemplating.apply(bigPipe, profile, graph, feed, inbox, ads, search)));
}
/**
* Shows an example of how to handle an error that occurs part way through streaming a response to the browser. Since
* you've already sent back the headers with a 200 OK, it's too late to send back a 500 error page, so instead, you
* have to inject JavaScript into the stream that will show an appropriate error page.
*
* @return
*/
public Result errorHandling() {
// Make several fake service calls in parallel to represent fetching data from remote backends. Some of the calls
// will be fast, some medium, and some slow.
F.Promise<Response> profilePromise = serviceClient.fakeRemoteCallMedium("profile");
F.Promise<Response> graphPromise = serviceClient.fakeRemoteCallMedium("graph");
F.Promise<Response> feedPromise = serviceClient.fakeRemoteCallErrorSlow("feed");
F.Promise<Response> inboxPromise = serviceClient.fakeRemoteCallSlow("inbox");
F.Promise<Response> adsPromise = serviceClient.fakeRemoteCallFast("ads");
F.Promise<Response> searchPromise = serviceClient.fakeRemoteCallFast("search");
// Convert each Promise into a Pagelet which will be rendered as HTML as soon as the data is available. Note that
// the render method used here will also handle the case where the Future completes with an error by rendering an
// error message.
Pagelet profile = new HtmlPagelet("profile", render(profilePromise));
Pagelet graph = new HtmlPagelet("graph", render(graphPromise));
Pagelet feed = new HtmlPagelet("feed", render(feedPromise));
Pagelet inbox = new HtmlPagelet("inbox", render(inboxPromise));
Pagelet ads = new HtmlPagelet("ads", render(adsPromise));
Pagelet search = new HtmlPagelet("search", render(searchPromise));
// Use BigPipe to compose the pagelets and render them immediately using a streaming template
BigPipe bigPipe = new BigPipe(PageletRenderOptions.ClientSide, profile, graph, feed, inbox, ads, search);
return ok(HtmlStreamHelper.toChunks(views.stream.withBigPipe.apply(bigPipe, profile, graph, feed, inbox, ads, search)));
}
/**
* Shows an example of how BigPipe escapes the contents of your pagelets so
* they cannot break out of their containing HTML elements (which are
* intentionally invisible).
*
* @return
*/
public Result escaping() {
F.Promise<JsonNode> shouldBeEscapedPromise = serviceClient.fakeRemoteCallJsonFast(FakeServiceClient$.MODULE$.RESPONSE_TO_TEST_ESCAPING());
Pagelet shouldBeEscaped = new JsonPagelet("shouldBeEscaped", shouldBeEscapedPromise);
BigPipe bigPipe = new BigPipe(PageletRenderOptions.ClientSide, shouldBeEscaped);
return ok(HtmlStreamHelper.toChunks(views.stream.escaping.apply(bigPipe, shouldBeEscaped)));
}
/**
* When the given Future redeems, render it with the module template. If the Future fails, render it with the
* error template.
*
* @param dataPromise
* @return
*/
private F.Promise<Html> render(F.Promise<Response> dataPromise) {
return dataPromise.map(views.html.helpers.module::apply).recover(views.html.helpers.error::apply);
}
}