1
/*
2
 * $Id$
3
 *
4
 * Copyright (C) 2003-2009 JNode.org
5
 *
6
 * This library is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation; either version 2.1 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful, but 
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
14
 * License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this library; If not, write to the Free Software Foundation, Inc., 
18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
 */
20
 
21
package org.jnode.vm;
22
23
import java.io.InputStream;
24
import java.io.OutputStream;
25
import java.io.PrintStream;
26
import java.lang.reflect.Constructor;
27
import java.lang.reflect.InvocationTargetException;
28
import java.lang.reflect.Method;
29
30
/**
31
 * @author epr
32
 */
33
public class VmProcess extends Process {
34
35
    /**
36
     * Identifier of this process
37
     */
38
    private final int id;
39
    /**
40
     * Last used process identifier
41
     */
42
    private static int lastId = 1;
43
    /**
44
     * Root thread group for this process
45
     */
46
    private final ThreadGroup threadGroup;
47
    /**
48
     * Exit code
49
     */
50
    private int exitValue;
51
    /**
52
     * Is this process still running
53
     */
54
    private boolean running;
55
    final String mainClassName;
56
    final String[] args;
57
    private InputStream in;
58
    private InputStream err;
59
    private OutputStream out;
60
    private static Process rootProcess;
61
62
    /**
63
     * Create a new process
64
     *
65
     * @param mainClassName
66
     * @param args
67
     * @param in
68
     * @param out
69
     * @param err
70
     */
71
    public VmProcess(String mainClassName, String[] args, InputStream in, PrintStream out, PrintStream err) {
72
        synchronized (getClass()) {
73
            this.id = lastId++;
74
        }
75
        this.running = true;
76
        this.threadGroup = new ThreadGroup("Process-" + id);
77
        this.mainClassName = mainClassName;
78
        if (args == null) {
79
            this.args = new String[0];
80
        } else {
81
            this.args = new String[args.length];
82
            System.arraycopy(args, 0, this.args, 0, args.length);
83
        }
84
85
        if (System.in == null) {
86
            Unsafe.debug("Set System.in.");
87
            System.setIn(in);
88
        }
89
        if (System.out == null) {
90
            Unsafe.debug("Set System.out.");
91
            System.setOut(out);
92
        }
93
        if (System.err == null) {
94
            Unsafe.debug("Set System.err.");
95
            System.setErr(err);
96
        }
97
98
        final Thread mainThread = new Thread(threadGroup, new ProcessRunner());
99
        mainThread.start();
100
    }
101
102
    private VmProcess(ThreadGroup rootGroup) {
103
        synchronized (getClass()) {
104
            this.id = lastId++;
105
        }
106
        this.running = true;
107
        this.threadGroup = rootGroup;
108
        this.mainClassName = "system";
109
        this.args = new String[0];
110
    }
111
112
    /**
113
     * Create and run a new process in its own classloader.
114
     *
115
     * @param mainClassName
116
     * @param args
117
     * @param envp
118
     * @return The created process
119
     * @throws Exception
120
     */
121
    public static Process createProcess(String mainClassName, String[] args, String[] envp)
122
        throws Exception {
123
        final ClassLoader cl = new VmProcessClassLoader();
124
        final Class processClass = cl.loadClass(VmProcess.class.getName());
125
        final Class[] argTypes = new Class[]{
126
            String.class,
127
            String[].class,
128
            InputStream.class,
129
            PrintStream.class,
130
            PrintStream.class
131
        };
132
        final Constructor cons = processClass.getConstructor(argTypes);
133
        final Object[] consArgs = new Object[]{
134
            mainClassName,
135
            args,
136
            System.in,
137
            System.out,
138
            System.err
139
        };
140
        final Process proc = (Process) cons.newInstance(consArgs);
141
        return proc;
142
    }
143
144
    /**
145
     * Get the root process
146
     *
147
     * @param group
148
     * @return the root process
149
     */
150
    public static Process getRootProcess(ThreadGroup group) {
151
        if (rootProcess == null) {
152
            rootProcess = new VmProcess(group);
153
        }
154
        return rootProcess;
155
    }
156
157
    /**
158
     * @see java.lang.Process#destroy()
159
     */
160
    public void destroy() {
161
        exit(1);
162
    }
163
164
    /**
165
     * @return The exit value
166
     * @throws IllegalThreadStateException
167
     * @see java.lang.Process#exitValue()
168
     */
169
    public int exitValue() throws IllegalThreadStateException {
170
        return exitValue;
171
    }
172
173
    /**
174
     * @return The error stream
175
     * @see java.lang.Process#getErrorStream()
176
     */
177
    public InputStream getErrorStream() {
178
        return err;
179
    }
180
181
    /**
182
     * @return The input stream
183
     * @see java.lang.Process#getInputStream()
184
     */
185
    public InputStream getInputStream() {
186
        return in;
187
    }
188
189
    /**
190
     * @return The output stream
191
     * @see java.lang.Process#getOutputStream()
192
     */
193
    public OutputStream getOutputStream() {
194
        return out;
195
    }
196
197
    /**
198
     * Stop this process
199
     *
200
     * @param exitValue
201
     */
202
    protected synchronized void exit(int exitValue) {
203
        this.exitValue = exitValue;
204
        this.running = false;
205
        notifyAll();
206
    }
207
208
    /**
209
     * @return The exit value
210
     * @throws InterruptedException
211
     * @see java.lang.Process#waitFor()
212
     */
213
    public synchronized int waitFor() throws InterruptedException {
214
        while (running) {
215
            wait();
216
        }
217
        return exitValue;
218
    }
219
220
    /**
221
     * Class used as new process thread.
222
     *
223
     * @author epr
224
     */
225
    class ProcessRunner
226
        implements Runnable {
227
228
        /**
229
         * Run the process
230
         *
231
         * @see java.lang.Runnable#run()
232
         */
233
        public void run() {
234
            try {
235
                final Class<?> mainClass = Class.forName(mainClassName);
236
                final Method mainMethod = mainClass.getMethod("main", new Class[]{String[].class});
237
238
                try {
239
                    mainMethod.invoke(null, new Object[]{args});
240
                } catch (InvocationTargetException ex) {
241
                    ex.getTargetException().printStackTrace();
242
                }
243
            } catch (Exception ex) {
244
                ex.printStackTrace();
245
            }
246
        }
247
248
    }
249
250
}