1.9.0 release notes
23rd June 2021
🌟 New features
A new module
armeria-graphql
integrates with GraphQL Java to let you serve GraphQL requests. #3318 #3373ServerBuilder sb = ...; sb.service("/graphql", GraphqlService.builder() .schemaFile(schemaFiles) .runtimeWiring(w -> ...) .configureDataLoaderRegistry(registry -> ...) ... .build());
You can now transform the objects of
HttpRequest
andHttpResponse
using a function. #3606 #3624HttpRequest req1 = ...; HttpRequest req2 = req1.mapHeaders(headers -> headers.toBuilder() .add("TraceId", "1") .build()) .mapData(data -> HttpData.ofUtf8(data.toStringUtf8() + '\n')) .mapTrailers(trailers -> trailers.toBuilder() .add("trailer-key", "val") .build()) HttpResponse res1 = ...; HttpResponse res2 = res1.mapHeaders(headers -> headers.toBuilder() .add("SessionId", "123") .build()) .mapInformational(informational -> { return informational.withMutations(builder -> { builder.add(HttpHeaderNames.USER_AGENT, "Armeria"); }); }) .mapData(data -> HttpData.ofUtf8(data.toStringUtf8() .replaceAll("\n", "<br/>"))) .mapTrailers(trailers -> trailers.toBuilder() .add("result", "0") .build());
You can now collect the all elements of a
StreamMessage
withStreamMessage.collect()
. #3603StreamMessage<Integer> stream = StreamMessage.of(1, 2, 3); CompletableFuture<List<Integer>> collected = stream.collect(); assert collected.join().equals(List.of(1, 2, 3));
You can exclude a
Route
from anotherRoute
when configuring a service. #2737 #3562ServerBuilder sb = ...; sb.route() .pathPrefix("/api") .exclude("prefix:/api/admin") .exclude(Route.builder() .pathPrefix("/api/v1") .consumes(MediaType.JSON) .build()) .build(apiService); sb.routeDecorator() .pathPrefix("/api") .exclude("prefix:/api/admin") .build(MetricCollectingService.newDecorator());
You can now update the service bindings without restarting a
Server
usingServer.reconfigure()
. #3041 #3362 Note that there are some limitations on theServer
reconfiguration. Please check out #3041 for details.Server server = ...; server.reconfigure(sb -> { sb.service("/dynamic", new LazyService()); });
You can now use
RequestOptions
to configure various options at request level. #3593HttpRequest req = ...; WebClient client = ...; RequestOptions options = RequestOptions.builder() .responseTimeout(Duration.ofSeconds(20)) .writeTimeout(Duration.ofSeconds(5)) .maxResponseLength(2048) .attr(foo, "bar") .build(); // Specify RequestOptions when sending a request. client.execute(req, options); // You can also specify the options when building a request // using WebClientRequestPreparation. client.prepare() .get("/some/path") .responseTimeout(Duration.ofSeconds(20)) .writeTimeout(Duration.ofSeconds(5)) .maxResponseLength(2048) .attr(foo, "bar") .execute();
You can now fluently build a
BlockingTaskExecutor
usingBlockingTaskExecutorBuilder
. #3315 #3389BlockingTaskExecutor.builder() .daemon(true) .numThreads(500) .threadNamePrefix("my-blocking-task-executor") .build();
You can now easily get a boolean value from
QueryParams
andHttpHeaders
. #3600HttpHeaders headers = HttpHeaders.of("checked", "true", "valid", "1"); assert headers.getBoolean("checked"); assert headers.getBoolean("valid"); QueryParams params = QueryParams.of("checked", "false", "valid", "0"); assert !params.getBoolean("checked"); assert !params.getBoolean("valid");
You can now conveniently configure a loopback port with
ServerBuilder.localPort()
. #3599 #3601Server.builder() .localPort(8080) .localPort(9090, SessionProtocol.HTTP, SessionProtocol.HTTPS);
All Jackson modules in the class loader are registered automatically by default via Jackson's built-in SPI mechanism for your convenience.
TCP user timeout option is now enabled by default when an idle timeout is enabled. #3509
TCP_KEEPIDLE
andTCP_KEEPINTVL
options are enabled when a PING interval is greater than 0.
You can now get the last value of a header or a query parameter using
HttpHeaders.getLast()
andQueryParams.getLast()
. #3438 #3568HttpHeaders headers = HttpHeaders.builder() .add(HttpHeaderNames.X_FORWARDED_FOR, List.of("203.0.113.195", "150.172.238.178")) .build(); assert headers.getLast(HttpHeaderNames.X_FORWARDED_FOR) .equals("150.172.238.178"); QueryParams params = QueryParams.builder() .add("items", List.of("1", "2", "3")) .build(); assert params.getLastInt("items").equals(3);
ContentTooLargeException
now provides you the following 3 properties about why the exception was raised. #3520, #3616- The maximum allowed content length
- The content length specified in the
content-length
header - The number of bytes transferred so far
You can now convert a
Multipart
into anHttpResponse
. #3642You can now warm up an
EventLoopGroup
usingEventLoopGroups.warmUp()
. #3363 #3610You can now figure out the number of connections managed by a
ClientFactory
withClientFactory.numConnections()
. #3596 #3613You can now specify the number of event loop threads and blocking task threads more conveniently. #3597 #3602
Server.builder() .workerGroup(3) .blockingTaskExecutor(10) ... .build(); ClientFactory.builder() .workerGroup(3) .build();
You can now clear the mocking state of a
MockWebServerExtension
usingMockWebServerExtension.reset()
. #3629
📈 Improvements
- Various optimizations for
StreamMessage
implementations. #3585 #3584 - You can now use an optimized context-aware
CompletableFuture
for Java 12 and above. #2119 #3615 - Various multipart MIME types were added to
MediaType
. #3642
🛠️ Bug fixes
ByteBuf
s are no longer leaked when using:- Spring WebFlux integration with compression #3608 #3617; or
StreamMessageDuplicator
#3476 #3583
- You no longer see a
ResponseTimeoutException
withCircuitBreakerClient
when a large number of messages are received from a response. #3631 - You can now set the service name of an annotated service using
the
ServerBuilder.defaultServiceNaming()
. #3589 - You no longer see a
ClosedStreamException
when anEndpointGroup
is empty. #3381 #3636 - Late
Subscriber
s correctly run on the specified executor. #3623 #3632 - An exception raised in
THttpService
is now properly peeled usingExceptions.peel()
. #3588 - You no longer see an
UnsatisfiedLinkError
when a Netty JAR without native libraries is included. #3485 #3607 - Additional headers are now correctly updated to
RequestOnlyLog.requestHeaders()
. #3579 #3581 - A timeout is more precisely scheduled for a request or a response. #3570
- You no longer see a duplicate
Content-Type
header when usingMultipart
. #3642
⛓ Dependencies
- Bouncy Castle 1.68 → 1.69
- Dropwizard 2.0.21 → 2.0.23
- Dropwizard Metrics 4.1.21 → 4.2.2
- Eureka 1.10.13 → 1.10.15
- gRPC-Java 1.37.0 → 1.38.1
- gRPC-Kotlin 1.0.0 → 1.1.0
- JBoss logging 3.4.1 → 3.4.2
- Jetty 9.4.39 → 9.4.42
- Kotlin 1.5.0 → 1.5.10
- Kotlin Coroutines 1.4.3 → 1.5.0
- Netty 4.1.63 → 4.1.65
- netty-tcnative-boringssl-static 2.0.38 → 2.0.39
- Prometheus 0.10.0 → 0.11.0
- Reactor 3.4.6 → 3.4.7
- RESTEasy 4.6.0 → 4.6.1
- RxJava 3.0.12 → 3.0.13
- Scala
- 2.12.13 → 2.12.14
- 2.13.5 → 2.13.6
- ScalaPB runtime 0.11.2 → 0.11.3
- ScalaPB json4s 0.11.0 → 0.11.1
- SLF4J 1.7.30 → 1.7.31
- Spring 5.3.7 → 5.3.8
- Spring Boot 2.4.5 → 2.5.1
- Tomcat
- 8.5.64 → 8.5.68
- 9.0.44 → 9.0.46
- Thrift 0.14.1 → 0.14.2