Skip to main content
info

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]()
  • You can now configure a timeout for an Endpoint selection of an DynamicEndpointGroup. #4246

    DnsAddressEndpointGroup 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) { ... }

    @Override
    public HttpResponse serve(HttpService delegate,
    ServiceRequestContext ctx,
    HttpRequest req)
    throws Exception {
    // Authorize the request.
    ...
    }
    }
    • Set armeria.enable-auto-injection to true to apply SpringDependencyInjector automatically when using Spring Boot integration.
      // in application.yml
      armeria:
      ports:
      ...
      enable-auto-injection: true // 👈👈👈
  • You can now customize the length of string fields or container fields of Thrift messages using THttpServiceBuilder or ThriftClientBuilder. #4024 #4226

    THttpService.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 Endpoint using Attributes and AttributesBuilder. #4241

    AttributeKey<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 MultipartFile to get the actual filename of an uploaded file through multipart/form-data. #4262

    @Consumes(MediaTypeNames.MULTIPART_FORM_DATA)
    @Post("/upload")
    public HttpResponse upload(MultipartFile multipartFile) {
    // The name parameter of the "content-disposition" header
    String name = multipartFile.name();
    // The filename parameter of the "content-disposition" header
    String filename = multipartFile.filename();
    // The file that stores the multipart content.
    File file = multipartFile.file();
    ...
    }
  • You can now read a range of bytes using ByteStreamMessage. #4058

    Path 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 data
    ByteStreamMessage partialContent = pathContent.range(100, 200);
  • You can now map a specific exception to a LogLevel in LoggingService and LoggingClient. #3400 #4090

    LoggingService.builder()
    .responseLogLevel(IllegalStateException.class,
    LogLevel.WARN)
    .responseLogLevel(IllegalArgumentException.class,
    LogLevel.ERROR)
    ...
  • You can now map a specific exception to an HttpResponse. #4279 #4283

    HttpResponse 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-encoding header and the CompressorRegistry has the compressor.
  • You can now add a hook to Server and ClientFactory that is called when the JVM shuts down. #4015 #4043

    server.closeOnJvmShutdown(() -> {
    System.err.println("Server is stopping soon.");
    }).thenRun(() -> {
    System.err.println("Server has stopped.");
    });
  • You can now customize internal Netty ChannelPipeline using ClientFactoryOptions.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 #4225
    serverBuilder.apply {
    annotatedService(object {
    // Both `/foo` and `/foo?a=bar` are allowed.
    @Get("/foo")
    fun foo(@Param a: String?) = // 👈👈👈
    HttpResponse.of("a: $a")
    })
    }

📈 Improvements

🛠️ Bug fixes

🏚️ Deprecations

☢️ Breaking changes

⛓ 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:

@natsumehu@trustin@litols@kojilin@jrhee17@Jimexist@ahnyujin@kezhenxu94@eisig@ta7uw@ks-yim@tobias-@minwoox@ngyukman@ikhoon@injae-kim@timothy-xflowpay@ghkim3221@be-hase@mauhiz@devdynam0507

Like Armeria?
Star us ⭐️

×