1/* 2 * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23package org.netbeans.jemmy; 24 25import java.awt.Component; 26 27/** 28 * 29 * Waits for something defined by Waitable interface to be happened. 30 * 31 * <BR><BR>Timeouts used: <BR> 32 * Waiter.TimeDelta - time delta to check actionProduced result.<BR> 33 * Waiter.WaitingTime - maximal waiting time<BR> 34 * Waiter.AfterWaitingTime - time to sleep after waiting has been finished.<BR> 35 * 36 * @see Timeouts 37 * @see Waitable 38 * 39 * @author Alexandre Iline (alexandre.iline@oracle.com) 40 */ 41public class Waiter<R, P> implements Waitable<R, P>, Timeoutable, Outputable { 42 43 private final static long TIME_DELTA = 10; 44 private final static long WAIT_TIME = 60000; 45 private final static long AFTER_WAIT_TIME = 0; 46 47 private final Waitable<R, P> waitable; 48 private long startTime = 0; 49 private long endTime = -1; 50 private R result; 51 private Timeouts timeouts; 52 private String waitingTimeOrigin; 53 private TestOut out; 54 55 /** 56 * Replace the fine-grained timeouts with a global flag which can be set, 57 * for instance, by a separate thread when a global timeout runs out. 58 */ 59 public static volatile boolean USE_GLOBAL_TIMEOUT = false; 60 public static volatile boolean globalTimeoutExpired = false; 61 62 /** 63 * Constructor. 64 * 65 * @param w Waitable object defining waiting criteria. 66 */ 67 public Waiter(Waitable<R, P> w) { 68 super(); 69 if (w == null) { 70 throw new NullPointerException("Waitable cannot be null"); 71 } 72 setTimeouts(JemmyProperties.getProperties().getTimeouts()); 73 setOutput(JemmyProperties.getProperties().getOutput()); 74 waitable = w; 75 } 76 77 /** 78 * Can be used from subclass. actionProduced() method must be overriden in 79 * a class that uses this super constructor. actionProduced() method in 80 * Waiter will throw UnsupportedOperationException whenever invoked. 81 */ 82 protected Waiter() { 83 super(); 84 setTimeouts(JemmyProperties.getProperties().getTimeouts()); 85 setOutput(JemmyProperties.getProperties().getOutput()); 86 waitable = null; 87 } 88 89 static { 90 Timeouts.initDefault("Waiter.TimeDelta", TIME_DELTA); 91 Timeouts.initDefault("Waiter.WaitingTime", WAIT_TIME); 92 Timeouts.initDefault("Waiter.AfterWaitingTime", AFTER_WAIT_TIME); 93 } 94 95 /** 96 * Defines current timeouts. 97 * 98 * @param timeouts A collection of timeout assignments. 99 * @see org.netbeans.jemmy.Timeoutable 100 * @see org.netbeans.jemmy.Timeouts 101 * @see #getTimeouts 102 */ 103 @Override 104 public void setTimeouts(Timeouts timeouts) { 105 this.timeouts = timeouts; 106 } 107 108 /** 109 * Like {@link #setTimeouts(Timeouts)}, but clones the timeouts first, then 110 * sets "Waiter.WaitingTime" to the timeout whose name is passed in. This 111 * name is remembered for display in timeout error messages so people know 112 * what to adjust. 113 * 114 * @param timeouts to be cloned and in which to look up "useAsWaitingTime". 115 * @param useAsWaitingTime the name of the timeout to apply to 116 * "Waiter.WaitingTime". 117 * @param waitingTimeOrigin overrides {@code useAsWaitingTime} in timeout 118 * reporting if non-null. 119 * @return the cloned timeouts. 120 */ 121 public Timeouts setTimeoutsToCloneOf(Timeouts timeouts, 122 String useAsWaitingTime, String waitingTimeOrigin) { 123 Timeouts t = timeouts.cloneThis(); 124 t.setTimeout("Waiter.WaitingTime", t.getTimeout(useAsWaitingTime)); 125 setTimeouts(t); 126 setWaitingTimeOrigin((null != waitingTimeOrigin) ? waitingTimeOrigin : useAsWaitingTime); 127 return t; 128 } 129 130 /** 131 * @see #setTimeoutsToCloneOf(Timeouts, String, String) 132 */ 133 public Timeouts setTimeoutsToCloneOf(Timeouts timeouts, 134 String useAsWaitingTime) { 135 return setTimeoutsToCloneOf(timeouts, useAsWaitingTime, null); 136 } 137 138 /** 139 * Sets the origin of the current "Waiter.WaitingTime" to be shown in 140 * timeout error messages 141 * 142 * @param origin is the name of the origin. 143 */ 144 public void setWaitingTimeOrigin(String origin) { 145 waitingTimeOrigin = origin; 146 } 147 148 /** 149 * Return current timeouts. 150 * 151 * @return the collection of current timeout assignments. 152 * @see org.netbeans.jemmy.Timeoutable 153 * @see org.netbeans.jemmy.Timeouts 154 * @see #setTimeouts 155 */ 156 @Override 157 public Timeouts getTimeouts() { 158 return timeouts; 159 } 160 161 /** 162 * Defines print output streams or writers. 163 * 164 * @param out Identify the streams or writers used for print output. 165 * @see org.netbeans.jemmy.Outputable 166 * @see org.netbeans.jemmy.TestOut 167 * @see #getOutput 168 */ 169 @Override 170 public void setOutput(TestOut out) { 171 this.out = out; 172 } 173 174 /** 175 * Returns print output streams or writers. 176 * 177 * @return an object that contains references to objects for printing to 178 * output and err streams. 179 * @see org.netbeans.jemmy.Outputable 180 * @see org.netbeans.jemmy.TestOut 181 * @see #setOutput 182 */ 183 @Override 184 public TestOut getOutput() { 185 return out; 186 } 187 188 /** 189 * Waits for not null result of actionProduced method of Waitable 190 * implementation passed into constructor. 191 * 192 * @param waitableObject Object to be passed into actionProduced method. 193 * @return non null result of action. 194 * @throws TimeoutExpiredException 195 * @exception InterruptedException 196 */ 197 public R waitAction(P waitableObject) 198 throws InterruptedException { 199 startTime = System.currentTimeMillis(); 200 out.printTrace(getWaitingStartedMessage()); 201 out.printGolden(getGoldenWaitingStartedMessage()); 202 long timeDelta = timeouts.getTimeout("Waiter.TimeDelta"); 203 while ((result = actionProduced(waitableObject)) == null) { 204 Thread.sleep(timeDelta); 205 if (timeoutExpired()) { 206 out.printError(getTimeoutExpiredMessage(timeFromStart())); 207 out.printGolden(getGoldenTimeoutExpiredMessage()); 208 throw (new TimeoutExpiredException(getActualDescription())); 209 } 210 } 211 endTime = System.currentTimeMillis(); 212 out.printTrace(getActionProducedMessage(endTime - startTime, result)); 213 out.printGolden(getGoldenActionProducedMessage()); 214 Thread.sleep(timeouts.getTimeout("Waiter.AfterWaitingTime")); 215 return result; 216 } 217 218 /** 219 * This method delegates call to the waitable passed in constructor. If a 220 * subclass was created using protected no-parameters constructor, it should 221 * implement its own actionProduced method() as this one will throw an 222 * UnsupportedOperationException. 223 * @see Waitable 224 * @param obj 225 */ 226 @Override 227 public R actionProduced(P obj) { 228 if (waitable != null) { 229 return waitable.actionProduced(obj); 230 } else { 231 throw new UnsupportedOperationException("actionProduced() return " 232 + "value is not defined. It used to return Boolean.TRUE " 233 + "in previous versions of Jemmy."); 234 } 235 } 236 237 /** 238 * @see Waitable 239 */ 240 @Override 241 public String getDescription() { 242 return "Unknown waiting"; 243 } 244 245 @Override 246 public String toString() { 247 return "Waiter{" + "description = " + getDescription() + ", waitable=" + waitable + ", startTime=" + startTime + ", endTime=" + endTime + ", result=" + result + ", waitingTimeOrigin=" + waitingTimeOrigin + '}'; 248 } 249 250 /** 251 * Returns message to be printed before waiting start. 252 * 253 * @return a message. 254 */ 255 protected String getWaitingStartedMessage() { 256 return "Start to wait action \"" + getActualDescription() + "\""; 257 } 258 259 /** 260 * Returns message to be printed when waiting timeout has been expired. 261 * 262 * @param timeSpent time from waiting start (milliseconds) 263 * @return a message. 264 */ 265 protected String getTimeoutExpiredMessage(long timeSpent) { 266 return ("\"" + getActualDescription() + "\" action has not been produced in " 267 + timeSpent + " milliseconds"); 268 } 269 270 /** 271 * Returns message to be printed when waiting has been successfully 272 * finished. 273 * 274 * @param timeSpent time from waiting start (milliseconds) 275 * @param result result of Waitable.actionproduced method. 276 * @return a message. 277 */ 278 protected String getActionProducedMessage(long timeSpent, final Object result) { 279 String resultToString; 280 if (result instanceof Component) { 281 // run toString in dispatch thread 282 resultToString = new QueueTool().invokeSmoothly( 283 new QueueTool.QueueAction<String>("result.toString()") { 284 @Override 285 public String launch() { 286 return result.toString(); 287 } 288 } 289 ); 290 } else { 291 resultToString = result.toString(); 292 } 293 return ("\"" + getActualDescription() + "\" action has been produced in " 294 + timeSpent + " milliseconds with result " 295 + "\n : " + resultToString); 296 } 297 298 /** 299 * Returns message to be printed int golden output before waiting start. 300 * 301 * @return a message. 302 */ 303 protected String getGoldenWaitingStartedMessage() { 304 return "Start to wait action \"" + getActualDescription() + "\""; 305 } 306 307 /** 308 * Returns message to be printed int golden output when waiting timeout has 309 * been expired. 310 * 311 * @return a message. 312 */ 313 protected String getGoldenTimeoutExpiredMessage() { 314 return "\"" + getActualDescription() + "\" action has not been produced"; 315 } 316 317 /** 318 * Returns message to be printed int golden output when waiting has been 319 * successfully finished. 320 * 321 * @return a message. 322 */ 323 protected String getGoldenActionProducedMessage() { 324 return "\"" + getActualDescription() + "\" action has been produced"; 325 } 326 327 /** 328 * Returns time from waiting start. 329 * 330 * @return Time spent for waiting already. 331 */ 332 protected long timeFromStart() { 333 return System.currentTimeMillis() - startTime; 334 } 335 336 private String getActualDescription() { 337 final String suffix = (null == waitingTimeOrigin) ? "" : " (" + waitingTimeOrigin + ")"; 338 if (waitable != null) { 339 return waitable.getDescription() + suffix; 340 } else { 341 return getDescription() + suffix; 342 } 343 } 344 345 private boolean timeoutExpired() { 346 if (USE_GLOBAL_TIMEOUT) { 347 return globalTimeoutExpired; 348 } 349 return timeFromStart() > timeouts.getTimeout("Waiter.WaitingTime"); 350 } 351 352} 353