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

com.oracle.bedrock.deferred.Repetitively Maven / Gradle / Ivy

/*
 * File: Repetitively.java
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * The contents of this file are subject to the terms and conditions of
 * the Common Development and Distribution License 1.0 (the "License").
 *
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the License by consulting the LICENSE.txt file
 * distributed with this file, or by consulting https://oss.oracle.com/licenses/CDDL
 *
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file LICENSE.txt.
 *
 * MODIFICATIONS:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 */

package com.oracle.bedrock.deferred;

import com.oracle.bedrock.Option;
import com.oracle.bedrock.OptionsByType;
import com.oracle.bedrock.deferred.options.InitialDelay;
import com.oracle.bedrock.deferred.options.MaximumRetryDelay;
import com.oracle.bedrock.deferred.options.RetryFrequency;
import com.oracle.bedrock.options.Timeout;
import com.oracle.bedrock.runtime.concurrent.RemoteCallable;
import com.oracle.bedrock.runtime.java.JavaApplication;
import com.oracle.bedrock.util.Duration;
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;

import java.util.Iterator;
import java.util.concurrent.TimeUnit;

import static com.oracle.bedrock.deferred.DeferredHelper.eventually;
import static com.oracle.bedrock.deferred.DeferredHelper.valueOf;

/**
 * Commonly used "assertThat" methods for the purposes of repetitively asserting
 * the state of {@link Deferred} values.
 * 

* To customize assertion behavior and timeouts, this class allows extensive use * of {@link Option}s. For example, the following {@link Option}s may be used * to customize timeout constraints when asserting {@link Deferred} values; * {@link Timeout}, {@link MaximumRetryDelay}, Initial {@link InitialDelay} and * {@link RetryFrequency}. *

* Copyright (c) 2016. All Rights Reserved. Oracle Corporation.
* Oracle is a registered trademark of Oracle Corporation and/or its affiliates. * * @see Eventually * @see Concurrently * * @author Brian Oliver */ public class Repetitively { /** * Asserts that a value will repetitively satisfy the specified {@link Matcher} * using the specified {@link Option}s. *

* Should the value be the result of a call to {@link DeferredHelper#invoking(Deferred)} * the result is unwrapped into a {@link Deferred} that is then used for the assert. * * @param the type of the value * * @param value the value * @param matcher the {@link Matcher} for the value * @param options the {@link Option}s for the assertion * * @throws AssertionError if the assertion fails */ public static void assertThat(T value, Matcher matcher, Option... options) throws AssertionError { assertThat(null, eventually(value), matcher, options); } /** * Asserts that a value will repetitively satisfy the specified {@link Matcher}. *

* Should the value be the result of a call to {@link DeferredHelper#invoking(Deferred)} * the result is unwrapped into a {@link Deferred} that is then used for the assert. * * @param the type of the value * * @param message the message for the AssertionError (null ok) * @param value the value * @param matcher the {@link Matcher} for the value * * @throws AssertionError if the assertion fails */ public static void assertThat(String message, T value, Matcher matcher) throws AssertionError { assertThat(message, eventually(value), matcher); } /** * Asserts that a value will repetitively satisfy the specified {@link Matcher} * using the provided {@link Option}s. *

* Should the value be the result of a call to {@link DeferredHelper#invoking(Deferred)} * the result is unwrapped into a {@link Deferred} that is then used for the assert. * * @param the type of the value * * @param message the message for the AssertionError (null ok) * @param value the value * @param matcher the {@link Matcher} for the value * @param options the {@link Option}s * * @throws AssertionError if the assertion fails */ public static void assertThat(String message, T value, Matcher matcher, Option... options) throws AssertionError { assertThat(message, eventually(value), matcher, options); } /** * Asserts that a {@link Deferred} value, when available, repetitively satisfies * the specified {@link Matcher} over the {@link Timeout} * and other constraints defined by the provided {@link Option}s. * * @param the type of the value * * @param message the message for the AssertionError (null ok) * @param deferred the {@link Deferred} value * @param matcher the {@link Matcher} for the value * @param options the {@link Option}s * * @throws AssertionError if the assertion fails */ public static void assertThat(String message, Deferred deferred, Matcher matcher, Option... options) throws AssertionError { // determine the time-out and retry constraints OptionsByType optionsByType = OptionsByType.of(options); long initialDelayDurationMS = optionsByType.getOrDefault(InitialDelay.class, InitialDelay.none()).to(TimeUnit.MILLISECONDS); long maximumRetryDurationMS = optionsByType.getOrDefault(Timeout.class, Timeout.after(DeferredHelper.getDefaultEnsuredMaximumRetryDuration())) .to(TimeUnit.MILLISECONDS); long maximumPollingDurationMS = optionsByType.getOrDefault(MaximumRetryDelay.class, MaximumRetryDelay.of(DeferredHelper.getDefaultEnsuredMaximumPollingDuration())) .to(TimeUnit.MILLISECONDS); Iterator retryDurations = optionsByType.getOrDefault(RetryFrequency.class, RetryFrequency.of(DeferredHelper.getDefaultEnsuredRetryDurationsIterable())) .get().iterator(); // the number of times successfully matched int matchCount = 0; // the number of matches attempted int attemptCount = 0; // determine the maximum time we can wait long remainingRetryDurationMS = maximumRetryDurationMS; do { // wait the initial duration if (initialDelayDurationMS > 0) { try { Thread.sleep(initialDelayDurationMS); } catch (InterruptedException e) { throw new AssertionError("Interrupted while resolving " + deferred, e); } // reduce the remaining time remainingRetryDurationMS -= initialDelayDurationMS; // NOTE: even if there's no time remaining we'll at least // attempt to acquire the object reference just once! } // the time the most recent acquisition took long acquisitionDurationMS = 0; try { long started = System.currentTimeMillis(); // set the remaining timeout optionsByType.add(Timeout.after(Math.min(remainingRetryDurationMS, 0), TimeUnit.MILLISECONDS)); T object = DeferredHelper.ensure(deferred, optionsByType.asArray()); long stopped = System.currentTimeMillis(); // the time spent trying to access the resource // is considered as part of the remaining time acquisitionDurationMS = stopped - started; remainingRetryDurationMS -= acquisitionDurationMS < 0 ? 0 : acquisitionDurationMS; // count this attempt attemptCount++; if (matcher.matches(object)) { // continue matching! matchCount++; } else { // generate a description for the match failure StringDescription description = new StringDescription(); matcher.describeMismatch(object, description); // throw an assertion with the error throw new AssertionError(description.toString() + " (attempted " + attemptCount + " time(s), succeeded " + matchCount + " time(s))"); } } catch (PermanentlyUnavailableException e) { // give up immediately! throw new AssertionError("Failed to resolve value for " + deferred, e); } catch (UnsupportedOperationException e) { // give up immediately when an operation is not supported throw new AssertionError("Failed to resolve value for " + deferred, e); } catch (TemporarilyUnavailableException e) { // SKIP: we will retry if the instance is temporarily unavailable } catch (RuntimeException e) { // SKIP: we assume all other runtime exceptions // simply means that we should retry } // determine if we need to retry if (maximumRetryDurationMS < 0 || remainingRetryDurationMS > 0) { // we can only retry while we have retry durations if (retryDurations.hasNext()) { try { Duration duration = retryDurations.next(); long durationMS = duration.to(TimeUnit.MILLISECONDS); // ensure we don't wait longer than the maximum polling duration if (durationMS > maximumPollingDurationMS) { durationMS = maximumPollingDurationMS; } // ensure we don't wait longer that the remaining duration if (remainingRetryDurationMS - durationMS < 0) { durationMS = remainingRetryDurationMS; } // only wait if we have a duration if (durationMS > 0) { TimeUnit.MILLISECONDS.sleep(durationMS); } // reduce the remaining time remainingRetryDurationMS -= durationMS; } catch (InterruptedException e) { // if we're interrupted, we give up immediately throw new AssertionError("Interrupted while resolving " + deferred, e); } } else { // if we run out of retry durations, we give up immediately throw new AssertionError("Exhausted retry time-out durations"); } } } while (maximumRetryDurationMS < 0 || remainingRetryDurationMS > 0); if (matchCount == 0) { throw new AssertionError("Failed to resolve a value for " + deferred); } } /** * Asserts that the specified {@link RemoteCallable} submitted to the * {@link JavaApplication} repetitively matches the specified matcher * using the specified {@link Option}s. * * @param the type of the value * * @param application the {@link JavaApplication} to which the {@link RemoteCallable} will be submitted * @param callable the {@link RemoteCallable} * @param matcher the {@link Matcher} representing the desire condition to match * * @throws AssertionError if the assertion fails */ public static void assertThat(JavaApplication application, RemoteCallable callable, Matcher matcher) throws AssertionError { assertThat(valueOf(new DeferredRemoteExecution(application, callable)), matcher); } /** * Asserts that the specified {@link RemoteCallable} submitted to * the {@link JavaApplication} repetitively matches the specified matcher * using the provided {@link Option}s. * * @param the type of the value * * @param application the {@link JavaApplication} to which the {@link RemoteCallable} will be submitted * @param callable the {@link RemoteCallable} * @param matcher the {@link Matcher} representing the desire condition to match * @param options the {@link Option}s * * @throws AssertionError if the assertion fails */ public static void assertThat(JavaApplication application, RemoteCallable callable, Matcher matcher, Option... options) throws AssertionError { assertThat(valueOf(new DeferredRemoteExecution(application, callable)), matcher, options); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy