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

dev.failsafe.okhttp.FailsafeCall Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */
package dev.failsafe.okhttp;

import dev.failsafe.*;
import dev.failsafe.internal.util.Assert;
import okhttp3.Callback;
import okhttp3.Response;

import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * A Failsafe wrapped OkHttp {@link Call}. Supports synchronous and asynchronous executions, and cancellation.
 *
 * @author Jonathan Halterman
 */
public final class FailsafeCall {
  private final FailsafeExecutor failsafe;
  private final okhttp3.Call initialCall;

  private volatile Call failsafeCall;
  private volatile CompletableFuture failsafeFuture;
  private AtomicBoolean cancelled = new AtomicBoolean();
  private AtomicBoolean executed = new AtomicBoolean();

  private FailsafeCall(FailsafeExecutor failsafe, okhttp3.Call call) {
    this.failsafe = failsafe;
    this.initialCall = call;
  }

  public static final class FailsafeCallBuilder {
    private FailsafeExecutor failsafe;

    private FailsafeCallBuilder(FailsafeExecutor failsafe) {
      this.failsafe = failsafe;
    }

    public 

> FailsafeCallBuilder compose(P innerPolicy) { failsafe = failsafe.compose(innerPolicy); return this; } public FailsafeCall compose(okhttp3.Call call) { return new FailsafeCall(failsafe, call); } } /** * Returns a FailsafeCallBuilder for the {@code outerPolicy} and {@code policies}. See {@link Failsafe#with(Policy, * Policy[])} for docs on how policy composition works. * * @param

policy type * @throws NullPointerException if {@code call} or {@code outerPolicy} are null */ @SafeVarargs public static

> FailsafeCallBuilder with(P outerPolicy, P... policies) { return new FailsafeCallBuilder(Failsafe.with(outerPolicy, policies)); } /** * Returns a FailsafeCallBuilder for the {@code failsafeExecutor}. * * @throws NullPointerException if {@code failsafeExecutor} is null */ public static FailsafeCallBuilder with(FailsafeExecutor failsafeExecutor) { return new FailsafeCallBuilder(Assert.notNull(failsafeExecutor, "failsafeExecutor")); } /** * Cancels the call. */ public void cancel() { if (!cancelled.compareAndSet(false, true)) return; if (failsafeCall != null) failsafeCall.cancel(false); if (failsafeFuture != null) failsafeFuture.cancel(false); } /** * Returns a clone of the FailsafeCall. */ public FailsafeCall clone() { return new FailsafeCall(failsafe, initialCall.clone()); } /** * Executes the call until a successful response is returned or the configured policies are exceeded. To avoid leaking * resources callers should {@link Response#close() close} the Response which in turn will close the underlying * ResponseBody. * * @throws IllegalStateException if the call has already been executed * @throws IOException if the request could not be executed due to cancellation, a connectivity problem, or timeout * @throws FailsafeException if the execution fails with a checked Exception. {@link FailsafeException#getCause()} can * be used to learn the underlying checked exception. */ public Response execute() throws IOException { Assert.isTrue(executed.compareAndSet(false, true), "already executed"); failsafeCall = failsafe.newCall(ctx -> { return prepareCall(ctx).execute(); }); try { return failsafeCall.execute(); } catch (FailsafeException e) { if (e.getCause() instanceof IOException) throw (IOException) e.getCause(); throw e; } } /** * Executes the call asynchronously until a successful result is returned or the configured policies are exceeded. To * avoid leaking resources callers should {@link Response#close() close} the Response which in turn will close the * underlying ResponseBody. */ public CompletableFuture executeAsync() { if (!executed.compareAndSet(false, true)) { CompletableFuture result = new CompletableFuture<>(); result.completeExceptionally(new IllegalStateException("already executed")); return result; } failsafeFuture = failsafe.getAsyncExecution(exec -> { prepareCall(exec).enqueue(new Callback() { @Override public void onResponse(okhttp3.Call call, Response response) { exec.recordResult(response); } @Override public void onFailure(okhttp3.Call call, IOException e) { exec.recordException(e); } }); }); return failsafeFuture; } /** * Returns whether the call has been cancelled. */ public boolean isCancelled() { return cancelled.get(); } /** * Returns whether the call has been executed. */ public boolean isExecuted() { return executed.get(); } private okhttp3.Call prepareCall(ExecutionContext ctx) { okhttp3.Call call; if (ctx.isFirstAttempt()) { call = initialCall; } else { Response response = ctx.getLastResult(); if (response != null) response.close(); call = initialCall.clone(); } // Propagate cancellation to the call ctx.onCancel(() -> { cancelled.set(true); call.cancel(); }); return call; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy