info
You're seeing the release note of an old version. Check out the latest release note.
v0.98.0
February 12, 2020
🎬 Before we begin
This release contains more breaking changes and deprecations than usual, which were necessary for the preparation of 1.0 release and the long term evolution of our API after 1.0. We'd like to apologize for any inconveniences caused by the breaking changes. Please don't forget we're always here for you and do let us know if you have any trouble upgrading, so we can help you!
🌟 New features
- Since this release, in preparation of 1.0 release, we annotated classes and packages which may have a chance of breaking change with the
@UnstableApiannotation. #2445 - The attribute access API of
RequestContexthas been revamped for simplicity. #2322AttributeKey<String> MY_ATTR = AttributeKey.valueOf("MY_ATTR");
RequestContext ctx = ...;
ctx.setAttr(MY_ATTR, "foo");
assert "foo".equals(ctx.attr(MY_ATTR)); - When a
RequestContextis derived or inherited from anotherRequestContext, the parent context's attributes are now visible from the derived or inherited context: #2322AttributeKey<String> MY_ATTR = AttributeKey.valueOf("MY_ATTR");
ServiceRequestContext parentCtx = ...;
ClientRequestContext childCtx = ...;
assert childCtx.root() == parentCtx;
parentCtx.setAttr(MY_ATTR, "foo");
// Root context's attributes are visible from its children.
assert "foo".equals(childCtx.attr(MY_ATTR));
// Not visible anymore if the attribute is overwritten.
childCtx.setAttr(MY_ATTR, "bar");
assert "foo".equals(parentCtx.attr(MY_ATTR));
assert "bar".equals(childCtx.attr(MY_ATTR)); {Client,Service}RequestContextnow provides more useful ways to schedule request or response timeout: #2343Server.builder()
.service("/svc", myService.decorate((delegate, ctx, req) -> {
if (req.headers().contains("x-extend-timeout")) {
// Extend the timeout by 10 seconds.
ctx.extendRequestTimeout(Duration.ofSeconds(10));
}
return delegate.serve(ctx, req);
}));RequestLogAPI has been revamped for safety and usability. #2342RequestContext ctx = ...;
// Asynchronous retrieval:
ctx.log().whenRequestComplete().thenAccept(log -> {
// Can't access response properties at compilation level.
assert log instanceof RequestOnlyLog;
System.err.println(log.toStringRequestOnly());
})
ctx.log().whenComplete().thenAccept(log -> {
// Can access all properties.
assert log instanceof RequestLog;
System.err.println(log.toStringResponseOnly());
});
ctx.log().whenAvailable(RequestLogProperty.SESSION).thenAccept(log -> {
// Advanced use case:
// Raises an exception if accessing an unavailable property.
System.err.println(log.channel());
});
// Accessing a property ensuring availability:
ctx.log().ensureRequestComplete().requestEndTimeNanos();
ctx.log().ensureComplete().responseEndTimeNanos();
ctx.log().ensureAvailable(RequestLogProperty.RESPONSE_CONTENT)
.responseContent();RequestLogalso has a new propertyNAME, which can be used as a method name in an RPC call, a span name in distributed tracing or any other human-readable string that can be used for identifying a request. #2413- Added a new immutable API for encoding and decoding HTTP query strings: #2307
QueryParams params = QueryParams.builder()
.add("foo", "1")
.add("bar", "2")
.build();
// Encoding
String queryString = params.toQueryString();
assert "foo=1&bar=2".equals(queryString);
// Decoding
QueryParams decodedParams = QueryParams.fromQueryString("foo=1&bar=2");
assert decodedParams.equals(params);
// Mutation
QueryParams newParams = params.toBuilder()
.add("baz", "3")
.build();
assert "foo=1&bar=2&baz=3".equals(newParams.toQueryString()); - Added various convenient boolean getter methods to
HttpStatus: #2435assert HttpStatus.CONTINUE.isInformational();
assert HttpStatus.OK.isSuccess();
assert HttpStatus.FOUND.isRedirect();
assert HttpStatus.BAD_REQUEST.isClientError();
assert HttpStatus.SERVICE_UNAVAILABLE.isServerError();
// No need to write like this anymore
assert HttpStatus.OK.codeClass() == HttpStatusClass.SUCCESS; - Added
MediaTypeNameswhich providesStringversion of well knownMediaTypes, which is useful when writing an annotated service: #2438class MyAnnotatedService {
@Get("/download/zip")
@Produces(MediaTypeNames.ZIP)
HttpResponse downloadArchive() { ... }
} - You can now add
{Request,Response}ConverterFunctionandExceptionHandlerFunctionto all annotated services in yourServereasily. #2316Server.builder()
.annotatedService("/users", userService)
.annotatedService("/posts", postService)
.annotatedService("/files", fileService)
// Applies all extensions to all 3 annotated services.
.annotatedServiceExtensions(
commonRequestConverters,
commonResponseConverters,
commonExceptionHandlers)
.build(); - You can now require a route to have HTTP headers and/or query parameters: #2102
Server.builder()
// Route to 'myService' only when:
// - 'x-must-exist' header exists,
// - and 'bar' query parameter exists.
.route().get("/foo")
.matchesHeaders("x-must-exist")
.matchesParams("bar")
.build(myService) - You can now customize the
SslContextcreated fromServerBuilder.tlsSelfSigned()orVirtualHost.tlsSelfSigned(): #2340Server.builder()
.tlsSelfSigned()
.tlsCustomizer(sslCtxBuilder -> {
sslCtxBuilder.ciphers(...);
}) - You can now close an
EndpointGroupasynchronously: #2430DnsAddressEndpointGroup group =
DnsAddressEndpointGroup.of("cluster.com", 8080);
group.whenClosed().thenRun(() -> {
System.err.println("Closed!");
});
group.closeAsync(); - You do not need to register your
EndpointGrouptoEndpointGroupRegistryfor client-side load balancing. Just specify it when you build a client: #2381EndpointGroup group = EndpointGroup.of(
Endpoint.of("node1.cluster.com"),
Endpoint.of("node2.cluster.com"));
// Thrift
HelloService.Iface client =
Clients.builder("tbinary+http", group)
.path("/api/thrift/hello")
.build(HelloService.Iface.class);
// gRPC
HelloServiceBlockingStub client =
Clients.builder("gproto+http", group)
.build(HelloServiceBlockingStub.class);
// Web
WebClient client =
WebClient.of(SessionProtocol.HTTP, group); - You can now limit the number of endpoints in
HealthCheckedEndpointGroup, which is very useful when there are many candidate endpoints in the group but you want to send requests to only a few of them, to avoid unnecessarily large number of outbound connections: #2177HealthCheckedEndpointGroup group =
HealthCheckedEndpointGroup.builder(delegateGroup, "/health")
.maxEndpointCount(3)
.build(); - You can now capture the
ClientRequestContextof your client call withClientRequestContextCaptor: #2344WebClient client = WebClient.of("http://foo.com/");
try (ClientRequestContextCaptor ctxCaptor = Clients.newContextCaptor()) {
HttpResponse res = client.get("/");
ClientRequestContext ctx = ctxCaptor.get();
...
} - Added
ClientFactory.insecure()andClientFactoryBuilder.tlsNoVerify()to simplify testing SSL/TLS connections with self-signed certificates: #2340// Using the default insecure factory
WebClient.builder("https://127.0.0.1:8443")
.factory(ClientFactory.insecure())
.build();
// Using a custom insecure factory
WebClient.builder("https://127.0.0.1:8443")
.factory(ClientFactory.builder()
.tlsNoVerify()
...
.build())
.build(); ClientFactoryis now part ofClientOptionsfor easier creation of derived clients. #2384ClientFactory factory = ...;
WebClient client =
WebClient.builder(...)
.factory(factory)
.build();
WebClient clientCopy =
WebClient.builder(...)
.options(client.options())
.build();
// Note that ClientFactory is copied as well.
assert client.factory() == clientCopy.factory();RetrofitMeterIdPrefixFunctionis now capable of adding HTTP method and request path pattern if you specify a Retrofit service class: #2356- New module
armeria-dropwizardprovides the integration with Dropwizard, which allows you to leverage the best of the both worlds. #2236- See Using Armeria with Dropwizard for more information.
- Special thanks to @cricket007 who volunteered for this.
- You can now customize
DocServicewhen integrating with Spring framework by injectingDocServiceConfigurator: #2327@Bean
public DocServiceConfigurator docServiceConfigurator() {
// Exclude all Thrift services from DocService.
return docServiceBuilder -> {
docServiceBuilder.exclude(DocServiceFilter.ofThrift());
};
} ServerRule(JUnit 4) andServerExtension(JUnit 5) now have more getters: #2449Endpointgetters:endpoint(SessionProtocol),httpEndpoint()andhttpsEndpoint()
URIgetters:uri(SessionProtocol),uri(SessionProtocol, SerializationFormat),httpUri(),httpUri(SerializationFormat),httpsUri()andhttpsUri(SerializationFormat)- The old deprecated getters return
Stringinstead ofURI.
InetSocketAddressgetters:socketAddress(SessionProtocol)
- The
CompletableFutures returned by our API will leave a warning log like the following when you perform a blocking operation in an event loop thread: #2275You can disable this functionality by specifying theCalling a blocking method on CompletableFuture from an event loop or
non-blocking thread. You should never do this ...
java.lang.IllegalStateException: Blocking event loop, don't do this.-Dcom.linecorp.armeria.reportBlockedEventLoop=falseJVM option. - You can now serialize and deserialize
ThriftCall,ThriftReply,TMessageandTBaseinto TTEXT JSON usingThriftJacksonModule. #2439ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new ThriftJacksonModule()); - You can now make any SLF4J
Loggercontext-aware withRequestContext.makeContextAware(Logger): #2341// Prints 'Hello!'
Logger logger = ...;
logger.info("Hello!");
// Prints '[<current context>] Hello!'
Logger ctxAwareLogger = ctx.makeContextAware(logger);
ctxAwareLogger("Hello!"); RequestContextExporteris now part of the core API, allowing you to integrate with other logging frameworks than Logback, such as Log4J2. #2314- You can now disable HTTP header validation the the
-Dcom.linecorp.armeria.validateHeaders=falseJVM option.
📈 Improvements
- Slightly reduced memory footprint of
Logging{Client,Service}#2341 UnknownHostExceptionraised by Armeria now explains what DNS query has failed. #2332WebClientnow accepts a URI that starts withnone+as well. #2361HealthCheckedEndpointGroupnow logs a helpful warning message when it receives a 4XX response. #2401- Our builder API Javadoc does not show mysterious return type parameter such as
BandSELFanymore. #2454 - A client now gets
SSLExceptioninstead ofClosedSessionExceptionif a connection attempt fails with an SSL/TLS handshake error. #2338
🛠️ Bug fixes
WebClientdoes not omit a request query string when sending a request to an absolute URI. #2309- A user cannot start a
Serverwith a misconfiguredSslContextanymore. #2325 - A user now always gets the correct
RequestContexteven if the contexts are nested. #1083 - Fixed a bug where thread-local context customizers were called for derived contexts unintentionally. #2344
Clients.withHttpHeaders()andwithContextCustomizer()now work with gRPC calls. #2344ClientRequestContext.path()now returns a correct path for gRPC client calls. #2344- You can now send a POST request with an empty body with
DocServiceclient. #2357 - Server-side route cache hit ratio was not as good as we intended. #2358
- Fixed various potential memory leak in
HttpResponseWriter. #2359 - Long-polling health check mechanism now detects a stall server which accepts a connection but does not send any response. #2392
ClientFactoryOptionsdoes not raise aNullPointerExceptionanymore. #2387- An
AsyncMethodCallbackspecified when making an async Thrift call now has thread-localClientRequestContextset properly. #2383 - gRPC client and server now works well with non-official gRPC stub generators such as reactive-grpc. #2376
- Fixed a bug where a
Servercan be started back afterclose()is called. #2406 - Fixed a regression where Reactor does not treat Armeria's event loop threads as non-blocking. #2404
- Armeria does not fail to initialize anymore even if it failed to load the
com.linecorp.armeria.versions.propertiesfile. #2398 - You'll not see the
cannot start a new stream with a DATA frameerrors anymore. #2429 RequestLog.requestStartTimeproperty now includes the time taken for making a connection attempt and the time taken by decorators. #2436- The
-Dcom.linecorp.armeria.dumpOpenSslInfo=trueJVM option does not trigger aStackOverflowErroranymore. #2418 - Fixed cosmetic issues in
DocServiceclient sidebar. #2470 - Made sure IPv6 DNS queries are not sent to some IPv4 only machines with a link-local IPv6 interface. #2464
🏚️ Deprecations
HttpParametershas been deprecated in favor ofQueryParams#2307ServerBuilder.tls()methods that require aConsumerhave been deprecated. UsetlsCustomizer(Consumer)instead. #2340// Before:
Server.builder()
.tls(..., sslCtxBuilder -> { ... });
// After:
Server.builder()
.tls(...)
.tlsCustomizer(sslCtxBuilder -> { ... });- Many classes which have
Httpin their names have been deprecated in favor of those withoutHttp, e.g. #2323RetryingHttpClient->RetryingClientHttpFileService->FileService
- Many builder classes' constructors have been deprecated in favor of
builder()static methods, e.g. #1719new ClientBuilder()->Clients.builder()new ArmeriaRetrofitBuilder()->ArmeriaRetrofit.builder()new ServerCacheControlBuilder()->ServerCacheControl.builder()
- Many public static final fields that are not truly constants have been deprecated in favor of static factory methods, e.g.
EndpointSelectionStrategy.ROUND_ROBIN->roundRogin()NodeValueCodec.DEFAULT->ofDefault()AuthTokenExtractors.BASIC->basic()
MoreNamingConventionshas been deprecated because we follow Micrometer's recommended naming convention. #2367Version.identify()has been deprecated in favor ofgetAll()#2398ServiceRequestContext.setRequestTimeout*()has been deprecated in favor ofextendRequestTimeout*(),setRequestTimeoutAfter*(),setRequestTimeoutAt*()andclearRequestTimeout(). #2343ClientRequestContext.setResponseTimeout*()has been deprecated in favor ofextendResponseTimeout*(),setResponseTimeoutAfter*(),setResponseTimeoutAt*()andclearResponseTimeout(). #2343- Many methods that return a
CompletableFuturehave been renamed from*Future()towhen*(), e.g. #2427HttpRequest.completionFuture()->whenComplete()HttpResponseWriter.onDemand()->whenConsumed()EndpointGroup.initialEndpointsFuture()->whenReady()
- Many URI-returning methods in
ServerRuleandServerExtensionhave been deprecated in favor of the new methods that do not require a path parameter: #2449ServerExtension server = ...;
// Before
server.httpUri("/");
server.httpUri("/foo");
// After
server.httpUri();
server.httpUri().resolve("/foo"); THttpService.allowedSerializationFormats()has been deprecated in favor ofsupportedSerializationFormats()for consistency withGrpcService. #2453Service.decorate(Class)has been deprecated in favor of otherdecorate()methods that require a function.ClosedPublisherExceptionhas been deprecated in favor ofClosedStreamException. #2468
☢️ Breaking changes
- Most revamped APIs in this release were changed in a backward-incompatible way:
RequestLog- Attribute API in
RequestContext
- Content previewing mechanism has been revamped into decorators. Use
ContentPreviewingClientandContentPreviewingService. - You cannot add callbacks to
RequestContextanymore because we found this feature results in poor performance and confusing behavior in many cases. We may want to revisit this feature if there is a valid use case for it. {Server,VirtualHost}Builder.tls()methods do not throw anSSLExceptionanymore.build()will throw anIllegalStateExceptioninstead. As a result, any SSL configuration failure will be known when a user callsbuild(), rather thantls().- We were not able to keep some classes or method signatures while we remove
Httpfrom class names. ServiceRequestContext.logger()has been removed due to performance issues with Log4J2.RequestContext.isTimedOut()has been removed.- We do not support Tomcat 8.0 anymore, which was obsoleted by Tomcat 8.5 anyway.
- The classes in
armeria-grpc-protocolhave been reorganized into multiple packages. - Our Micrometer meter IDs have been changed, which means you might need to update your monitoring configuration. If you wish to switch back to the legacy naming style, specify the
-Dcom.linecorp.armeria.useLegacyMeterNamed=trueJVM option. However, please keep in mind this option will eventually go away, because the new naming convention is recommended by Micrometer. - All our methods do not return
Optionalanymore. They are all@Nullablenow. If you wish to continue usingOptional, just wrap the return value withOptional.ofNullable(). EndpointGroupRegistryhas been removed, because you can now just specify anEndpointGroupdirectly when creating a client.- As a result, you need to specify an
EndpointSelectionStrategywhen building anEndpointGroup. If unspecified,EndpointSelectionStrategy.weightedRoundRobin()is used.
- As a result, you need to specify an
MeterIdPrefixFunctionis not a functional interface anymore. You must implement two methods explicitly:activeRequestPrefix()andcompleteRequestPrefix().- Now that
ClientFactoryis a part ofClientOption, the following code will not work as expected, becauseoptions(options)will overwrite the factory.To fix this, you must callClientOptions options = ClientOptions.builder().maxResponseLength(...).build();
ClientFactory factory = ClientFactory.builder().useHttp2Preface(false).build();
WebClient client = WebClient.builder(...)
.factory(factory)
.options(options)
.build();
// This will fail!
assert client.options().factory() == factory;options()first, and then override the individual properties:WebClient client = WebClient.builder(...)
.options(options)
.factory(factory)
.build(); StreamMessage.subscribe(..., boolean withPooledObjects)has been removed. Usesubscribe(..., SubscriptionOption.WITH_POOLED_OBJECTS).StreamMessage.drainAll(..., boolean withPooledObjects)has been removed. UsedrainAll(..., SubscriptionOption.WITH_POOLED_OBJECTS).HttpRequestDuplicatorandHttpResponseDuplicatorare now interfaces. UseHttpRequest.toDuplicator()andHttpResponse.toDuplicator()to create a duplicator.StructuredLogandStructuredLoggingServicehave been removed. UseAccessLogWriter.ThriftStructuredLogJsonFormathas been removed. RegisterThriftJacksonModuletoObjectMapperto serialize or deserialize Thrift objects.
⛓ Dependencies
- Brave 5.9.1 -> 5.9.3
- Dropwizard 1.3.17 -> 1.3.18
- Dropwizard Metrics 4.1.1 -> 4.1.2
- gRPC 1.25 -> 1.27
- Jackson 2.10.1 -> 2.10.2.20200130
- java-jwt 3.8.3 -> 3.9.0
- Jetty 9.4.24 -> 9.4.26
- Micrometer 1.3.2 -> 1.3.3
- Netty 4.1.43 -> 4.1.45
- TCNative BoringSSL 2.0.26 -> 2.0.28
- Prometheus Java client 0.8.0 -> 0.8.1
- Reactor 3.3.1 -> 3.3.2
- Retrofit 2.6.2 -> 2.7.1
- RxJava 2.2.15 -> 2.2.17
- SLF4J 1.7.29 -> 1.7.30
- Spring Boot 2.2.1 -> 2.2.4, 2.1.10 -> 2.1.12
- Thrift 0.12.0 -> 0.13.0
- Tomcat 9.0.29 -> 9.0.30, 8.5.49 -> 8.5.50
🙇 Thank you
This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:











































