aboutsummaryrefslogtreecommitdiff
path: root/test/java/awt/regtesthelpers/process/ProcessCommunicator.java
blob: 8177a2d2ab32ea36b29e8be32f32b901972098ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
 * Copyright (c) 2007, 2014, 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.
 *
 * 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 test.java.awt.regtesthelpers.process;

import java.io.*;

/**
 *  This class is created to solve interprocess communication problems.
 *  When you need to write a regression test which should verify inter jvm
 *  behavior such as DnD data transfer, Clipboard data transfer, focus
 *  transfer etc., you could use the next scenario:
 *
 *  1. Write an implementation for the parent JVM, using applet test.
 *  2. Write an implementation for the child JVM or native application, using
 *     main() function.
 *  3. Execute child process using  ProcessCommunicator.executeChildProcess()
 *     method.
 *  4. You can decide whether the test is passed on the basis of
 *     ProcessResults class data.
 *
 *  Note: The class is not thread safe. You should access its methods only from
 *        the same thread.
 */

public class ProcessCommunicator {

    private static final String javaHome = System.getProperty("java.home", "");
    private static final String javaPath = javaHome + File.separator + "bin" +
            File.separator + "java ";
    private static String command = "";
    private static volatile Process process;

    private ProcessCommunicator() {}

    /**
     * The same as {#link #executeChildProcess(Class,String)} except
     * the {@code classPathArgument} parameter. The class path
     * parameter is for the debug purposes
     *
     * @param classToExecute is passed to the child JVM
     * @param classPathArguments class path for the child JVM
     * @param args arguments that will be passed to the executed class
     * @return results of the executed {@code Process}
     */
    public static ProcessResults executeChildProcess(final Class classToExecute,
                           final String classPathArguments, final String [] args)
    {
        try {
            String command = buildCommand(classToExecute, classPathArguments, args);
            process = Runtime.getRuntime().exec(command);
            return doWaitFor(process);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Executes child {code Process}
     *
     * @param classToExecute class to be executed as a child java process
     * @param args args to be passed in to the child process
     * @return results of the executed {@code Process}
     */
    public static ProcessResults executeChildProcess(final Class classToExecute,
                           final String [] args)
    {
        return executeChildProcess(classToExecute, System.getProperty("java.class.path"), args);
    }

    /**
     * Waits for a process and return its results.
     * This is a workaround for {@code Process.waitFor()} never returning.
     *
     * @return results of the executed {@code Process}
     */
    public static ProcessResults doWaitFor(final Process p) {
        ProcessResults pres = new ProcessResults();

        final InputStream in;
        final InputStream err;

        try {
            in = p.getInputStream();
            err = p.getErrorStream();

            boolean finished = false;

            while (!finished) {
                try {
                    while (in.available() > 0) {
                        pres.appendToStdOut((char)in.read());
                    }
                    while (err.available() > 0) {
                        pres.appendToStdErr((char)err.read());
                    }
                    // Ask the process for its exitValue. If the process
                    // is not finished, an IllegalThreadStateException
                    // is thrown. If it is finished, we fall through and
                    // the variable finished is set to true.
                    pres.setExitValue(p.exitValue());
                    finished  = true;
                }
                catch (IllegalThreadStateException e) {
                    // Process is not finished yet;
                    // Sleep a little to save on CPU cycles
                    Thread.currentThread().sleep(500);
                }
            }
            if (in != null) in.close();
            if (err != null) err.close();
        }
        catch (Throwable e) {
            System.err.println("doWaitFor(): unexpected exception");
            e.printStackTrace();
        }
        return pres;
    }

    /**
     * Builds command on the basis of the passed class name,
     * class path and arguments.
     *
     * @param classToExecute with class will be executed in the new JVM
     * @param classPathArguments java class path (only for test purposes)
     * @param args arguments for the new application. This could be used
     *             to pass some information from the parent to child JVM.
     * @return command to execute the {@code Process}
     */
    private static String buildCommand(final Class classToExecute,
                         final String classPathArguments, final String [] args)
    {
        StringBuilder commandBuilder = new StringBuilder();
        commandBuilder.append(javaPath).append(" ");
        commandBuilder.append("-cp ").append(System.getProperty("test.classes", ".")).append(File.pathSeparatorChar);

        if (classPathArguments.trim().length() > 0) {
            commandBuilder.append(classPathArguments).append(" ");
        }

        commandBuilder.append(" ");
        commandBuilder.append(classToExecute.getName());
        for (String argument:args) {
            commandBuilder.append(" ").append(argument);
        }
        command = commandBuilder.toString();
        return command;
    }

    /**
     * Could be used for the debug purposes.
     *
     * @return command that was build to execute the child process
     */
    public static String getExecutionCommand () {
        return command;
    }

    /**
     * Terminates the process created by {@code executeChildProcess} methods.
     */
    public static void destroyProcess() {
        if (process != null) {
            if (process.isAlive()) {
                process.destroy();
            }
            process = null;
        }
    }
}