You're seeing the release note of an old version. Check out the latest release note.
v1.17.0
July 6, 2022
🌟 New features
-
You can now easily send and receive RESTful APIs using
RestClient. #4263- Java
RestClient restClient = RestClient.of("...");CompletableFuture<ResponseEntity<Customer>> response =restClient.get("/api/v1/customers/{customerId}").pathParam("customerId", "0000001").execute(Customer.class);
- Kotlin
val restClient: RestClient = RestClient.of("...");val response: ResponseEntity<Customer> =restClient.get("/api/v1/customers/{customerId}").pathParam("customerId", "0000001").execute<Customer>() // a suspend function
- Scala
val restClient: ScalaRestClient = ScalaRestClient("...")val response: Future[ResponseEntity[Result]] =restClient.post("/api/v1/customers").contentJson(new Customer(...)).execute[Result]()
- Java
-
You can now configure a timeout for an
Endpointselection of anDynamicEndpointGroup. #4246DnsAddressEndpointGroup delegate = DnsAddressEndpointGroup.of(...);HealthCheckedEndpointGroup healthGroup =HealthCheckedEndpointGroup.builder(delegate, "/health").selectionTimeout(Duration.ofSeconds(10)) // 👈👈👈.build(); -
You can now inject dependencies in an annotation using
DependencyInjector. #4006 #4202// Inject authClient that is needed to create the AuthDecorator.WebClient authClient = ...DependencyInjector injector =DependencyInjector.ofSingletons(new AuthDecorator(authClient));serverBuilder.dependencyInjector(dependencyInjector, true);// An annotated service that uses AuthDecorator.@Get("/foo")@Decorator(AuthDecorator.class)public FooResponse foo(FooRequest req) {// Authrorized request....}// authClient is injected.class AuthDecorator implements DecoratingHttpServiceFunction {AuthDecorator(WebClient authClient) { ... }@Overridepublic HttpResponse serve(HttpService delegate,ServiceRequestContext ctx,HttpRequest req)throws Exception {// Authorize the request....}}- Set
armeria.enable-auto-injectiontotrueto applySpringDependencyInjectorautomatically when using Spring Boot integration.// in application.ymlarmeria:ports:...enable-auto-injection: true // 👈👈👈
- Set
-
You can now customize the length of string fields or container fields of Thrift messages using
THttpServiceBuilderorThriftClientBuilder. #4024 #4226THttpService.builder().addService(new MyThriftService()).maxRequestStringLength(MAX_STRING_LENGTH).maxRequestContainerLength(MAX_CONTAINER_LENGTH).build();ThriftClients.builder("https://my.server.com").path("/thrift").maxResponseStringLength(MAX_STRING_LENGTH).maxResponseContainerLength(MAX_CONTAINER_LENGTH).build(HelloService.AsyncIface.class); -
You can now set attributes to an
EndpointusingAttributesandAttributesBuilder. #4241AttributeKey<String> region = AttributeKey.valueOf(MyAttrs.class, "region");Attributes attrs =Attributes.builder().set(region, "us-west-1").build();Endpoint endpointA = ...;Endpoint endpointB = endpointA.withAttrs(attrs);assert endpointB.attr(region).equals("us-west-1");assert !endpointA.equals(endpointB) -
You can now use
MultipartFileto get the actual filename of an uploaded file throughmultipart/form-data. #4262@Consumes(MediaTypeNames.MULTIPART_FORM_DATA)@Post("/upload")public HttpResponse upload(MultipartFile multipartFile) {// The name parameter of the "content-disposition" headerString name = multipartFile.name();// The filename parameter of the "content-disposition" headerString filename = multipartFile.filename();// The file that stores the multipart content.File file = multipartFile.file();...} -
You can now read a range of bytes using
ByteStreamMessage. #4058Path path = ...;// Fluently build a `StreamMessage` for a `Path`.ByteStreamMessage pathContent =StreamMessage.builder(path).bufferSize(1024).alloc(alloc).executor(executor).build();// Slice a range of a file dataByteStreamMessage partialContent = pathContent.range(100, 200); -
You can now map a specific exception to a
LogLevelinLoggingServiceandLoggingClient. #3400 #4090LoggingService.builder().responseLogLevel(IllegalStateException.class,LogLevel.WARN).responseLogLevel(IllegalArgumentException.class,LogLevel.ERROR)... -
You can now map a specific exception to an
HttpResponse. #4279 #4283HttpResponse recovered =response.recover(IllegalStateException.class,cause -> HttpResponse.of("Fallback")); -
You can now enable automatic compression for a gRPC response. #4258 #4266
GrpcService.builder().addService(new MyGrpcService()).autoCompression(true).build();- This only works when the client sends a
grpc-accept-encodingheader and theCompressorRegistryhas the compressor.
- This only works when the client sends a
-
You can now add a hook to
ServerandClientFactorythat is called when the JVM shuts down. #4015 #4043server.closeOnJvmShutdown(() -> {System.err.println("Server is stopping soon.");}).thenRun(() -> {System.err.println("Server has stopped.");}); -
You can now customize internal Netty
ChannelPipelineusingClientFactoryOptions.CHANNEL_PIPELINE_CUSTOMIZER. #3907 #4260- This is an advanced feature and not recommended to use if you are not familiar with Armeria and Netty internals.
Kotlin
- You can now use the Kotlin nullable type (
?) to indicate a nullable parameter in an annotated service. #4144 #4225serverBuilder.apply {annotatedService(object {// Both `/foo` and `/foo?a=bar` are allowed.@Get("/foo")fun foo(@Param a: String?) = // 👈👈👈HttpResponse.of("a: $a")})}
📈 Improvements
- The
EndpointSelectionTimeoutExceptionis thrown if timeout occurred when selecting anEndpointfrom anEndpointGroup. #4269 - You can now set multiple
AccessLogWriterstoServerBuilderandServiceBindingBuilder. #4280 - You can now give a performance optimization hint to a client by setting an
ExchangeTypeto aRequestOptions. TheExchangeTypeis configured automatically for:- A gRPC client
- A Thrift client
- A
WebClientwhen it's used withResponseAs. #4236
- Better performance for unary gRPC call. #4192
- Annotated services perform better on unary responses. #4177
🛠️ Bug fixes
- You can now run
ServerInterceptorsin a blocking task executor whenGrpcServiceBuilder.useBlockingTaskExecutor()is enabled. #4275 #4331 Multiparts.getBoundary()now throws anIllegalStateExceptioninstead ofNullPointerExceptionwhen a boundary is missing. #4193 #4325- Armeria retrofit streaming mode now requests data to upstream when it does not have one. #4319
- You no longer see deep recursive calls in
PublisherBasedStreamMessage. #4298 - You no longer see
IllegalArgumentExceptionindicating the prohibited character of a header name in an environment where Spring integration module and Netty client are used together. (e.g. Spring Cloud Gateway) #4293 ServerCacheControl.DISABLEDandServerCacheControl.REVALIDATEDnow havemax-ageset to0for better compatibility with old browsers. #4290 #4291- You no longer see a
NullPointerExceptionfromFlagswhenRequestContextExportingAppenderis used. #4285 - You no longer see the
InputStreamreturned byStreamMessage.toInputStream()blocks indefinitely in a certain situation. #4268 #4271 - A
WriteTimeoutExceptionis now thrown when a header only request times out. #4255 #4259 - The request now completes even when the content sanitizer throws an exception. #4248
- A proxy client doesn't resolve a hostname anymore. #4244 #4245
- The Spring Boot integration module doesn't create a new
WebClientanymore every time it's injected. #4240 - Armeria server sends now 400 Bad Request when an HTTP/2 upgrade request contains invalid headers. #4016 #4224
EurekaUpdatingListenernow registers itself with theinstanceIdwhich ishostname:appname:port, so it doesn't cause a conflict when more than one app with the same name are running in the same host. #4223- A ramping up
EndpointSelectionStrategynow works correctly in combination withHealthCheckedEndpointGroup. #4221 - The information of a thrown exception in gRPC service is now propagated to the client via the
grpc-status-details-binheader. #4203 #4204
🏚️ Deprecations
AbstractEndpointSelector.select(ClientRequestContext,ScheduledExecutorService,long)is deprecated in favor ofAbstractEndpointSelector.select()?full. #4246ThriftSerializationFormats.protocolFactory(SerializationFormat)is deprecated in favor ofThriftSerializationFormats.protocolFactory(SerializationFormat,int,int). #4226BINARY,COMPACT,JSON,TEXT, andTEXT_NAMED_ENUMinThriftProtocolFactoriesare deprecated in favor of corresponding factory methods. #4226StreamMessage.of(Path,int),StreamMessage.of(Path,ByteBufAllocator,int)andStreamMessage.of(Path,ExecutorService,ByteBufAllocator,int)are deprecated in favor ofStreamMessage.builder(Path). #4058ServiceConfig.shutdownBlockingTaskExecutorOnStop(),ServiceConfig.shutdownAccessLogWriterOnStop(),ServerConfig.shutdownBlockingTaskExecutorOnStop()andServerConfig.shutdownWorkerGroupOnStop()are not used anymore. #4280
☢️ Breaking changes
ByteStreamMessageis returned instead ofStreamMessage<HttpData>. #4058HttpService.exchangeType(RequestHeaders, Route)is nowHttpService.exchangeType(). #4177
⛓ Dependencies
- Brave 5.13.8 → 5.13.9
- Bucket4J 7.4.0 → 7.5.0
- Dropwizard metrics 4.2.9 → 4.2.10
- GraphQL 1.8.0 → 1.8.2
- gRPC-Java 1.45.1 → 1.47.0
- gRPC-Kotlin 1.2.1 → 1.3.0
- Jackson 2.13.2.1 → 2.13.3
- Kotlin 1.6.20 → 1.7.0
- Kotlinx 1.6.1 → 1.6.3
- Micrometer 1.8.5 → 1.9.1
- Netty 4.1.76.Final → 4.1.78.Final
- Netty incubator 0.0.13.Final → 0.0.14.Final
- Project Reactor 3.4.17 → 3.4.19
- Prometheus Simpleclient 0.15.0 → 0.16.0
- Reactive Streams 1.0.3 → 1.0.4
- RxJava 3.1.4 → 3.1.5
- ScalaPB 0.11.10 → 0.11.11
- Spring 5.3.19 → 5.3.21
- Spring Boot 2.6.6 → 2.7.1
🙇 Thank you
This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:




















