0.96.0 release notes

23rd November 2019

🌟 New features

  • The type signature of clients and services has been greatly simplified. Note that this involves various breaking changes unfortunately. See the 'Breaking changes' section for the details. #2239 #2254

    // Before:
    public class FooHttpService implements Service<HttpRequest, HttpResponse> { ... }
    public class FooRpcService implements Service<RpcRequest, RpcResponse> { ... }
    public class FooHttpClient implements Client<HttpRequest, HttpResponse> { ... }
    public class FooRpcClient implements Client<RpcRequest, RpcResponse> { ... }
    
    Service<HttpRequest, HttpResponse> foo(Service<HttpRequest, HttpResponse> bar) { ... }
    
    Function<Service<HttpRequest, HttpResponse>,
             ? extends Service<HttpRequest, HttpResponse>> httpServiceDecorator;
    Function<Service<RpcRequest, RpcResponse>,
             ? extends Service<RpcRequest, RpcResponse>> rpcServiceDecorator;
    Function<Client<HttpRequest, HttpResponse>,
             ? extends Client<HttpRequest, HttpResponse>> httpClientDecorator;
    Function<Client<RpcRequest, RpcResponse>,
             ? extends Client<RpcRequest, RpcResponse>> rpcClientDecorator;
    
    // After:
    public class FooHttpService implements HttpService { ... }
    public class FooRpcService implements RpcService { ... }
    public class FooHttpClient implements HttpClient { ... }
    public class FooRpcClient implements RpcClient { ... }
    
    HttpService foo(HttpService bar) { ... }
    
    Function<? super HttpService, ? extends HttpService> httpServiceDecorator;
    Function<? super RpcService, ? extends RpcService> rpcServiceDecorator;
    Function<? super HttpClient, ? extends HttpClient> httpClientDecorator;
    Function<? super RpcClient, ? extends RpcClient> rpcClientDecorator;
  • You can now specify service-level settings for annotated services, just like other service types. #2180 #2222

    Server server =
        Server.builder()
              .annotatedService().pathPrefix("/api")
                                 .requestTimeoutMillis(5000)
                                 .exceptionHandler((ctx, req, cause) -> ...)
                                 .build(new Object() { ... }))
              .build();
  • RequestContext now has its own RequestId, which is a 64-bit random integer by default. #2001 #2174 #2203 #2224

    Server server =
        Server.builder()
              .service("/", (ctx, req) -> {
                  return HttpResponse.of("Request ID: %s", ctx.id().text());
              })
              .build();
  • You can now add a cause to an HttpStatusException. #2253

    HttpService service = (ctx, req) -> {
        try (FileInputStream in = new FileInputStream(...)) {
            ...
        } catch (FileNotFoundException e) {
            throw HttpStatusException.of(HttpStatus.NOT_FOUND, e);
        }
    };
  • Armeria server now always has a fallback service which is matched when no routes match a request. #1625 #2255

    • As a result, a route decorator bound at prefix:/ can intercept any requests, even the requests not handled by any services:
      // CorsService will intercept any requests,
      // even the ones not handled by any services.
      Server server =
          Server.builder()
                .service("/foo", fooService)
                .service("/bar", barService)
                .routeDecorator().pathPrefix("/")
                                 .build(CorsService.newDecorator(...))
                .build();
  • You can now determine the log level of LoggingClient and LoggingService dynamically. #2250 #2258

    LoggingClient.builder()
                 .requestLogLevel(log -> ...)
                 .responseLogLevel(log -> {
                     if (log.responseCause() == null ||
                         log.responseCause() instanceof HarmlessException) {
                         return LogLevel.INFO;
                     } else {
                         return LogLevel.WARN;
                     }
                 });
  • ClientFactoryOptions has been added to allow programmatic access to the ClientFactory settings. #2230

    ClientFactory factory = ...;
    boolean pipelining = factory.options().useHttp1Pipelining();
  • You can now disable Armeria's Netty-based asynchronous DNS resolver by specifying -Dcom.linecorp.armeria.useJdkDnsResolver=true JVM option. Use it only when the default resolver does not work. #2261

💪 Improvements

  • Armeria client does not block a request anymore when DNS resolution takes long time. #2017 #2217 #2231
  • You do not have to prepend none+ prefix to non-RPC URIs anymore. #2219 #2241
    // Before:
    Clients.of("none+https://...", ...);
    // After:
    Clients.of("https://...", ...);
    • The behavior of Scheme.parse() and tryParse() has been improved in the same manner:
      // Scheme.parse() now uses SerializationFormat.NONE automatically:
      assert Scheme.parse("http") == Scheme.parse("none+http");

🛠️ Bug fixes

  • Armeria Spring auto-configuration now works correctly even when only GrpcServiceRegistrationBeans are given. #2234
  • RequestContext is not pushed more often than necessary anymore when notifying RequestLogListeners. #2227
  • DynamicEndpointGroup now handles the case where an endpoint address does not change but only a weight. #2240
  • HttpStatusException now respects Flags.verboseExceptionSampler(). #2253
  • AccessLogWriter does not fail with a ClassCastException anymore. #2259
  • Fixed a bug where a service receives an OPTIONS request even if the service did not opt-in for OPTIONS method. #2263

🏚️ Deprecations

  • ClientFactory.DEFAULT has been deprecated in favor of ClientFactory.ofDefault().
  • The getters for host-level settings in ServerConfig have been deprecated. #2244 #2246
    // Before:
    HttpService service = (ctx, req) -> {
        return HttpResponse.of("maxRequestLength: %d",
                               ctx.server().maxRequestLength());
    };
    // After:
    HttpService service = (ctx, req) -> {
        return HttpResponse.of("maxRequestLength: %d",
                               ctx.virtualHost().maxRequestLength());
    };
  • Continuing the migration to static builder() factory methods, the following classes have switched from constructors to static builder() methods: #2221
    • ClientCacheControl
    • ClientConnectionTimings
    • ClientDecoration
    • ClientFactory
    • ClientOptions
    • ClientRequestContext
    • EndpointInfo
    • FieldInfo
    • HttpClient
    • LoggingClient
    • LoggingService
    • RequestContextCurrentTraceContext
    • RetrofitMeterIdPrefixFunction
    • ServerCacheControl
    • ServiceRequestContext

☢️ Breaking changes

  • The signatures of RequestContext.newDerivedContext() have been changed so they always require RequestId, HttpRequest and RpcRequest for less ambiguity. #2209 #2224

  • Scheme.parse() and tryParse() do not fail anymore even if the scheme does not start with none+: #2219 #2241

    // Scheme.parse() now uses SerializationFormat.NONE automatically:
    assert Scheme.parse("http") == Scheme.parse("none+http");
  • ClientBuilderParams.uri() does not return a URI with none+ prefix anymore. #2219 #2241

  • HttpClient interface has been renamed to WebClient. HttpClient is now a different interface that extends Client<HttpRequest, HttpResponse>. #2254

    // Doesn't work:
    HttpClient client = HttpClient.of("https://www.google.com/");
    // Good:
    WebClient client = WebClient.of("https://www.google.com/");
  • Services must implement HttpService or RpcService and clients must implement HttpClient or RpcClient: #2239 #2254

    // Before:
    public class FooHttpService implements Service<HttpRequest, HttpResponse> { ... }
    public class FooRpcService implements Service<RpcRequest, RpcResponse> { ... }
    public class FooHttpClient implements Client<HttpRequest, HttpResponse> { ... }
    public class FooRpcClient implements Client<RpcRequest, RpcResponse> { ... }
    // After:
    public class FooHttpService implements HttpService { ... }
    public class FooRpcService implements RpcService { ... }
    public class FooHttpClient implements HttpClient { ... }
    public class FooRpcClient implements RpcClient { ... }
    • You'll have to change any Service<...> and Client<...> usages in your code into HttpService, RpcService, HttpClient or RpcClient, unless you intended to express both HTTP- and RPC- level types.

      // Before:
      Service<HttpRequest, HttpResponse> foo(Service<HttpRequest, HttpResponse> bar) { ... }
      // After:
      HttpService foo(HttpService bar) { ... }
    • Similarly, you must use SimpleDecoratingHttpClient or SimpleDecoratingRpcClient instead of SimpleDecoratingClient, and SimpleDecoratingHttpService or SimpleDecoratingRpcService instead of SimpleDecoratingService.

      // Does not work:
      class MyDecoratorService extends SimpleDecoratingService<HttpRequest, HttpResponse> {
          MyDecoratorService(Service<HttpRequest, HttpResponse> delegate) {
              super(delegate);
          }
          ...
      }
      // Good:
      class MyDecoratorService extends SimpleDecoratingHttpService {
          // Note the constructor parameter change.
          MyDecoratorService(HttpService delegate) {
              super(delegate);
          }
          ...
      }
    • If you implemented your decorator without extending the SimpleDecorating{Http,Rpc}Service or SimpleDecorating{Http,Rpc}Client, then you must make sure that it implements HttpService, RpcRequest, HttpClient or RpcClient.

      // Does not work:
      class MyDecoratorService implements Service<HttpRequest, HttpResponse> {
      
          final Service<HttpRequest, HttpResponse> delegate;
      
          MyDecoratorService(Service<HttpRequest, HttpResponse> delegate) {
              this.delegate = delegate;
          }
          ...
      }
      // Good:
      class MyDecoratorService implements HttpService {
          // Note the type change.
          final HttpService delegate;
          // Note the constructor parameter change.
          MyDecoratorService(HttpService delegate) {
              this.delegate = delegate;
          }
          ...
      }
  • Service.decorate() has been pushed down to HttpService and RpcService: #2239 #2254

    // Does not work:
    Service<HttpRequest, HttpResponse> service = (ctx, req) -> ...;
    service.decorate(myDecorator); // No such method
    // Good:
    HttpService service (ctx, req) -> ...;
    service.decorate(myDecorator); // OK!
  • The type parameters of decorator Functions have been changed. #2239 #2254

    // Before:
    Function<Service<HttpRequest, HttpResponse>,
             ? extends Service<HttpRequest, HttpResponse>> httpServiceDecorator;
    Function<Service<RpcRequest, RpcResponse>,
             ? extends Service<RpcRequest, RpcResponse>> rpcServiceDecorator;
    Function<Client<HttpRequest, HttpResponse>,
             ? extends Client<HttpRequest, HttpResponse>> httpClientDecorator;
    Function<Client<RpcRequest, RpcResponse>,
             ? extends Client<RpcRequest, RpcResponse>> rpcClientDecorator;
    // After:
    Function<? super HttpService, ? extends HttpService> httpServiceDecorator;
    Function<? super RpcService, ? extends RpcService> rpcServiceDecorator;
    Function<? super HttpClient, ? extends HttpClient> httpClientDecorator;
    Function<? super RpcClient, ? extends RpcClient> rpcClientDecorator;
  • Some types, which could be used at both HTTP- and RPC- level, have been split into two different types. For example:

    • DecoratingClientFunction has been split into DecoratingHttpClientFunction and DecoratingRpcClientFunction.
    • DecoratingServiceFunction has been split into DecoratingHttpServiceFunction and DecoratingRpcServiceFunction.
    • LoggingClient has been split into LoggingClient and LoggingRpcClient.
    • ServiceWithRoutes has been split into HttpServiceWithRoutes and RpcServiceWithRoutes.
    • TransientService has been split into TransientHttpService and TransientRpcService.
  • The following services are now provided only at HTTP-level. Please let us know if you need an RPC-level version of them.

    • LoggingService
    • MetricCollectingService
    • StructuredLoggingService
    • KafkaStructuredLoggingService

⛓ Dependencies

  • Brave 5.8.0 -> 5.9.0
  • gRPC 1.24.1 -> 1.25.0
  • Jackson 2.10.0 -> 2.10.1
  • Micrometer 1.3.0 -> 1.3.1
  • Netty TCNative BoringSSL 2.0.26 -> 2.0.27
  • Protobuf 3.9.1 -> 3.10.0
  • RxJava 2.2.13 -> 2.2.14
  • SLF4J 1.7.28 -> 1.7.29
  • Spring Boot 2.1.9 -> 2.1.10

🙇 Thank you