1 /*
   2  * Copyright (c) 2009, 2012, 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. Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package org.jemmy.fx;
  26 
  27 import com.sun.javafx.tk.Toolkit;
  28 import javafx.application.Platform;
  29 import org.jemmy.JemmyException;
  30 import org.jemmy.TimeoutExpiredException;
  31 import org.jemmy.action.AbstractExecutor;
  32 import org.jemmy.action.Action;
  33 import org.jemmy.env.Environment;
  34 import org.jemmy.env.Timeout;
  35 import org.jemmy.timing.State;
  36 
  37 /**
  38  * A utility class to work with Java FX event queue.
  39  * @author shura, KAM
  40  */
  41 public class QueueExecutor extends AbstractExecutor {
  42 
  43     /**
  44      * @see #isQuiet()
  45      */
  46     public static final Timeout QUEUE_THROUGH_TIME = new Timeout("FXExecutor.FX_QUEUE_THROUGH_TIME", 50);
  47     static final Timeout QUEUE_IDENTIFYING_TIMEOUT =
  48             new Timeout("jemmyfx.executor.queue.thread", 1000);
  49     /**
  50      *
  51      */
  52     public static final QueueExecutor EXECUTOR = new QueueExecutor();
  53     Thread queueThread = null;
  54     EmptyFunction emptyFunction;
  55 
  56     private QueueExecutor() {
  57         super();
  58         emptyFunction = new EmptyFunction();
  59     }
  60 
  61     /**
  62      * Gets what thread is the queue thread.
  63      * @return
  64      */
  65     public Thread getQueueThread() {
  66         if (queueThread == null) {
  67             try {
  68                 Platform.runLater(new Runnable() {
  69 
  70                     @Override
  71                     public void run() {
  72                         queueThread = Thread.currentThread();
  73                     }
  74                 });
  75                 Root.ROOT.getEnvironment().getWaiter(QUEUE_IDENTIFYING_TIMEOUT.getName()).ensureState(new State<Object>() {
  76 
  77                     @Override
  78                     public Object reached() {
  79                         return queueThread;
  80                     }
  81                 });
  82             } catch (TimeoutExpiredException e) {
  83                 //this is bad. THere got to be a way to check if we're on the queue
  84                 //or not. right now - no other way - sorry
  85                 queueThread = Thread.currentThread();
  86             }
  87         }
  88         return queueThread;
  89     }
  90 
  91     /**
  92      * {@inheritDoc}
  93      * @param env
  94      * @param action
  95      * @param parameters
  96      */
  97     @Override
  98     public void executeQueue(Environment env, Action action, Object... parameters) {
  99         if (isOnQueue()) {
 100             action.execute(parameters);
 101         } else {
 102             final WrapperFunction wrapper = new WrapperFunction();
 103             wrapper.setAction(action);
 104             wrapper.setParameters(parameters);
 105             Platform.runLater(wrapper);
 106             wrapper.waitDone(env.getTimeout(MAX_ACTION_TIME));
 107             if (wrapper.failed()) {
 108                 throw new JemmyException("Failed to execute action '" + action + "' through Platform.runLater", action.getThrowable());
 109             }
 110         }
 111     }
 112 
 113     /**
 114      * {@inheritDoc}
 115      * @param env
 116      * @param action
 117      * @param parameters
 118      */
 119     @Override
 120     public void executeQueueDetached(Environment env, Action action, Object... parameters) {
 121         WrapperFunction w = new WrapperFunction();
 122         w.setAction(action);
 123         w.setParameters(parameters);
 124         Platform.runLater(w);
 125     }
 126 
 127     /**
 128      * Checks whether the calling code is already on the queue thread.
 129      * @return
 130      */
 131     @Override
 132     public boolean isOnQueue() {
 133         //return Thread.currentThread().equals(getQueueThread());
 134         try {
 135             Toolkit.getToolkit().checkFxUserThread();
 136         } catch (Throwable th) {
 137             return false;
 138         }
 139         return true;
 140     }
 141 
 142     /**
 143      * Checks whether the things are "quiet". All is currently does is check that
 144      * something comes through the queue quickly enough as defined by
 145      * <code>QUEUE_THROUGH_TIME</code> timeout.
 146      * @return
 147      */
 148     @Override
 149     protected boolean isQuiet() {
 150         emptyFunction.prepare();
 151         Platform.runLater(emptyFunction);
 152         Environment.getEnvironment().getWaiter(MAX_ACTION_TIME).ensureState(new State<Object>() {
 153 
 154             @Override
 155             public Object reached() {
 156                 return emptyFunction.isExecuted() ? "" : null;
 157             }
 158         });
 159         return emptyFunction.getTime() <= QUEUE_THROUGH_TIME.getValue();
 160     }
 161 
 162     class EmptyFunction implements Runnable {
 163 
 164         private long time;
 165         private long startTime;
 166         private boolean executed;
 167 
 168         @Override
 169         public void run() {
 170             time = System.currentTimeMillis() - startTime;
 171             executed = true;
 172         }
 173 
 174         public boolean isExecuted() {
 175             return executed;
 176         }
 177 
 178         public void prepare() {
 179             executed = false;
 180             startTime = System.currentTimeMillis();
 181         }
 182 
 183         public long getTime() {
 184             return time;
 185         }
 186     }
 187 
 188     class WrapperFunction implements Runnable {
 189 
 190         private Action action = null;
 191         private Object[] parameters = null;
 192         private boolean done = false;
 193 
 194         public void setAction(Action action) {
 195             this.action = action;
 196         }
 197 
 198         public void setParameters(Object[] parameters) {
 199             this.parameters = parameters;
 200         }
 201 
 202         @Override
 203         public void run() {
 204             try {
 205                 action.execute(parameters);
 206             } catch (RuntimeException e) {
 207             }
 208             synchronized (this) {
 209                 done = true;
 210                 notifyAll();
 211             }
 212         }
 213 
 214         public boolean isDone() {
 215             return done;
 216         }
 217 
 218         public void clean() {
 219             done = false;
 220             action = null;
 221             parameters = null;
 222         }
 223 
 224         public boolean failed() {
 225             return !done || action.failed();
 226         }
 227 
 228         public Throwable getThrowable() {
 229             return action.getThrowable();
 230         }
 231 
 232         public synchronized void waitDone(Timeout timeout) {
 233             try {
 234                 long maxTime = System.currentTimeMillis() + timeout.getValue();
 235                 while (!done && System.currentTimeMillis() < maxTime) {
 236                     wait(maxTime - System.currentTimeMillis());
 237                 }
 238             } catch (InterruptedException ex) {
 239             }
 240         }
 241     }
 242 }