--- old/core/JemmyAWTInput/src/org/jemmy/input/RobotExecutor.java 2019-03-06 17:23:24.000000000 -0800 +++ /dev/null 2019-03-06 17:23:24.000000000 -0800 @@ -1,603 +0,0 @@ -/* - * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code 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 General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package org.jemmy.input; - - -import java.awt.EventQueue; -import java.awt.image.BufferedImage; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OptionalDataException; -import java.io.PrintWriter; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Modifier; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.jemmy.JemmyException; -import org.jemmy.Rectangle; -import org.jemmy.env.Environment; -import org.jemmy.env.Timeout; -import org.jemmy.image.AWTImage; -import org.jemmy.image.Image; -import org.jemmy.image.PNGDecoder; -import org.jemmy.image.PNGEncoder; -import org.jemmy.timing.State; -import org.jemmy.timing.Waiter; -import org.jemmy.interfaces.Keyboard.KeyboardButton; -import org.jemmy.interfaces.Mouse.MouseButton; - -/** - * - * @author КАМ - */ -class RobotExecutor { - - private static RobotExecutor instance; - private AWTMap awtMap = null; - - public static RobotExecutor get() { - if (instance == null) { - instance = new RobotExecutor(); - } - return instance; - } - - /** - * A reference to the robot instance. - */ - protected ClassReference robotReference = null; - protected Timeout autoDelay; - private boolean inited = false; - private boolean runInOtherJVM = false; - private boolean ready = false; - private boolean connectionEstablished = false; - private ObjectOutputStream outputStream; - private ObjectInputStream inputStream; - private Socket socket; - private int connectionPort; - private String connectionHost; - public static final int CONNECTION_TIMEOUT = Integer.parseInt( - (String)Environment.getEnvironment().getProperty( - AWTRobotInputFactory.OTHER_VM_CONNECTION_TIMEOUT_PROPERTY, - Integer.toString(60000 * 15))); // 15 min - - public RobotExecutor() { - } - - void setAWTMap(AWTMap awtMap) { - this.awtMap = awtMap; - } - - AWTMap getAWTMap() { - if (awtMap == null) { - awtMap = new AWTMap(); - } - return awtMap; - } - - private void ensureInited() { - if (!inited) { - runInOtherJVM = Boolean.parseBoolean((String)Environment.getEnvironment() - .getProperty(AWTRobotInputFactory.OTHER_VM_PROPERTY, - Boolean.toString(runInOtherJVM))); - inited = true; - } - } - - public Image createScreenCapture(Rectangle screenRect) { - Object result = makeAnOperation("createScreenCapture", new Object[] { - new java.awt.Rectangle(screenRect.x, screenRect.y, screenRect.width, - screenRect.height) }, - new Class[] { java.awt.Rectangle.class }); - if (result.getClass().isAssignableFrom(BufferedImage.class)) { - return new AWTImage(BufferedImage.class.cast(result)); - } else { - throw new JemmyException("Screen capture (" + result - + ") is not a BufferedImage"); - } - } - - public Object makeAnOperation(String method, Object[] params, Class[] paramClasses) { - ensureInited(); - if (runInOtherJVM) { - return makeAnOperationRemotely(method, params, paramClasses); - } else { - return makeAnOperationLocally(method, params, paramClasses); - } - } - - public void exit() { - ensureInited(); - if (runInOtherJVM) { - ensureConnection(); - try { - outputStream.writeObject("exit"); - connectionEstablished = false; - deleteProperties(); - } catch (IOException ex) { - throw new JemmyException("Failed to invoke exit", ex); - } - } - } - - private Object makeAnOperationLocally(String method, Object[] params, Class[] paramClasses) { - if (robotReference == null) { - initRobot(); - } - try { - convert(method, params, paramClasses); - Object result = robotReference.invokeMethod(method, params, paramClasses); - synchronizeRobot(); - return result; - } catch (InvocationTargetException e) { - throw (new JemmyException("Exception during java.awt.Robot accessing", e)); - } catch (IllegalStateException e) { - throw (new JemmyException("Exception during java.awt.Robot accessing", e)); - } catch (NoSuchMethodException e) { - throw (new JemmyException("Exception during java.awt.Robot accessing", e)); - } catch (IllegalAccessException e) { - throw (new JemmyException("Exception during java.awt.Robot accessing", e)); - } - } - - private int convert(Object obj) { - if (MouseButton.class.isAssignableFrom(obj.getClass())) { - return awtMap.convert((MouseButton)obj); - } else if (KeyboardButton.class.isAssignableFrom(obj.getClass())) { - return awtMap.convert((KeyboardButton)obj); - } else { - throw new JemmyException("Unable to recognize object", obj); - } - } - - private static final Set convertables = new HashSet(Arrays.asList(new String[] {"mousePress", "mouseRelease", "keyPress", "keyRelease"})); - - private void convert(String method, Object[] params, Class[] paramClasses) { - if (convertables.contains(method)) - for (int i = 0; i < params.length; i++) { - params[i] = new Integer(convert(params[i])); - paramClasses[i] = Integer.TYPE; - } - } - - public static void main(String[] args) { - System.setProperty("apple.awt.UIElement", "true"); - if (args.length != 0 && args.length != 1) { - System.err.println("Usage: java ... [-D" + - Environment.JEMMY_PROPERTIES_FILE_PROPERTY + "=" + - "<.jemmy.properties full path>]" + - " RobotExecutor [connectionPort]"); - System.exit(-1); - } - if (args.length == 1) { - Environment.getEnvironment().setProperty( - AWTRobotInputFactory.OTHER_VM_PORT_PROPERTY, args[0]); - } - RobotExecutor re = new RobotExecutor(); - try { - re.server(); - } catch (Exception ex) { - ex.printStackTrace(System.err); - System.err.flush(); - System.exit(-1); - } - } - - private File props; - - private void deleteProperties() { - if (props != null) { - props.delete(); - props = null; - } - } - - private void prepareProperties() { - deleteProperties(); - try { - props = File.createTempFile(".jemmy.othervm.", ".properties"); - props.deleteOnExit(); - PrintWriter fw = new PrintWriter(props); - for(Field f : AWTRobotInputFactory.class.getDeclaredFields()) { - if ((f.getModifiers() & Modifier.FINAL) != 0 && - (f.getModifiers() & Modifier.STATIC) != 0 && - f.getType().equals(String.class) && - f.getName().startsWith("OTHER_VM_") && - Environment.getEnvironment().getProperty((String)f.get(null)) != null) { - fw.println(f.get(null) + "=" + Environment.getEnvironment().getProperty((String)f.get(null))); - } - } - fw.close(); - } catch (IllegalArgumentException ex) { - throw new JemmyException("Failed to create temporary properties file: " + props.getAbsolutePath(), ex); - } catch (IllegalAccessException ex) { - throw new JemmyException("Failed to create temporary properties file: " + props.getAbsolutePath(), ex); - } catch (IOException ex) { - throw new JemmyException("Failed to create temporary properties file: " + props.getAbsolutePath(), ex); - } - - } - - private void startServer() { - try { - prepareProperties(); - ProcessBuilder pb = new ProcessBuilder("java", - //"-Xrunjdwp:transport=dt_socket,suspend=y,server=y,address=8000", - "-cp", System.getProperty("java.class.path"), - "-D" + Environment.JEMMY_PROPERTIES_FILE_PROPERTY + - "=" + props.getAbsolutePath(), - RobotExecutor.class.getName(), - Integer.toString(connectionPort)); - // TODO: Improve output -// System.out.println("Starting server"); -// System.out.println("Command: " + pb.command()); -// System.out.flush(); - pb.redirectErrorStream(true); - final Process p = pb.start(); - new Thread() { - - @Override - public void run() { - BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); - while (true) { - try { - String line = br.readLine(); - if (line == null) { - break; - } - System.out.println("SERVER: " + line); - } catch (IOException ex) { - throw new JemmyException("Exception during other JVM output processing", ex); - } - } - } - }.start(); - } catch (IOException ex) { - throw new JemmyException("Failed to start other JVM", ex); - } - } - - public void ensureConnection() { - ensureInited(); - if (runInOtherJVM && !connectionEstablished) { - initClientConnection(); - } - } - - private void initClientConnection() { - connectionHost = (String)Environment.getEnvironment().getProperty( - AWTRobotInputFactory.OTHER_VM_HOST_PROPERTY, "localhost"); - connectionPort = Integer.parseInt((String)Environment.getEnvironment() - .getProperty(AWTRobotInputFactory.OTHER_VM_PORT_PROPERTY, - "53669")); - try { - try { - socket = new Socket(connectionHost, connectionPort); - } catch (IOException ex) { - if ("localhost".equalsIgnoreCase(connectionHost) - || "127.0.0.1".equals(connectionHost)) { - // TODO Improve check for localhost - startServer(); - Environment.getEnvironment().getTimeout(""); - Timeout waitTime = new Timeout("connection wait time", 5 * 60000); - socket = new Waiter(waitTime).ensureState(new State() { - Exception ex; - public Socket reached() { - Socket socket = null; - try { - socket = new Socket(connectionHost, connectionPort); - } catch (UnknownHostException ex1) { - ex = ex1; - } catch (Exception ex1) { - ex = ex1; - } - return socket; - } - - @Override - public String toString() { - if (ex != null) { - // TODO: Provide better mechanics for exception handling - Logger.getLogger(RobotExecutor.class.getName()) - .log(Level.INFO, null, ex); - } - return "Waiting for connection to be established " + - "with other JVM (" + connectionHost - + ":" + connectionPort + ", exception: " + ex + ")"; - } - }); - } else { - throw new JemmyException("Failed to establish socket " + - "connection with other JVM (" + connectionHost - + ":" + connectionPort + ")", ex); - } - } - outputStream = new ObjectOutputStream(socket.getOutputStream()); - inputStream = new ObjectInputStream(socket.getInputStream()); - - connectionEstablished = true; - ready = true; - - System.out.println("Connection established!"); - setAutoDelay(autoDelay); - } catch (IOException ex) { - throw new JemmyException("Failed to establish socket connection " + - "with other JVM (" + connectionHost + ":" + connectionPort - + ")", ex); - } - } - - public synchronized Object getProperty(String name) { - ensureConnection(); - try { - outputStream.writeObject("getProperty"); - outputStream.writeObject(name); - Object result = inputStream.readObject(); - String response = (String)(inputStream.readObject()); - if (!"OK".equals(response)) { - throw new JemmyException("Remote operation didn't succeed"); - } - return result; - } catch (ClassNotFoundException ex) { - throw new JemmyException("Socket communication with other JVM failed", ex); - } catch (OptionalDataException ex) { - throw new JemmyException("Socket communication with other JVM " + - "failed: OptionalDataException eof = " + ex.eof + ", " + - "length = " + ex.length, ex); - } catch (IOException ex) { - throw new JemmyException("Socket communication with other JVM failed", ex); - } - } - - private synchronized Object makeAnOperationRemotely(String method, Object[] params, Class[] paramClasses) { - ensureConnection(); - try { - outputStream.writeObject("makeAnOperation"); - outputStream.writeObject(method); - outputStream.writeObject(params); - outputStream.writeObject(paramClasses); - Object result; - String response = (String)(inputStream.readObject()); - if ("image".equals(response)) { - result = PNGDecoder.decode(inputStream, false); - } else { - if (!"OK".equals(response)) { - throw new JemmyException("Remote operation didn't succeed"); - } - result = inputStream.readObject(); - } - return result; - } catch (ClassNotFoundException ex) { - throw new JemmyException("Socket communication with other JVM failed", ex); - } catch (OptionalDataException ex) { - throw new JemmyException("Socket communication with other JVM " + - "failed: OptionalDataException eof = " + ex.eof + ", " + - "length = " + ex.length, ex); - } catch (IOException ex) { - throw new JemmyException("Socket communication with other JVM failed", ex); - } - } - - private void server() { - System.out.println("Robot ready!"); - System.out.flush(); - ServerSocket sc; - connectionPort = Integer.parseInt((String)Environment.getEnvironment() - .getProperty(AWTRobotInputFactory.OTHER_VM_PORT_PROPERTY, - "53669")); - while(true) { - Thread watchdog = new Thread("RobotExecutor.server watchdog") { - - @Override - public void run() { - try { - Thread.sleep(CONNECTION_TIMEOUT); - System.out.println("Exiting server as there is no " + - "connection for " + CONNECTION_TIMEOUT / 60000.0 - + " minutes"); - System.out.flush(); - System.exit(0); - } catch (InterruptedException ex) { - // Ignoring exception as it is okay - } - } - - }; - watchdog.start(); - System.out.println("Waiting for incoming connection for up to " - + CONNECTION_TIMEOUT / 60000.0 + " minutes"); - try { - sc = new ServerSocket(connectionPort); - socket = sc.accept(); - watchdog.interrupt(); - } catch (IOException ex) { - throw new JemmyException("Can't establish connection with client", ex); - } - System.out.println("Connection established!"); - try { - inputStream = new ObjectInputStream(socket.getInputStream()); - outputStream = new ObjectOutputStream(socket.getOutputStream()); - while(true) { - String command = (String)inputStream.readObject(); - if ("exit".equals(command)) { - System.exit(0); - } - if ("getProperty".equals(command)) { - String property = (String)inputStream.readObject(); - outputStream.writeObject(Environment.getEnvironment().getProperty(property)); - outputStream.writeObject("OK"); - } - if ("makeAnOperation".equals(command)) { - String method = (String)inputStream.readObject(); - Object[] params = (Object[])inputStream.readObject(); - Class[] paramClasses = (Class[])inputStream.readObject(); - Object result = makeAnOperationLocally(method, params, - paramClasses); - if (result instanceof BufferedImage) { - outputStream.writeObject("image"); - BufferedImage image = BufferedImage.class.cast(result); - new PNGEncoder(outputStream, PNGEncoder.COLOR_MODE) - .encode(image, false); - } else { - outputStream.writeObject("OK"); - outputStream.writeObject(result); - } - } - } - } catch (ClassNotFoundException ex) { - throw new JemmyException("Socket communication with other " + - "JVM failed", ex); - } catch (IOException ex) { - Logger.getLogger(RobotExecutor.class.getName()) - .log(Level.SEVERE, null, ex); - } finally { - if (socket != null) { - try { - socket.close(); - } catch (IOException ex) { - Logger.getLogger(RobotExecutor.class.getName()).log( - Level.SEVERE, "Exception during socket closing", ex); - } - } - if (sc != null) { - try { - sc.close(); - } catch (IOException ex) { - Logger.getLogger(RobotExecutor.class.getName()).log( - Level.SEVERE, "Exception during server socket " + - "closing", ex); - } - } - } - } - } - - private void initRobot() { - // need to init Robot in dispatch thread because it hangs on Linux - // (see http://www.netbeans.org/issues/show_bug.cgi?id=37476) - if (EventQueue.isDispatchThread()) { - doInitRobot(); - } else { - try { - EventQueue.invokeAndWait(new Runnable() { - - public void run() { - doInitRobot(); - } - }); - } catch (InterruptedException ex) { - throw new JemmyException("Failed to initialize robot", ex); - } catch (InvocationTargetException ex) { - throw new JemmyException("Failed to initialize robot", ex); - } - } - } - - private void doInitRobot() { - try { - ClassReference robotClassReverence = new ClassReference("java.awt.Robot"); - robotReference = new ClassReference(robotClassReverence.newInstance(null, null)); - if (awtMap == null) { - awtMap = new AWTMap(); - } - setAutoDelay(autoDelay); - ready = true; - } catch (InvocationTargetException e) { - throw (new JemmyException("Exception during java.awt.Robot accessing", e)); - } catch (IllegalStateException e) { - throw (new JemmyException("Exception during java.awt.Robot accessing", e)); - } catch (NoSuchMethodException e) { - throw (new JemmyException("Exception during java.awt.Robot accessing", e)); - } catch (IllegalAccessException e) { - throw (new JemmyException("Exception during java.awt.Robot accessing", e)); - } catch (ClassNotFoundException e) { - throw (new JemmyException("Exception during java.awt.Robot accessing", e)); - } catch (InstantiationException e) { - throw (new JemmyException("Exception during java.awt.Robot accessing", e)); - } - } - - /** - * Calls java.awt.Robot.waitForIdle() method. - */ - protected void synchronizeRobot() { - ensureInited(); - if (!runInOtherJVM) { - // TODO: It looks like this method is rudimentary - if (!EventQueue.isDispatchThread()) { - if (robotReference == null) { - initRobot(); - } - try { - robotReference.invokeMethod("waitForIdle", null, null); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - - public void setAutoDelay(Timeout autoDelay) { - this.autoDelay = autoDelay; - if (ready) { - makeAnOperation("setAutoDelay", new Object[]{new Integer((int) ((autoDelay != null) ? autoDelay.getValue() : 0))}, new Class[]{Integer.TYPE}); - } - } - - public boolean isRunInOtherJVM() { - ensureInited(); - return runInOtherJVM; - } - - public void setRunInOtherJVM(boolean runInOtherJVM) { - if (inited && this.runInOtherJVM && this.connectionEstablished && !runInOtherJVM) { - shutdownConnection(); - } - this.runInOtherJVM = runInOtherJVM; - inited = true; - ready = false; - } - - private void shutdownConnection() { - try { - outputStream.writeObject("exit"); - socket.close(); - connectionEstablished = false; - } catch (IOException ex) { - throw new JemmyException("Failed to shutdown connection", ex); - } - } -} --- /dev/null 2019-03-06 17:23:24.000000000 -0800 +++ new/core/JemmyAWTInput/src/org/jemmy/input/awt/RobotExecutor.java 2019-03-06 17:23:24.000000000 -0800 @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.jemmy.input.awt; + + +import java.awt.EventQueue; +import java.awt.image.BufferedImage; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OptionalDataException; +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.jemmy.JemmyException; +import org.jemmy.Rectangle; +import org.jemmy.env.Environment; +import org.jemmy.env.Timeout; +import org.jemmy.image.awt.AWTImage; +import org.jemmy.image.Image; +import org.jemmy.image.awt.PNGDecoder; +import org.jemmy.image.awt.PNGEncoder; +import org.jemmy.timing.State; +import org.jemmy.timing.Waiter; +import org.jemmy.interfaces.Keyboard.KeyboardButton; +import org.jemmy.interfaces.Mouse.MouseButton; + +/** + * + * @author КАМ + */ +class RobotExecutor { + + private static RobotExecutor instance; + private AWTMap awtMap = null; + + public static RobotExecutor get() { + if (instance == null) { + instance = new RobotExecutor(); + } + return instance; + } + + /** + * A reference to the robot instance. + */ + protected ClassReference robotReference = null; + protected Timeout autoDelay; + private boolean inited = false; + private boolean runInOtherJVM = false; + private boolean ready = false; + private boolean connectionEstablished = false; + private ObjectOutputStream outputStream; + private ObjectInputStream inputStream; + private Socket socket; + private int connectionPort; + private String connectionHost; + public static final int CONNECTION_TIMEOUT = Integer.parseInt( + (String)Environment.getEnvironment().getProperty( + AWTRobotInputFactory.OTHER_VM_CONNECTION_TIMEOUT_PROPERTY, + Integer.toString(60000 * 15))); // 15 min + + public RobotExecutor() { + } + + void setAWTMap(AWTMap awtMap) { + this.awtMap = awtMap; + } + + AWTMap getAWTMap() { + if (awtMap == null) { + awtMap = new AWTMap(); + } + return awtMap; + } + + private void ensureInited() { + if (!inited) { + runInOtherJVM = Boolean.parseBoolean((String)Environment.getEnvironment() + .getProperty(AWTRobotInputFactory.OTHER_VM_PROPERTY, + Boolean.toString(runInOtherJVM))); + inited = true; + } + } + + public Image createScreenCapture(Rectangle screenRect) { + Object result = makeAnOperation("createScreenCapture", new Object[] { + new java.awt.Rectangle(screenRect.x, screenRect.y, screenRect.width, + screenRect.height) }, + new Class[] { java.awt.Rectangle.class }); + if (result.getClass().isAssignableFrom(BufferedImage.class)) { + return new AWTImage(BufferedImage.class.cast(result)); + } else { + throw new JemmyException("Screen capture (" + result + + ") is not a BufferedImage"); + } + } + + public Object makeAnOperation(String method, Object[] params, Class[] paramClasses) { + ensureInited(); + if (runInOtherJVM) { + return makeAnOperationRemotely(method, params, paramClasses); + } else { + return makeAnOperationLocally(method, params, paramClasses); + } + } + + public void exit() { + ensureInited(); + if (runInOtherJVM) { + ensureConnection(); + try { + outputStream.writeObject("exit"); + connectionEstablished = false; + deleteProperties(); + } catch (IOException ex) { + throw new JemmyException("Failed to invoke exit", ex); + } + } + } + + private Object makeAnOperationLocally(String method, Object[] params, Class[] paramClasses) { + if (robotReference == null) { + initRobot(); + } + try { + convert(method, params, paramClasses); + Object result = robotReference.invokeMethod(method, params, paramClasses); + synchronizeRobot(); + return result; + } catch (InvocationTargetException e) { + throw (new JemmyException("Exception during java.awt.Robot accessing", e)); + } catch (IllegalStateException e) { + throw (new JemmyException("Exception during java.awt.Robot accessing", e)); + } catch (NoSuchMethodException e) { + throw (new JemmyException("Exception during java.awt.Robot accessing", e)); + } catch (IllegalAccessException e) { + throw (new JemmyException("Exception during java.awt.Robot accessing", e)); + } + } + + private int convert(Object obj) { + if (MouseButton.class.isAssignableFrom(obj.getClass())) { + return awtMap.convert((MouseButton)obj); + } else if (KeyboardButton.class.isAssignableFrom(obj.getClass())) { + return awtMap.convert((KeyboardButton)obj); + } else { + throw new JemmyException("Unable to recognize object", obj); + } + } + + private static final Set convertables = new HashSet(Arrays.asList(new String[] {"mousePress", "mouseRelease", "keyPress", "keyRelease"})); + + private void convert(String method, Object[] params, Class[] paramClasses) { + if (convertables.contains(method)) + for (int i = 0; i < params.length; i++) { + params[i] = new Integer(convert(params[i])); + paramClasses[i] = Integer.TYPE; + } + } + + public static void main(String[] args) { + System.setProperty("apple.awt.UIElement", "true"); + if (args.length != 0 && args.length != 1) { + System.err.println("Usage: java ... [-D" + + Environment.JEMMY_PROPERTIES_FILE_PROPERTY + "=" + + "<.jemmy.properties full path>]" + + " RobotExecutor [connectionPort]"); + System.exit(-1); + } + if (args.length == 1) { + Environment.getEnvironment().setProperty( + AWTRobotInputFactory.OTHER_VM_PORT_PROPERTY, args[0]); + } + RobotExecutor re = new RobotExecutor(); + try { + re.server(); + } catch (Exception ex) { + ex.printStackTrace(System.err); + System.err.flush(); + System.exit(-1); + } + } + + private File props; + + private void deleteProperties() { + if (props != null) { + props.delete(); + props = null; + } + } + + private void prepareProperties() { + deleteProperties(); + try { + props = File.createTempFile(".jemmy.othervm.", ".properties"); + props.deleteOnExit(); + PrintWriter fw = new PrintWriter(props); + for(Field f : AWTRobotInputFactory.class.getDeclaredFields()) { + if ((f.getModifiers() & Modifier.FINAL) != 0 && + (f.getModifiers() & Modifier.STATIC) != 0 && + f.getType().equals(String.class) && + f.getName().startsWith("OTHER_VM_") && + Environment.getEnvironment().getProperty((String)f.get(null)) != null) { + fw.println(f.get(null) + "=" + Environment.getEnvironment().getProperty((String)f.get(null))); + } + } + fw.close(); + } catch (IllegalArgumentException ex) { + throw new JemmyException("Failed to create temporary properties file: " + props.getAbsolutePath(), ex); + } catch (IllegalAccessException ex) { + throw new JemmyException("Failed to create temporary properties file: " + props.getAbsolutePath(), ex); + } catch (IOException ex) { + throw new JemmyException("Failed to create temporary properties file: " + props.getAbsolutePath(), ex); + } + + } + + private void startServer() { + try { + prepareProperties(); + ProcessBuilder pb = new ProcessBuilder("java", + //"-Xrunjdwp:transport=dt_socket,suspend=y,server=y,address=8000", + "-cp", System.getProperty("java.class.path"), + "-D" + Environment.JEMMY_PROPERTIES_FILE_PROPERTY + + "=" + props.getAbsolutePath(), + RobotExecutor.class.getName(), + Integer.toString(connectionPort)); + // TODO: Improve output +// System.out.println("Starting server"); +// System.out.println("Command: " + pb.command()); +// System.out.flush(); + pb.redirectErrorStream(true); + final Process p = pb.start(); + new Thread() { + + @Override + public void run() { + BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); + while (true) { + try { + String line = br.readLine(); + if (line == null) { + break; + } + System.out.println("SERVER: " + line); + } catch (IOException ex) { + throw new JemmyException("Exception during other JVM output processing", ex); + } + } + } + }.start(); + } catch (IOException ex) { + throw new JemmyException("Failed to start other JVM", ex); + } + } + + public void ensureConnection() { + ensureInited(); + if (runInOtherJVM && !connectionEstablished) { + initClientConnection(); + } + } + + private void initClientConnection() { + connectionHost = (String)Environment.getEnvironment().getProperty( + AWTRobotInputFactory.OTHER_VM_HOST_PROPERTY, "localhost"); + connectionPort = Integer.parseInt((String)Environment.getEnvironment() + .getProperty(AWTRobotInputFactory.OTHER_VM_PORT_PROPERTY, + "53669")); + try { + try { + socket = new Socket(connectionHost, connectionPort); + } catch (IOException ex) { + if ("localhost".equalsIgnoreCase(connectionHost) + || "127.0.0.1".equals(connectionHost)) { + // TODO Improve check for localhost + startServer(); + Environment.getEnvironment().getTimeout(""); + Timeout waitTime = new Timeout("connection wait time", 5 * 60000); + socket = new Waiter(waitTime).ensureState(new State() { + Exception ex; + public Socket reached() { + Socket socket = null; + try { + socket = new Socket(connectionHost, connectionPort); + } catch (UnknownHostException ex1) { + ex = ex1; + } catch (Exception ex1) { + ex = ex1; + } + return socket; + } + + @Override + public String toString() { + if (ex != null) { + // TODO: Provide better mechanics for exception handling + Logger.getLogger(RobotExecutor.class.getName()) + .log(Level.INFO, null, ex); + } + return "Waiting for connection to be established " + + "with other JVM (" + connectionHost + + ":" + connectionPort + ", exception: " + ex + ")"; + } + }); + } else { + throw new JemmyException("Failed to establish socket " + + "connection with other JVM (" + connectionHost + + ":" + connectionPort + ")", ex); + } + } + outputStream = new ObjectOutputStream(socket.getOutputStream()); + inputStream = new ObjectInputStream(socket.getInputStream()); + + connectionEstablished = true; + ready = true; + + System.out.println("Connection established!"); + setAutoDelay(autoDelay); + } catch (IOException ex) { + throw new JemmyException("Failed to establish socket connection " + + "with other JVM (" + connectionHost + ":" + connectionPort + + ")", ex); + } + } + + public synchronized Object getProperty(String name) { + ensureConnection(); + try { + outputStream.writeObject("getProperty"); + outputStream.writeObject(name); + Object result = inputStream.readObject(); + String response = (String)(inputStream.readObject()); + if (!"OK".equals(response)) { + throw new JemmyException("Remote operation didn't succeed"); + } + return result; + } catch (ClassNotFoundException ex) { + throw new JemmyException("Socket communication with other JVM failed", ex); + } catch (OptionalDataException ex) { + throw new JemmyException("Socket communication with other JVM " + + "failed: OptionalDataException eof = " + ex.eof + ", " + + "length = " + ex.length, ex); + } catch (IOException ex) { + throw new JemmyException("Socket communication with other JVM failed", ex); + } + } + + private synchronized Object makeAnOperationRemotely(String method, Object[] params, Class[] paramClasses) { + ensureConnection(); + try { + outputStream.writeObject("makeAnOperation"); + outputStream.writeObject(method); + outputStream.writeObject(params); + outputStream.writeObject(paramClasses); + Object result; + String response = (String)(inputStream.readObject()); + if ("image".equals(response)) { + result = PNGDecoder.decode(inputStream, false); + } else { + if (!"OK".equals(response)) { + throw new JemmyException("Remote operation didn't succeed"); + } + result = inputStream.readObject(); + } + return result; + } catch (ClassNotFoundException ex) { + throw new JemmyException("Socket communication with other JVM failed", ex); + } catch (OptionalDataException ex) { + throw new JemmyException("Socket communication with other JVM " + + "failed: OptionalDataException eof = " + ex.eof + ", " + + "length = " + ex.length, ex); + } catch (IOException ex) { + throw new JemmyException("Socket communication with other JVM failed", ex); + } + } + + private void server() { + System.out.println("Robot ready!"); + System.out.flush(); + ServerSocket sc; + connectionPort = Integer.parseInt((String)Environment.getEnvironment() + .getProperty(AWTRobotInputFactory.OTHER_VM_PORT_PROPERTY, + "53669")); + while(true) { + Thread watchdog = new Thread("RobotExecutor.server watchdog") { + + @Override + public void run() { + try { + Thread.sleep(CONNECTION_TIMEOUT); + System.out.println("Exiting server as there is no " + + "connection for " + CONNECTION_TIMEOUT / 60000.0 + + " minutes"); + System.out.flush(); + System.exit(0); + } catch (InterruptedException ex) { + // Ignoring exception as it is okay + } + } + + }; + watchdog.start(); + System.out.println("Waiting for incoming connection for up to " + + CONNECTION_TIMEOUT / 60000.0 + " minutes"); + try { + sc = new ServerSocket(connectionPort); + socket = sc.accept(); + watchdog.interrupt(); + } catch (IOException ex) { + throw new JemmyException("Can't establish connection with client", ex); + } + System.out.println("Connection established!"); + try { + inputStream = new ObjectInputStream(socket.getInputStream()); + outputStream = new ObjectOutputStream(socket.getOutputStream()); + while(true) { + String command = (String)inputStream.readObject(); + if ("exit".equals(command)) { + System.exit(0); + } + if ("getProperty".equals(command)) { + String property = (String)inputStream.readObject(); + outputStream.writeObject(Environment.getEnvironment().getProperty(property)); + outputStream.writeObject("OK"); + } + if ("makeAnOperation".equals(command)) { + String method = (String)inputStream.readObject(); + Object[] params = (Object[])inputStream.readObject(); + Class[] paramClasses = (Class[])inputStream.readObject(); + Object result = makeAnOperationLocally(method, params, + paramClasses); + if (result instanceof BufferedImage) { + outputStream.writeObject("image"); + BufferedImage image = BufferedImage.class.cast(result); + new PNGEncoder(outputStream, PNGEncoder.COLOR_MODE) + .encode(image, false); + } else { + outputStream.writeObject("OK"); + outputStream.writeObject(result); + } + } + } + } catch (ClassNotFoundException ex) { + throw new JemmyException("Socket communication with other " + + "JVM failed", ex); + } catch (IOException ex) { + Logger.getLogger(RobotExecutor.class.getName()) + .log(Level.SEVERE, null, ex); + } finally { + if (socket != null) { + try { + socket.close(); + } catch (IOException ex) { + Logger.getLogger(RobotExecutor.class.getName()).log( + Level.SEVERE, "Exception during socket closing", ex); + } + } + if (sc != null) { + try { + sc.close(); + } catch (IOException ex) { + Logger.getLogger(RobotExecutor.class.getName()).log( + Level.SEVERE, "Exception during server socket " + + "closing", ex); + } + } + } + } + } + + private void initRobot() { + // need to init Robot in dispatch thread because it hangs on Linux + // (see http://www.netbeans.org/issues/show_bug.cgi?id=37476) + if (EventQueue.isDispatchThread()) { + doInitRobot(); + } else { + try { + EventQueue.invokeAndWait(new Runnable() { + + public void run() { + doInitRobot(); + } + }); + } catch (InterruptedException ex) { + throw new JemmyException("Failed to initialize robot", ex); + } catch (InvocationTargetException ex) { + throw new JemmyException("Failed to initialize robot", ex); + } + } + } + + private void doInitRobot() { + try { + ClassReference robotClassReverence = new ClassReference("java.awt.Robot"); + robotReference = new ClassReference(robotClassReverence.newInstance(null, null)); + if (awtMap == null) { + awtMap = new AWTMap(); + } + setAutoDelay(autoDelay); + ready = true; + } catch (InvocationTargetException e) { + throw (new JemmyException("Exception during java.awt.Robot accessing", e)); + } catch (IllegalStateException e) { + throw (new JemmyException("Exception during java.awt.Robot accessing", e)); + } catch (NoSuchMethodException e) { + throw (new JemmyException("Exception during java.awt.Robot accessing", e)); + } catch (IllegalAccessException e) { + throw (new JemmyException("Exception during java.awt.Robot accessing", e)); + } catch (ClassNotFoundException e) { + throw (new JemmyException("Exception during java.awt.Robot accessing", e)); + } catch (InstantiationException e) { + throw (new JemmyException("Exception during java.awt.Robot accessing", e)); + } + } + + /** + * Calls java.awt.Robot.waitForIdle() method. + */ + protected void synchronizeRobot() { + ensureInited(); + if (!runInOtherJVM) { + // TODO: It looks like this method is rudimentary + if (!EventQueue.isDispatchThread()) { + if (robotReference == null) { + initRobot(); + } + try { + robotReference.invokeMethod("waitForIdle", null, null); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + public void setAutoDelay(Timeout autoDelay) { + this.autoDelay = autoDelay; + if (ready) { + makeAnOperation("setAutoDelay", new Object[]{new Integer((int) ((autoDelay != null) ? autoDelay.getValue() : 0))}, new Class[]{Integer.TYPE}); + } + } + + public boolean isRunInOtherJVM() { + ensureInited(); + return runInOtherJVM; + } + + public void setRunInOtherJVM(boolean runInOtherJVM) { + if (inited && this.runInOtherJVM && this.connectionEstablished && !runInOtherJVM) { + shutdownConnection(); + } + this.runInOtherJVM = runInOtherJVM; + inited = true; + ready = false; + } + + private void shutdownConnection() { + try { + outputStream.writeObject("exit"); + socket.close(); + connectionEstablished = false; + } catch (IOException ex) { + throw new JemmyException("Failed to shutdown connection", ex); + } + } +}