pl.allegro.tech.hermes.consumers.consumer.sender.SingleMessageSendingResult Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hermes-consumers Show documentation
Show all versions of hermes-consumers Show documentation
Fast and reliable message broker built on top of Kafka.
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