BAEL-3353: Asynchronous HTTP Programming with Play Framework (#8349)
* project creation and added ws library * BAEL-3353: pushing code for article * BAEL-3353: changing test names * BAEL-3353: removed unused imports * BAEL-3353: applied review changes and fixed unit test bug * BAEL-3353: added more examples to PR * BAEL-3353: updated test following feedback * BAEL-3353: add new unit test for large data download * BAEL-3353: fixed PR following feedback
This commit is contained in:
parent
53b58ab36b
commit
59623b094e
|
@ -0,0 +1,43 @@
|
||||||
|
package controllers;
|
||||||
|
|
||||||
|
import play.data.Form;
|
||||||
|
import play.data.FormFactory;
|
||||||
|
import play.mvc.Controller;
|
||||||
|
import play.mvc.Result;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
// Add the following to conf/routes
|
||||||
|
/*
|
||||||
|
GET /$model;format="camel"$ controllers.$model;format="Camel"$Controller.$model;format="camel"$Get
|
||||||
|
POST /$model;format="camel"$ controllers.$model;format="Camel"$Controller.$model;format="camel"$Post
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $model;format="Camel"$ form controller for Play Java
|
||||||
|
*/
|
||||||
|
public class $model;format="Camel"$Controller extends Controller {
|
||||||
|
|
||||||
|
private final Form<$model;format="Camel"$Data> $model;format="camel"$Form;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public $model;format="Camel"$Controller(FormFactory formFactory) {
|
||||||
|
this.$model;format="camel"$Form = formFactory.form($model;format="Camel"$Data.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result $model;format="camel"$Get() {
|
||||||
|
return ok(views.html.$model;format="camel"$.form.render($model;format="camel"$Form));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result $model;format="camel"$Post() {
|
||||||
|
Form<$model;format="Camel"$Data> boundForm = $model;format="camel"$Form.bindFromRequest();
|
||||||
|
if (boundForm.hasErrors()) {
|
||||||
|
return badRequest(views.html.$model;format="camel"$.form.render(boundForm));
|
||||||
|
} else {
|
||||||
|
$model;format="Camel"$Data $model;format="camel"$ = boundForm.get();
|
||||||
|
flash("success", "$model;format="Camel"$ " + $model;format="camel"$);
|
||||||
|
return redirect(routes.$model;format="Camel"$Controller.$model;format="camel"$Get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package controllers;
|
||||||
|
|
||||||
|
import play.data.validation.Constraints;
|
||||||
|
|
||||||
|
public class $model;format="Camel"$Data {
|
||||||
|
|
||||||
|
@Constraints.Required
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Constraints.Required
|
||||||
|
private Integer age;
|
||||||
|
|
||||||
|
public $model;format="Camel"$Data() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getAge() {
|
||||||
|
return age;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAge(Integer age) {
|
||||||
|
this.age = age;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("$model;format="Camel"$Data(%s, %s)", name, age);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
@($model;format="camel"$Form: Form[$model;format="Camel"$Data])
|
||||||
|
|
||||||
|
<h1>$model;format="camel"$ form</h1>
|
||||||
|
|
||||||
|
@flash.getOrDefault("success", "")
|
||||||
|
|
||||||
|
@helper.form(action = routes.$model;format="Camel"$Controller.$model;format="camel"$Post()) {
|
||||||
|
@helper.CSRF.formField
|
||||||
|
@helper.inputText($model;format="camel"$Form("name"))
|
||||||
|
@helper.inputText($model;format="camel"$Form("age"))
|
||||||
|
<input type="submit" value="submit"/>
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
description = Generates a Controller with form handling
|
||||||
|
model = user
|
|
@ -0,0 +1 @@
|
||||||
|
Temporary file until g8-scaffold will generate "test" directory
|
|
@ -0,0 +1,50 @@
|
||||||
|
package controllers;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import play.Application;
|
||||||
|
import play.filters.csrf.*;
|
||||||
|
import play.inject.guice.GuiceApplicationBuilder;
|
||||||
|
import play.mvc.*;
|
||||||
|
import play.test.WithApplication;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static play.mvc.Http.RequestBuilder;
|
||||||
|
import static play.mvc.Http.Status.OK;
|
||||||
|
import static play.test.Helpers.*;
|
||||||
|
import static play.api.test.CSRFTokenHelper.*;
|
||||||
|
|
||||||
|
public class $model;format="Camel"$ControllerTest extends WithApplication {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Application provideApplication() {
|
||||||
|
return new GuiceApplicationBuilder().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test$model;format="Camel"$Get() {
|
||||||
|
RequestBuilder request = new RequestBuilder()
|
||||||
|
.method(GET)
|
||||||
|
.uri("/$model;format="camel"$");
|
||||||
|
|
||||||
|
Result result = route(app, request);
|
||||||
|
assertEquals(OK, result.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test$model;format="Camel"$Post() {
|
||||||
|
HashMap<String, String> formData = new HashMap<>();
|
||||||
|
formData.put("name", "play");
|
||||||
|
formData.put("age", "4");
|
||||||
|
RequestBuilder request = addCSRFToken(new RequestBuilder()
|
||||||
|
.header(Http.HeaderNames.HOST, "localhost")
|
||||||
|
.method(POST)
|
||||||
|
.bodyForm(formData)
|
||||||
|
.uri("/$model;format="camel"$"));
|
||||||
|
|
||||||
|
Result result = route(app, request);
|
||||||
|
assertEquals(SEE_OTHER, result.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
logs
|
||||||
|
target
|
||||||
|
/.idea
|
||||||
|
/.idea_modules
|
||||||
|
/.classpath
|
||||||
|
/.project
|
||||||
|
/.settings
|
||||||
|
/RUNNING_PID
|
|
@ -0,0 +1,38 @@
|
||||||
|
package controllers;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import play.mvc.Controller;
|
||||||
|
import play.mvc.Http;
|
||||||
|
import play.mvc.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This controller contains an action to handle HTTP requests to the application's home page.
|
||||||
|
*/
|
||||||
|
public class HomeController extends Controller {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An action that renders an HTML page with a welcome message. The configuration in the
|
||||||
|
* <code>routes</code> file means that this method will be called when the application receives
|
||||||
|
* a
|
||||||
|
* <code>GET</code> request with a path of <code>/</code>.
|
||||||
|
*/
|
||||||
|
public Result index(Http.Request request) throws JsonProcessingException {
|
||||||
|
return ok(printStats(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String printStats(Http.Request request) throws JsonProcessingException {
|
||||||
|
Map<String, String[]> stringMap = request.body()
|
||||||
|
.asFormUrlEncoded();
|
||||||
|
Map<String, Object> map = ImmutableMap.of(
|
||||||
|
"Result", "ok",
|
||||||
|
"GetParams", request.queryString(),
|
||||||
|
"PostParams", stringMap == null ? Collections.emptyMap() : stringMap,
|
||||||
|
"Headers", request.getHeaders().toMap()
|
||||||
|
);
|
||||||
|
return new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(map);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
@()
|
||||||
|
|
||||||
|
@main("Welcome to Play") {
|
||||||
|
<h1>Welcome to Play!</h1>
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
@*
|
||||||
|
* This template is called from the `index` template. This template
|
||||||
|
* handles the rendering of the page header and body tags. It takes
|
||||||
|
* two arguments, a `String` for the title of the page and an `Html`
|
||||||
|
* object to insert into the body of the page.
|
||||||
|
*@
|
||||||
|
@(title: String)(content: Html)
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
@* Here's where we render the page title `String`. *@
|
||||||
|
<title>@title</title>
|
||||||
|
<link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")">
|
||||||
|
<link rel="shortcut icon" type="image/png" href="@routes.Assets.versioned("images/favicon.png")">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
@* And here's where we render the `Html` object containing
|
||||||
|
* the page content. *@
|
||||||
|
@content
|
||||||
|
|
||||||
|
<script src="@routes.Assets.versioned("javascripts/main.js")" type="text/javascript"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,12 @@
|
||||||
|
name := """async"""
|
||||||
|
organization := "com.example"
|
||||||
|
|
||||||
|
version := "1.0-SNAPSHOT"
|
||||||
|
|
||||||
|
lazy val root = (project in file(".")).enablePlugins(PlayJava)
|
||||||
|
|
||||||
|
scalaVersion := "2.13.1"
|
||||||
|
|
||||||
|
// comment out the original line
|
||||||
|
libraryDependencies += guice
|
||||||
|
libraryDependencies += javaWs
|
|
@ -0,0 +1,11 @@
|
||||||
|
# This is the main configuration file for the application.
|
||||||
|
# https://www.playframework.com/documentation/latest/ConfigFile
|
||||||
|
play.ws.followRedirects=false
|
||||||
|
play.ws.useragent=MyPlayApplication
|
||||||
|
play.ws.compressionEnabled=true
|
||||||
|
# time to wait for the connection to be established
|
||||||
|
play.ws.timeout.connection=30.seconds
|
||||||
|
# time to wait for data after the connection is open
|
||||||
|
play.ws.timeout.idle=30.seconds
|
||||||
|
# max time available to complete the request
|
||||||
|
play.ws.timeout.request=300.seconds
|
|
@ -0,0 +1,36 @@
|
||||||
|
<!-- https://www.playframework.com/documentation/latest/SettingsLogger -->
|
||||||
|
<configuration>
|
||||||
|
|
||||||
|
<conversionRule conversionWord="coloredLevel" converterClass="play.api.libs.logback.ColoredLevel" />
|
||||||
|
|
||||||
|
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
|
||||||
|
<file>${application.home:-.}/logs/application.log</file>
|
||||||
|
<encoder>
|
||||||
|
<pattern>%date [%level] from %logger in %thread - %message%n%xException</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%coloredLevel %logger{15} - %message%n%xException{10}</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="ASYNCFILE" class="ch.qos.logback.classic.AsyncAppender">
|
||||||
|
<appender-ref ref="FILE" />
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="ASYNCSTDOUT" class="ch.qos.logback.classic.AsyncAppender">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="play" level="INFO" />
|
||||||
|
<logger name="application" level="DEBUG" />
|
||||||
|
<logger name="controllers" level="DEBUG" />
|
||||||
|
|
||||||
|
<root level="WARN">
|
||||||
|
<appender-ref ref="ASYNCFILE" />
|
||||||
|
<appender-ref ref="ASYNCSTDOUT" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</configuration>
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Routes
|
||||||
|
# This file defines all application routes (Higher priority routes first)
|
||||||
|
# ~~~~
|
||||||
|
|
||||||
|
# An example controller showing a sample home page
|
||||||
|
GET / controllers.HomeController.index(request: Request)
|
||||||
|
POST / controllers.HomeController.index(request: Request)
|
||||||
|
|
||||||
|
# Map static resources from the /public folder to the /assets URL path
|
||||||
|
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
|
|
@ -0,0 +1 @@
|
||||||
|
sbt.version=1.3.3
|
|
@ -0,0 +1,7 @@
|
||||||
|
// The Play plugin
|
||||||
|
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.7.3")
|
||||||
|
|
||||||
|
// Defines scaffolding (found under .g8 folder)
|
||||||
|
// http://www.foundweekends.org/giter8/scaffolding.html
|
||||||
|
// sbt "g8Scaffold form"
|
||||||
|
addSbtPlugin("org.foundweekends.giter8" % "sbt-giter8-scaffold" % "0.11.0")
|
Binary file not shown.
After Width: | Height: | Size: 687 B |
|
@ -0,0 +1,232 @@
|
||||||
|
package controllers;
|
||||||
|
|
||||||
|
import static java.time.temporal.ChronoUnit.SECONDS;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static play.mvc.Http.Status.SERVICE_UNAVAILABLE;
|
||||||
|
|
||||||
|
import akka.Done;
|
||||||
|
import akka.actor.ActorSystem;
|
||||||
|
import akka.stream.ActorMaterializer;
|
||||||
|
import akka.stream.javadsl.Sink;
|
||||||
|
import akka.util.ByteString;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
import java.util.concurrent.CompletionStage;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import play.Application;
|
||||||
|
import play.inject.guice.GuiceApplicationBuilder;
|
||||||
|
import play.libs.concurrent.Futures;
|
||||||
|
import play.libs.ws.WSClient;
|
||||||
|
import play.libs.ws.WSResponse;
|
||||||
|
import play.libs.ws.ahc.AhcCurlRequestLogger;
|
||||||
|
import play.mvc.Result;
|
||||||
|
import play.mvc.Results;
|
||||||
|
import play.test.WithServer;
|
||||||
|
|
||||||
|
public class HomeControllerTest extends WithServer {
|
||||||
|
|
||||||
|
private final Logger log = LoggerFactory.getLogger(HomeControllerTest.class);
|
||||||
|
private String url;
|
||||||
|
private int port;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Application provideApplication() {
|
||||||
|
return new GuiceApplicationBuilder().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
OptionalInt optHttpsPort = testServer.getRunningHttpsPort();
|
||||||
|
if (optHttpsPort.isPresent()) {
|
||||||
|
port = optHttpsPort.getAsInt();
|
||||||
|
url = "https://localhost:" + port;
|
||||||
|
} else {
|
||||||
|
port = testServer.getRunningHttpPort()
|
||||||
|
.getAsInt();
|
||||||
|
url = "http://localhost:" + port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenASingleGetRequestWhenResponseThenBlockWithCompletableAndLog()
|
||||||
|
throws Exception {
|
||||||
|
WSClient ws = play.test.WSTestClient.newClient(port);
|
||||||
|
WSResponse wsResponse = ws.url(url)
|
||||||
|
.setRequestFilter(new AhcCurlRequestLogger())
|
||||||
|
.addHeader("key", "value")
|
||||||
|
.addQueryParameter("num", "" + 1)
|
||||||
|
.get()
|
||||||
|
.toCompletableFuture()
|
||||||
|
.get();
|
||||||
|
|
||||||
|
log.debug("Thread#" + Thread.currentThread()
|
||||||
|
.getId() + " Request complete: Response code = "
|
||||||
|
+ wsResponse.getStatus()
|
||||||
|
+ " | Response: " + wsResponse.getBody() + " | Current Time:"
|
||||||
|
+ System.currentTimeMillis());
|
||||||
|
assert (HttpStatus.SC_OK == wsResponse.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenASingleGetRequestWhenResponseThenLog() throws Exception {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
WSClient ws = play.test.WSTestClient.newClient(port);
|
||||||
|
ws.url(url)
|
||||||
|
.setRequestFilter(new AhcCurlRequestLogger())
|
||||||
|
.addHeader("key", "value")
|
||||||
|
.addQueryParameter("num", "" + 1)
|
||||||
|
.get()
|
||||||
|
.thenAccept(r -> {
|
||||||
|
log.debug("Thread#" + Thread.currentThread()
|
||||||
|
.getId() + " Request complete: Response code = "
|
||||||
|
+ r.getStatus()
|
||||||
|
+ " | Response: " + r.getBody() + " | Current Time:" + System.currentTimeMillis());
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
"Waiting for requests to be completed. Current Time: " + System.currentTimeMillis());
|
||||||
|
latch.await(5, TimeUnit.SECONDS );
|
||||||
|
assertEquals(0, latch.getCount());
|
||||||
|
log.debug("All requests have been completed. Exiting test.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenASinglePostRequestWhenResponseThenLog() throws Exception {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
WSClient ws = play.test.WSTestClient.newClient(port);
|
||||||
|
ws.url(url)
|
||||||
|
.setContentType("application/x-www-form-urlencoded")
|
||||||
|
.post("key1=value1&key2=value2")
|
||||||
|
.thenAccept(r -> {
|
||||||
|
log.debug("Thread#" + Thread.currentThread()
|
||||||
|
.getId() + " Request complete: Response code = "
|
||||||
|
+ r.getStatus()
|
||||||
|
+ " | Response: " + r.getBody() + " | Current Time:" + System.currentTimeMillis());
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
"Waiting for requests to be completed. Current Time: " + System.currentTimeMillis());
|
||||||
|
latch.await(5, TimeUnit.SECONDS );
|
||||||
|
assertEquals(0, latch.getCount());
|
||||||
|
log.debug("All requests have been completed. Exiting test.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenMultipleRequestsWhenResponseThenLog() throws Exception {
|
||||||
|
CountDownLatch latch = new CountDownLatch(100);
|
||||||
|
WSClient ws = play.test.WSTestClient.newClient(port);
|
||||||
|
IntStream.range(0, 100)
|
||||||
|
.parallel()
|
||||||
|
.forEach(num ->
|
||||||
|
ws.url(url)
|
||||||
|
.setRequestFilter(new AhcCurlRequestLogger())
|
||||||
|
.addHeader("key", "value")
|
||||||
|
.addQueryParameter("num", "" + num)
|
||||||
|
.get()
|
||||||
|
.thenAccept(r -> {
|
||||||
|
log.debug(
|
||||||
|
"Thread#" + num + " Request complete: Response code = " + r.getStatus()
|
||||||
|
+ " | Response: " + r.getBody() + " | Current Time:"
|
||||||
|
+ System.currentTimeMillis());
|
||||||
|
latch.countDown();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
"Waiting for requests to be completed. Current Time: " + System.currentTimeMillis());
|
||||||
|
latch.await(5, TimeUnit.SECONDS );
|
||||||
|
assertEquals(0, latch.getCount());
|
||||||
|
log.debug("All requests have been completed. Exiting test.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenLongResponseWhenTimeoutThenHandle() throws Exception {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
WSClient ws = play.test.WSTestClient.newClient(port);
|
||||||
|
Futures futures = app.injector()
|
||||||
|
.instanceOf(Futures.class);
|
||||||
|
CompletionStage<Result> f = futures.timeout(
|
||||||
|
ws.url(url)
|
||||||
|
.setRequestTimeout(Duration.of(1, SECONDS))
|
||||||
|
.get()
|
||||||
|
.thenApply(result -> {
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000L);
|
||||||
|
return Results.ok();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return Results.status(
|
||||||
|
SERVICE_UNAVAILABLE);
|
||||||
|
}
|
||||||
|
}), 1L, TimeUnit.SECONDS
|
||||||
|
);
|
||||||
|
CompletionStage<Object> res = f.handleAsync((result, e) -> {
|
||||||
|
if (e != null) {
|
||||||
|
log.error("Exception thrown", e);
|
||||||
|
latch.countDown();
|
||||||
|
return e.getCause();
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
res.thenAccept(result -> assertEquals(TimeoutException.class, result));
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
"Waiting for requests to be completed. Current Time: " + System.currentTimeMillis());
|
||||||
|
latch.await(5, TimeUnit.SECONDS );
|
||||||
|
assertEquals(0, latch.getCount());
|
||||||
|
log.debug("All requests have been completed. Exiting test.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenMultigigabyteResponseConsumeWithStreams() throws Exception {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
final ActorSystem system = ActorSystem.create();
|
||||||
|
final ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||||
|
final Path path = Files.createTempFile("tmp_", ".out");
|
||||||
|
|
||||||
|
WSClient ws = play.test.WSTestClient.newClient(port);
|
||||||
|
log.info("Starting test server on url: " + url);
|
||||||
|
ws.url(url)
|
||||||
|
.stream()
|
||||||
|
.thenAccept(
|
||||||
|
response -> {
|
||||||
|
try {
|
||||||
|
OutputStream outputStream = java.nio.file.Files.newOutputStream(path);
|
||||||
|
Sink<ByteString, CompletionStage<Done>> outputWriter =
|
||||||
|
Sink.foreach(bytes -> {
|
||||||
|
log.info("Reponse: " + bytes.utf8String());
|
||||||
|
outputStream.write(bytes.toArray());
|
||||||
|
});
|
||||||
|
|
||||||
|
response.getBodyAsSource()
|
||||||
|
.runWith(outputWriter, materializer);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("An error happened while opening the output stream", e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.whenComplete((value, error) -> latch.countDown());
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
"Waiting for requests to be completed. Current Time: " + System.currentTimeMillis());
|
||||||
|
latch.await(5, TimeUnit.SECONDS );
|
||||||
|
assertEquals(0, latch.getCount());
|
||||||
|
log.debug("All requests have been completed. Exiting test.");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue