1.10.0 release notes
19th August 2021
🌟 New features
A new module
armeria-sangria
that integrates with Sangria GraphQL is introduced to let you serve a GraphQL request in Scala. #3703 #3704val schema: Schema[CharacterRepo, Unit] = Schema(Query) Server.builder() .service("/graphql", SangriaGraphqlService.builder(schema, new CharacterRepo) .enableTracing(true) .maxQueryDepth(10) .build())
You can now configure
WebClient
to automatically follow redirections. #2489 #3641WebClient.builder() .followRedirects() .build(); // Customize redirection policy RedirectConfig config = RedirectConfig.builder() .maxRedirects(10) .allownDomains("foo.com", "bar.com") .build(); WebClient.builder("https://example.com") .followRedirects(config) .build();
You can now recover a failed
HttpResponse
with a fallbackHttpResponse
. It would be useful for handling an error ofHttpResponse
in a decorator. #3674HttpResponse response = HttpResponse.ofFailure(new IllegalStateException("Oops...")); // The failed HttpResponse will be recovered by the fallback function. HttpResponse recovered = response.recover(cause -> HttpResponse.of("Fallback")); assert recovered.aggregate().join().contentUtf8().equals("Fallback");
You can now resume a failed
StreamMessage
with a fallbackStreamMessage
. #3674DefaultStreamMessage<Integer> stream = new DefaultStreamMessage<>(); stream.write(1); stream.write(2); stream.close(new IllegalStateException("Oops...")); StreamMessage<Integer> resumed = stream.recoverAndResume(cause -> StreamMessage.of(3, 4)); assert resumed.collect().join().equals(List.of(1, 2, 3, 4));
You can now transform an error of
StreamMessage
into another usingStreamMessage.mapError()
. #3668StreamMessage stream = StreamMessage.aborted(ClosedStreamException.get()); StreamMessage transformed = stream.mapError(ex -> { if (ex instanceof ClosedStreamException) { return new IllegalStateException(ex); } else { return ex; } });
You can now automatically encode an object into JSON for a response payload using
HttpResponse.ofJson()
. #3662MyObject myObject = ...; HttpResponse.ofJson(myObject); MyError myError = ...; HttpResponse.ofJson(HttpStatus.INTERNAL_SERVER_ERROR, myError);
You can now fluently inject
io.grpc.ServerInterceptor
s usingGrpcServiceBuilder.intercept()
.GrpcService.builder() .addService(myService) .intercept(myInterceptror) .build();
You can now easily add
Accept
headers withRequestHeadersBuilder.accept()
. #3704RequestHeaders.builder() .accept(MediaType.JSON) .accept(MediaType.PLAIN_TEXT);
You can now initiate graceful connection shutdown using
ServiceRequestContext.initiateConnectionShutdown()
. #3516 #3715You can now specify the default
ObjectMapper
usingJacksonObjectMapperProvider
. #3728public class MyObjectMapperProvider implements JacksonObjectMapperProvider { @Override public ObjectMapper newObjectMapper() { return JsonMapper .builder() .addModules(new KotlinModule()) .enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES) .build(); } }
You can now serve
application/grpc-web+proto
andapplication/grpc-web-text+proto
protocols usingAbstractUnaryGrpcService
. #3638 #3716gRPC trailers are available in the
RequestContext
for unary gRPC server and client. #3724 #3739UnaryGrpcClient client = ...; try (ClientRequestContextCaptor captor = Clients.newContextCaptor()) { client.execute("/com.example.MyService/UnaryCall", request); ClientRequestContext ctx = captor.get(); HttpHeaders trailers = GrpcWebTrailers.get(ctx); // 👈👈👈 }
You can now compress and decompress content with Brotli when using
EncodingService
,DecodingService
andDecodingClient
. #3544 #3686You can now create an
HttpResponseException
with a cause. #3674You can now easily capture
ServiceRequestContexts
usingServerExtension
in tests. #3648You can now suppress the inconsistent Netty version warning by specifying
-Dcom.linecorp.armeria.warnNettyVersions=false
. #3572 #3766
📃 Documentation
- You can now learn how to write a REST service with Armeria by walking through the
brand-new tutorials. #3420
- Special thanks to @freevie who volunteered for this.
📈 Improvements
HttpHeaders
now properly caches well-known HTTP headers' value objects. #3711 #3714ConcurrencyLimitTimeoutException
is now raised when a request times out inConcurrencyLimitingClient
. #3681OAuth2AuthorizationGrant
no longer uses locks for better performance. #3571 #3618
🛠️ Bug fixes
- You no longer see an
IllegalArgumentException
whenHttpRequest.aggregate()
andHttpResponse.aggregate()
fail with an exception. #3676 #3684 #3687 - You no longer see an incomplete
RequestLog
when anHttpStatusException
is raised. #3719 #3674 - You no longer see a
NullPointerException
when an HTTP/1 connection of a client is closed. #3729 #3747 - You no longer see a
NullPointerException
when aStreamMessage
is aborted. #3731 - No more unintended leakage of sensitive information when logging a
RequestLog
. #3758 - A request is now correctly reset when
ServiceRequestContext.cancel()
is called. #3674 - You no longer see a
ClassNotFoundException
in the shaded fastutil. #3713 - Armeria now accepts harmless yet illegal characters in a request path for HTTP/1 connections, like it did for HTTP/2. #3778
- You no longer see an
EmptyEndpointGroupException
whenEndpoints
are completely switched to newEndpoints
inHealthCheckedEndpointGroup
. #3637 - You can now correctly monitor a
BlockingTaskExecutor
with Micrometer. #3710 #3718 BlockingTaskExecutor
's idle threads are now properly terminated after timeout. #3718- You no longer see a
ClassNotFoundException
when aTransportType
is initialized. #3717 - The second
RetryRuleWithContent
is now properly applied to decide to retry or not when usingRetryingClient
. #3720 requestDurationNanos
,responseDurationNanos
andtotalDurationNanos
of a child log are properly calculated. #3749- An
IllegalArgumentException
raised in annotated service will be correctly logged byLoggingService
. #3674 - You no longer see
ClosedSessionException
when a large content is sent to an invalid path. #3674 ServerBuilder.localPort()
correctly binds both IPv4 and IPv6 loopback addresses. #3725 #3726- A
Publisher
or aCompletableFuture
returned by a service now properly gets a cancellation signal when the request is cancelled. #3690 #3691 - Brave's
ScopeDecorator
s now correctly propagates a decoratedScope
context when usingBraveService
andBraveClient
. #3408 #3430 - A
Server
in Spring integration is now automatically started bySmartLifecycle
. #3759 #3762 - You no longer see
CancelledSubscriptionException
when a WebFlux'sWebClient
receives an unexpectedContent-Type
header. #3730 #3750 TomcatService
now properly handles an exception with Spring Web'sExceptionHandler
s. #3447 #3732ErrorWebExceptionHandler
in WebFlux converts an exception to the response correctly. #3779JettyService
doesn't raise anEofException
anymore. #3688- Kotlin
jsr305=strict
option no longer raises a false positive error when a null value is returned from where nulls are allowed to return. #3751 - You no longer see null values when a case class is decoded from a JSON when using annotated services with Scala. 400 Bad Request will be returned instead. #3728
🏚️ Deprecations
-Dcom.linecorp.armeria.annotatedServiceExceptionVerbosity
andExceptionVerbosity
has been deprecated. #3674- You can use
LoggingService
that automatically logs exceptions using for your convenience; or manually log exceptions usingServerBuilder.exceptionHandler()
.
- You can use
ObservableResponseConverterFunction.<init>(ResponseConverterFunction,ExceptionHandlerFunction)
has been deprecated in favor ofObservableResponseConverterFunction.<init>(ResponseConverterFunction)
. #3674HttpResponseException.of(HttpStatus)
andHttpResponseException.of(int)
has been deprecated in favor ofHttpStatusException
. #3674
☢️ Breaking changes
ExceptionHandler
now returnsHttpResponse
instead ofAggregatedHttpResponse
. #3674// Before: ExceptionHandler handler = (ctx, cause) -> { if (cause instanceof IllegalArgumentException) { return AggregatedHttpResponse.of(HttpStatus.BAD_REQUEST); } return null; } // After: ExceptionHandler handler = (ctx, cause) -> { if (cause instanceof IllegalArgumentException) { return HttpResponse.of(HttpStatus.BAD_REQUEST); // 👈👈👈 } return null; }
RequestContext
is added as the first parameter ofGrpcStatusFunction
. #3692 #3693// Before: GrpcService.builder() .exceptionMapping((throwable, metadata) -> { if (throwable instanceof IllegalArgumentException) { return Status.INVALID_ARGUMENT; } return null; }); // After: GrpcService.builder() .exceptionMapping((ctx, throwable, metadata) -> { // 👈👈👈 if (throwable instanceof IllegalArgumentException) { return Status.INVALID_ARGUMENT; } return null; });
JacksonModuleProvider
has been removed in favor ofJacksonObjectMapperProvider
.ResponseConverterFunctionProvider.createResponseConverterFunction(Type,ResponseConverterFunction,ExceptionHandlerFunction)
has been removed in favor ofResponseConverterFunctionProvider.createResponseConverterFunction()?full
. #3674OAuth2AuthorizationGrant.withAuthorization()
has been removed. #3618
⛓ Dependencies
- Curator 5.1.0 → 5.2.0
- Dropwizard 2.0.23 → 2.0.24
- Dropwizard Metrics 4.2.2 → 4.2.3
- Jackson 2.12.3 → 2.12.4
- Jetty 9.4.42 → 9.4.23
- GraphQL-Java 16.2 → 17.1
- gRPC-Java 1.38.1 → 1.40.0
- Kotlin 1.5.10 → 1.5.21
- kotlinx-coroutines-core 1.5.0 → 1.5.1
- Logback 1.2.3 → 1.2.5
- Micrometer 1.7.1 → 1.7.2
- Netty 4.1.65 → 4.1.66
- netty-tcnative-boringssl-static 2.0.39 → 2.0.40
- netty-transport-native-io_uring 0.0.5 → 0.0.8
- java-jwt 3.16.0 → 3.18.1
- protobuf-java 3.12.0 → 3.17.2
- Reactor 3.4.7 → 3.4.9
- RESTeasy 4.6.1 → 4.7.1
- RxJava 3.0.13 → 3.1.0
- scala-collection-compat 2.4.4 → 2.5.0
- ScalaPB 0.11.4 → 0.11.5
- ScalaPB json4s 0.11.1 → 0.12.0
- SLF4J 1.7.31 → 1.7.32
- Spring 5.3.8 → 5.3.9
- Spring Boot 2.5.2 → 2.5.3
- ZooKeeper 3.6.2 → 3.6.3