Decorating a client

A ‘decorating client’ (or a ‘decorator’) is a client that wraps another client to intercept an outgoing request or an incoming response. As its name says, it is an implementation of the decorator pattern. Client decoration takes a crucial role in Armeria. A lot of core features such as logging, metrics and distributed tracing are implemented as decorators and you will also find it useful when separating concerns.

There are basically two ways to write a decorating client:

Implementing DecoratingHttpClientFunction and DecoratingRpcClientFunction

DecoratingHttpClientFunction and DecoratingRpcClientFunction are functional interfaces that greatly simplify the implementation of a decorating client. They enable you to write a decorating client with a single lambda expression:

import com.linecorp.armeria.common.HttpRequest;
import com.linecorp.armeria.common.HttpResponse;

ClientBuilder cb = Clients.builder(...);
...
cb.decorator((delegate, ctx, req) -> {
    auditRequest(req);
    return delegate.execute(ctx, req);
});

MyService.Iface client = cb.build(MyService.Iface.class);

Extending SimpleDecoratingHttpClient and SimpleDecoratingRpcClient

If your decorator is expected to be reusable, it is recommended to define a new top-level class that extends SimpleDecoratingHttpClient or SimpleDecoratingRpcClient depending on whether you are decorating an HttpClient or an RpcClient:

import com.linecorp.armeria.client.HttpClient;
import com.linecorp.armeria.client.SimpleDecoratingHttpClient;

public class AuditClient extends SimpleDecoratingHttpClient {
    public AuditClient(HttpClient delegate) {
        super(delegate);
    }

    @Override
    public HttpResponse execute(ClientRequestContext ctx, HttpRequest req) throws Exception {
        auditRequest(req);
        return delegate().execute(ctx, req);
    }
}

ClientBuilder cb = Clients.builder(...);
...
// Using a lambda expression:
cb.decorator(delegate -> new AuditClient(delegate));