diff --git a/commercetools-java-client-core/src/main/java/io/sphere/sdk/client/AutoRefreshSphereAccessTokenSupplierImpl.java b/commercetools-java-client-core/src/main/java/io/sphere/sdk/client/AutoRefreshSphereAccessTokenSupplierImpl.java index eda3c89c70d..403078e8569 100644 --- a/commercetools-java-client-core/src/main/java/io/sphere/sdk/client/AutoRefreshSphereAccessTokenSupplierImpl.java +++ b/commercetools-java-client-core/src/main/java/io/sphere/sdk/client/AutoRefreshSphereAccessTokenSupplierImpl.java @@ -42,9 +42,14 @@ private List createRules() { final boolean unauthorized = latestError instanceof UnauthorizedException; return unknownHost || unauthorized; }; + final Predicate isSuspended = r -> { + final Throwable latestError = r.getLatestError(); + return latestError instanceof SuspendedProjectException; + }; final RetryRule fatalRetryRule = RetryRule.of(isFatal, RetryAction.ofShutdownServiceAndSendLatestException()); final RetryRule retryScheduledRetryRule = RetryRule.of(RetryPredicate.ofAlwaysTrue(), RetryAction.ofScheduledRetry(2, retryContext -> Duration.ofMillis(retryContext.getAttempt() * retryContext.getAttempt() * 50))); - return asList(fatalRetryRule, retryScheduledRetryRule); + final RetryRule suspendedRetryRule = RetryRule.of(isSuspended, RetryAction.ofExponentialBackoff(100, 1000, 60000)); + return asList(suspendedRetryRule, fatalRetryRule, retryScheduledRetryRule); } private CompletionStage supervisedTokenSupplier(final Supplier> supplier) { diff --git a/commercetools-java-client-core/src/main/java/io/sphere/sdk/client/TokensSupplierImpl.java b/commercetools-java-client-core/src/main/java/io/sphere/sdk/client/TokensSupplierImpl.java index 678296d29b5..d59992e21e8 100644 --- a/commercetools-java-client-core/src/main/java/io/sphere/sdk/client/TokensSupplierImpl.java +++ b/commercetools-java-client-core/src/main/java/io/sphere/sdk/client/TokensSupplierImpl.java @@ -129,7 +129,12 @@ private Tokens parseResponse(final HttpResponse httpResponse, final HttpRequest exception = new InvalidClientCredentialsException(config); } if (error.equals("invalid_scope")) { - exception = new InvalidScopeException(exception); + final String description = jsonNode.get("error_description").asText(); + if (description.endsWith("suspended")) { + exception = new SuspendedProjectException(exception); + } else { + exception = new InvalidScopeException(exception); + } } } catch (final JsonException e) { exception = new UnauthorizedException(httpResponse.toString(), e,httpResponse.getStatusCode()); diff --git a/commercetools-java-client-core/src/test/java/io/sphere/sdk/client/AutoRefreshSphereAccessTokenSupplierImplTest.java b/commercetools-java-client-core/src/test/java/io/sphere/sdk/client/AutoRefreshSphereAccessTokenSupplierImplTest.java index b89478e53bd..9dd908dd67c 100644 --- a/commercetools-java-client-core/src/test/java/io/sphere/sdk/client/AutoRefreshSphereAccessTokenSupplierImplTest.java +++ b/commercetools-java-client-core/src/test/java/io/sphere/sdk/client/AutoRefreshSphereAccessTokenSupplierImplTest.java @@ -5,6 +5,7 @@ import io.sphere.sdk.http.HttpResponse; import io.sphere.sdk.models.Base; import org.junit.Test; +import org.slf4j.LoggerFactory; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; @@ -31,6 +32,21 @@ public void selectNextRetryTime() { assertThat(AuthActor.selectNextRetryTime(-100L)).as("at least a second").isEqualTo(1L); } + @Test + public void testSuspendedClient() throws Exception { + final SuspendableDoubleHttpClient httpClient = getSuspendableHttpClient(); + + try(final SphereAccessTokenSupplier supplier = + AutoRefreshSphereAccessTokenSupplierImpl.createAndBeginRefreshInBackground(SphereAuthConfig.of("project-key", "client-id", "clientSecret"), httpClient, true)) { + Thread.sleep(2000); + httpClient.suspended = true; + Thread.sleep(120000); + LoggerFactory.getLogger(AutoRefreshSphereAccessTokenSupplierImplTest.class).debug("Project unsuspended"); + httpClient.suspended = false; + Thread.sleep(10000); + } + } + private TestDoubleHttpClient getHttpClient() { return new TestDoubleHttpClient() { @Override @@ -40,6 +56,31 @@ protected HttpResponse executeSync(final HttpRequest httpRequest, final int requ }; } + private SuspendableDoubleHttpClient getSuspendableHttpClient() { + return new SuspendableDoubleHttpClient() { + @Override + protected HttpResponse executeSync(final HttpRequest httpRequest, final int requestId) { + if (suspended) { + return HttpResponse.of(400, "{\n" + + " \"statusCode\" : 400,\n" + + " \"message\" : \"Project 'test-project-suspension-1' is suspended\",\n" + + " \"errors\" : [ {\n" + + " \"code\" : \"invalid_scope\",\n" + + " \"message\" : \"Project 'test-project-suspension-1' is suspended\"\n" + + " } ],\n" + + " \"error\" : \"invalid_scope\",\n" + + " \"error_description\" : \"Project 'test-project-suspension-1' is suspended\"\n" + + " }"); + } + return HttpResponse.of(200, "{\"access_token\": \"vkFuQ6oTwj8_Ye4eiRSsqMeqLYNeQRJi\", \"expires_in\": 1}"); + } + }; + } + + public static abstract class SuspendableDoubleHttpClient extends TestDoubleHttpClient { + public boolean suspended = false; + } + public static abstract class TestDoubleHttpClient extends Base implements HttpClient { private final AtomicInteger timesCalled = new AtomicInteger(0); diff --git a/commercetools-sdk-base/src/main/java/io/sphere/sdk/client/SuspendedProjectException.java b/commercetools-sdk-base/src/main/java/io/sphere/sdk/client/SuspendedProjectException.java new file mode 100644 index 00000000000..ed503d0d555 --- /dev/null +++ b/commercetools-sdk-base/src/main/java/io/sphere/sdk/client/SuspendedProjectException.java @@ -0,0 +1,13 @@ +package io.sphere.sdk.client; + +/** + * when trying to make a call to commercetools with suspended project + * + */ +public class SuspendedProjectException extends UnauthorizedException{ + private static final long serialVersionUID = 0L; + + public SuspendedProjectException(final Throwable cause) { + super("Project suspended",cause,400); + } +}