/* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.richfaces.wait; /** * Implementation of immutable class with purpose of waiting with customizable timeout, interval, and failure behaviour and * delay on start of waiting. * * @author <a href="mailto:lfryc@redhat.com">Lukas Fryc</a> * @version $Revision$ * @param <T> the end implementation of DefaultWaiting as the return type for setter methods */ public class Wait implements Cloneable { public static final long DEFAULT_INTERVAL = 200; /** * Default waiting timeout */ public static final long DEFAULT_TIMEOUT = 10000; /** * Indicates when the first test for the condition should be delayed after waiting starts. */ private boolean isDelayed = true; /** * Interval between tries to test condition for satisfaction */ private long interval = DEFAULT_INTERVAL; /** * Timeout of whole waiting procedure */ private long timeout = DEFAULT_TIMEOUT; /** * Failure indicates that waiting timeouted. * * If is set to null, no failure will be thrown after timeout. */ private Object failure = "Waiting timed out"; /** * Arguments to format failure message if it is string value and should be formatted */ private Object[] failureArgs; /** * Returns the interval set for this object. * * @return the set interval */ protected long getInterval() { return interval; } /** * Returns the timeout set for this object. * * @return the timeout set for this object */ protected long getTimeout() { return timeout; } /** * Returns if this waiting's start is delayed. * * @return if this waiting's start is delayed */ protected boolean isDelayed() { return isDelayed; } /** * Returns instance of waiting with same properties like this object and interval set to given interval. * * @param interval in milliseconds that will be preset to returned instance of Waiting * @return Waiting instance configured with given interval */ public Wait interval(long interval) { if (interval == this.interval) { return (Wait) this; } Wait copy = (Wait) this.copy(); copy.interval = interval; return (Wait) copy; } /** * Returns instance of waiting with same properties like this object and timeout set to given timeout. * * @param timeout in milliseconds that will be preset to returned instance of Waiting * @return Waiting instance configured with given timeout */ public Wait timeout(long timeout) { if (timeout == this.timeout) { return (Wait) this; } Wait copy = (Wait) this.copy(); copy.timeout = timeout; return (Wait) copy; } /** * <p> * Returns Waiting object initialized with given exception. * </p> * * <p> * If the exception is instance of RuntimeException, it will be thrown in case of waiting timed out. * </p> * * <p> * If the exception isn't instance of RuntimeException, the WaitingTimeoutException will be thrown with cause preset to the * given Throwable. * </p> * * <p> * If failure is set to null, timeout will not result to failure! * </p> * * @param exception the instance of RuntimeException to be thrown or any other Exception when the WaitTimeoutException * should be thrown with this exception as cause * @return Waiting instance configured with given exception as cause of waiting timeout */ public Wait failWith(Exception exception) { if (exception == null && this.failure == null) { return (Wait) this; } Wait copy = (Wait) this.copy(); copy.failure = exception; copy.failureArgs = null; return (Wait) copy; } /** * <p> * Returns preset instance of waiting with given failure message parametrized by given objects. * </p> * * <p> * To parametrize failure message, the * {@link org.jboss.arquillian.ajocado.format.SimplifiedFormat#format(String, Object...)} will be used. * </p> * * <p> * If failure is set to null, timeout will not result to failure! * </p> * * @param failureMessage character sequence that will be used as message of exception thrown in case of waiting timeout or * null if waiting timeout shouldn't result to failure * @param arguments arguments to failureMessage which will be use in parametrization of failureMessage * @return Waiting instance initialized with given failureMessage and arguments */ public Wait failWith(CharSequence failureMessage, Object... arguments) { Wait copy = (Wait) this.copy(); copy.failure = failureMessage; copy.failureArgs = arguments; return (Wait) copy; } /** * Sets no failure after waiting timeout. * * Waiting timeout with this preset don't result to failure! * * @return Waiting instance initialized with no failure */ public Wait dontFail() { return failWith(null); } /** * Sets no delay between start of waiting and first test for conditions. * * @return Waiting instance initialized with no delay */ public Wait noDelay() { return withDelay(false); } /** * <p> * Set if testing condition should be delayed of one interval after the start of waiting. * </p> * * <p> * The length of delay is one interval (see {@link #interval(long)}). * </p> * * @param isDelayed true if start of condition testing should be delayed; false otherwise * @return Waiting instance initialized with the delay before start of testing conditions if isDelayed is set to true; with * no delay otherwise */ public Wait withDelay(boolean isDelayed) { if (isDelayed == this.isDelayed) { return (Wait) this; } Wait copy = (Wait) this.copy(); copy.isDelayed = isDelayed; return (Wait) copy; } /** * Stars loop waiting to satisfy condition. * * @param condition what wait for to be satisfied */ public void until(Condition condition) { long start = System.currentTimeMillis(); long end = start + this.getTimeout(); boolean delay = this.isDelayed(); while (System.currentTimeMillis() < end) { if (!delay && condition.isTrue()) { return; } delay = false; try { Thread.sleep(this.getInterval()); } catch (InterruptedException e) { throw new RuntimeException(e); } if (System.currentTimeMillis() >= end) { if (condition.isTrue()) { return; } } } fail(); } /** * Tries to fail by throwing 'failure' throwable. * * If failure is instance of RuntimeException, will be directly thrown. Otherwise will be failure clothe to * RuntimeException. * * If failure is null, method wont fail. */ protected void fail() { if (failure != null) { throw prepareFailure(); } } /** * Prepares a exception for failing the waiting * * @return runtime exception */ private RuntimeException prepareFailure() { if (failure instanceof RuntimeException) { return (RuntimeException) failure; } if (failure instanceof CharSequence) { return new WaitTimeoutException((CharSequence) failure, failureArgs); } return new WaitTimeoutException((Exception) failure); } /** * This methods helps to make copies of current instance. * * @return copy of current instance */ private Wait copy() { try { return (Wait) this.clone(); } catch (CloneNotSupportedException e) { throw new IllegalStateException(e); } } }