/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.vm; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * @author epr */ public class VmProcess extends Process { /** * Identifier of this process */ private final int id; /** * Last used process identifier */ private static int lastId = 1; /** * Root thread group for this process */ private final ThreadGroup threadGroup; /** * Exit code */ private int exitValue; /** * Is this process still running */ private boolean running; final String mainClassName; final String[] args; private InputStream in; private InputStream err; private OutputStream out; private static Process rootProcess; /** * Create a new process * * @param mainClassName * @param args * @param in * @param out * @param err */ public VmProcess(String mainClassName, String[] args, InputStream in, PrintStream out, PrintStream err) { synchronized (getClass()) { this.id = lastId++; } this.running = true; this.threadGroup = new ThreadGroup("Process-" + id); this.mainClassName = mainClassName; if (args == null) { this.args = new String[0]; } else { this.args = new String[args.length]; System.arraycopy(args, 0, this.args, 0, args.length); } if (System.in == null) { Unsafe.debug("Set System.in."); System.setIn(in); } if (System.out == null) { Unsafe.debug("Set System.out."); System.setOut(out); } if (System.err == null) { Unsafe.debug("Set System.err."); System.setErr(err); } final Thread mainThread = new Thread(threadGroup, new ProcessRunner()); mainThread.start(); } private VmProcess(ThreadGroup rootGroup) { synchronized (getClass()) { this.id = lastId++; } this.running = true; this.threadGroup = rootGroup; this.mainClassName = "system"; this.args = new String[0]; } /** * Create and run a new process in its own classloader. * * @param mainClassName * @param args * @param envp * @return The created process * @throws Exception */ public static Process createProcess(String mainClassName, String[] args, String[] envp) throws Exception { final ClassLoader cl = new VmProcessClassLoader(); final Class processClass = cl.loadClass(VmProcess.class.getName()); final Class[] argTypes = new Class[]{ String.class, String[].class, InputStream.class, PrintStream.class, PrintStream.class }; final Constructor cons = processClass.getConstructor(argTypes); final Object[] consArgs = new Object[]{ mainClassName, args, System.in, System.out, System.err }; final Process proc = (Process) cons.newInstance(consArgs); return proc; } /** * Get the root process * * @param group * @return the root process */ public static Process getRootProcess(ThreadGroup group) { if (rootProcess == null) { rootProcess = new VmProcess(group); } return rootProcess; } /** * @see java.lang.Process#destroy() */ public void destroy() { exit(1); } /** * @return The exit value * @throws IllegalThreadStateException * @see java.lang.Process#exitValue() */ public int exitValue() throws IllegalThreadStateException { return exitValue; } /** * @return The error stream * @see java.lang.Process#getErrorStream() */ public InputStream getErrorStream() { return err; } /** * @return The input stream * @see java.lang.Process#getInputStream() */ public InputStream getInputStream() { return in; } /** * @return The output stream * @see java.lang.Process#getOutputStream() */ public OutputStream getOutputStream() { return out; } /** * Stop this process * * @param exitValue */ protected synchronized void exit(int exitValue) { this.exitValue = exitValue; this.running = false; notifyAll(); } /** * @return The exit value * @throws InterruptedException * @see java.lang.Process#waitFor() */ public synchronized int waitFor() throws InterruptedException { while (running) { wait(); } return exitValue; } /** * Class used as new process thread. * * @author epr */ class ProcessRunner implements Runnable { /** * Run the process * * @see java.lang.Runnable#run() */ public void run() { try { final Class<?> mainClass = Class.forName(mainClassName); final Method mainMethod = mainClass.getMethod("main", new Class[]{String[].class}); try { mainMethod.invoke(null, new Object[]{args}); } catch (InvocationTargetException ex) { ex.getTargetException().printStackTrace(); } } catch (Exception ex) { ex.printStackTrace(); } } } }