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

pl.allegro.tech.hermes.consumers.consumer.sender.SingleMessageSendingResult Maven / Gradle / Ivy

There is a newer version: 2.8.0
Show newest version
package pl.allegro.tech.hermes.consumers.consumer.sender;

import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import jakarta.ws.rs.core.Response;
import org.eclipse.jetty.client.Result;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import pl.allegro.tech.hermes.common.exception.InternalProcessingException;

import java.net.URI;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Predicate;

import static io.netty.handler.codec.http.HttpResponseStatus.SERVICE_UNAVAILABLE;
import static io.netty.handler.codec.http.HttpResponseStatus.TOO_MANY_REQUESTS;
import static io.netty.handler.codec.http.HttpResponseStatus.UNAUTHORIZED;
import static jakarta.ws.rs.core.Response.Status.Family.CLIENT_ERROR;
import static jakarta.ws.rs.core.Response.Status.Family.SUCCESSFUL;
import static jakarta.ws.rs.core.Response.Status.Family.familyOf;

public class SingleMessageSendingResult implements MessageSendingResult {

    private Throwable failure;
    private boolean loggable;
    private boolean ignoreInRateCalculation = false;

    private Optional retryAfterMillis = Optional.empty();
    private Optional requestUri = Optional.empty();
    private int statusCode;
    private Response.Status.Family responseFamily;

    SingleMessageSendingResult(Throwable failure, boolean ignoreInRateCalculation) {
        this.failure = failure;
        this.loggable = !isTimeout();
        this.ignoreInRateCalculation = ignoreInRateCalculation;
    }

    SingleMessageSendingResult(Throwable failure) {
        this(failure, false);
    }

    SingleMessageSendingResult(Result result) {
        this.failure = result.getFailure();

        if (result.getResponse() != null) {
            initializeForStatusCode(result.getResponse().getStatus());
            if (shouldRetryOnStatusCode()) {
                initializeRetryAfterMillis(result);
            }
        }

        this.loggable = !isTimeout() && !hasHttpAnswer();
        this.requestUri = Optional.ofNullable(result.getRequest().getURI());
    }

    SingleMessageSendingResult(int statusCode) {
        initializeForStatusCode(statusCode);
    }

    SingleMessageSendingResult(int statusCode, URI requestURI) {
        this(statusCode);
        this.requestUri = Optional.of(requestURI);
    }

    SingleMessageSendingResult(int statusCode, long retryAfterMillis) {
        initializeForStatusCode(statusCode);
        if (shouldRetryOnStatusCode() && retryAfterMillis >= 0) {
            this.retryAfterMillis = Optional.of(retryAfterMillis);
        }
    }

    public SingleMessageSendingResult(Result result, URI uri) {
        this(result);
        this.requestUri = Optional.of(uri);
    }

    private void initializeForStatusCode(int statusCode) {
        this.statusCode = statusCode;
        responseFamily = familyOf(statusCode);
        if (this.failure == null && !isInFamily(SUCCESSFUL)) {
            this.failure = new InternalProcessingException("Message sending failed with status code: " + statusCode);
        }
    }

    private void initializeRetryAfterMillis(Result result) {
        HttpFields headers = result.getResponse().getHeaders();
        if (headers.contains(HttpHeader.RETRY_AFTER)) {
            try {
                int seconds = Integer.parseInt(headers.get(HttpHeader.RETRY_AFTER));
                if (seconds >= 0) {
                    retryAfterMillis = Optional.of(TimeUnit.SECONDS.toMillis(seconds));
                }
            } catch (NumberFormatException e) {
                // retryAfterMillis stays empty
            }
        }
    }

    private boolean isInFamily(Response.Status.Family family) {
        Preconditions.checkNotNull(family);
        return family.equals(responseFamily);
    }

    @Override
    public boolean isRetryLater() {
        return shouldRetryOnStatusCode() && retryAfterMillis.isPresent();
    }

    private boolean shouldRetryOnStatusCode() {
        return getStatusCode() == SERVICE_UNAVAILABLE.code()
                || getStatusCode() == TOO_MANY_REQUESTS.code();
    }

    @Override
    public boolean succeeded() {
        return getFailure() == null;
    }

    Throwable getFailure() {
        return failure;
    }

    @Override
    public String getRootCause() {
        return failure != null ? Throwables.getRootCause(failure).getMessage() : CAUSE_UNKNOWN;
    }

    @Override
    public int getStatusCode() {
        return statusCode;
    }

    @Override
    public boolean isLoggable() {
        return loggable;
    }

    @Override
    public Optional getRetryAfterMillis() {
        return retryAfterMillis;
    }

    public Optional getRequestUri() {
        return requestUri;
    }

    @Override
    public boolean isClientError() {
        return isInFamily(CLIENT_ERROR);
    }

    @Override
    public boolean ignoreInRateCalculation(boolean retryClientErrors, boolean isOAuthSecuredSubscription) {
        return isRetryLater()
                || this.ignoreInRateCalculation
                || (isClientError() && !retryClientErrors && !(isOAuthSecuredSubscription && isUnauthorized()));
    }

    private boolean isUnauthorized() {
        return getStatusCode() == UNAUTHORIZED.code();
    }

    @Override
    public boolean isTimeout() {
        return failure instanceof TimeoutException;
    }

    @Override
    public List getLogInfo() {
        return Collections.singletonList(new MessageSendingResultLogInfo(getRequestUri(), failure, getRootCause()));
    }

    @Override
    public List getSucceededUris(Predicate filter) {
        if (filter.test(this) && requestUri.isPresent()) {
            return Collections.singletonList(getRequestUri().get());
        } else {
            return Collections.emptyList();
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy