All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pro.fessional.wings.slardar.httprest.okhttp.OkHttpTokenClient Maven / Gradle / Ivy

package pro.fessional.wings.slardar.httprest.okhttp;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.jetbrains.annotations.NotNull;

/**
 * Clients that auto send Header Token-based authentication, such as Oauth2.
 * Implements Call.Factory, because the Interceptor and Authenticator are not lightweight enough.
 * See okhttp-how-to-refresh-access-token-efficiently
 *
 * @author trydofor
 * @since 2022-11-25
 */
public class OkHttpTokenClient implements Call.Factory, OkHttpBuildableClient {

    private final Tokenize tokenize;
    private final Call.Factory tkClient;

    public OkHttpTokenClient(@NotNull final OkHttpClient client, @NotNull final Tokenize tokenize) {
        this.tokenize = tokenize;
        this.tkClient = (Call.Factory) client
            .newBuilder()
            .authenticator((ignored, response) -> {
                if (syncInitToken(tokenize, (Call.Factory) client)) {
                    final Request.Builder bd = response.request().newBuilder();
                    if (tokenize.fillToken(bd)) {
                        return bd.build();
                    }
                }
                return null;
            })
            .build();
    }

    @NotNull
    @Override
    public Call newCall(@NotNull Request request) {
        if (tokenize != null && tokenize.needToken(request)) {
            return newCall(request.newBuilder());
        }

        return tkClient.newCall(request);
    }

    @NotNull
    @Override
    public Call newCall(@NotNull Request.Builder builder) {
        if (!tokenize.fillToken(builder)) {
            if (syncInitToken(tokenize, tkClient)) {
                if (!tokenize.fillToken(builder)) {
                    throw new IllegalStateException("failed to fill token");
                }
            }
            else {
                throw new IllegalStateException("failed to init token");
            }
        }
        //
        return tkClient.newCall(builder.build());
    }

    @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
    public static boolean syncInitToken(@NotNull final Tokenize tokenize, @NotNull Call.Factory callFactory) {
        final Request.Builder builder = new Request.Builder();
        synchronized (tokenize) {
            // re check
            if (tokenize.fillToken(builder)) {
                return true;
            }
            else {
                return tokenize.initToken(callFactory);
            }
        }
    }

    public interface Tokenize {
        /**
         * Whether to need token
         */
        boolean needToken(@NotNull Request request);

        /**
         * Set a valid token, usually in the header.
         * `false` means the token is not valid, should try init
         */
        boolean fillToken(Request.Builder builder);

        /**
         * try init token
         */
        boolean initToken(@NotNull Call.Factory callFactory);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy