001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.luni.internal.process;
019:
020: import java.io.BufferedInputStream;
021: import java.io.BufferedOutputStream;
022: import java.io.File;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.io.OutputStream;
026:
027: import org.apache.harmony.luni.util.Util;
028:
029: /**
030: * This class provides the {@link Runtime#exec(String[], String[], File)}
031: * implementation.
032: *
033: * Instances of class Process provide control of and access to platform
034: * processes. This is the concrete implementation of class java.lang.Process.
035: *
036: * @see java.lang.Runtime
037: */
038: public class SystemProcess extends Process {
039:
040: // Fill in the JNI id caches
041: private static native void oneTimeInitialization();
042:
043: static {
044: oneTimeInitialization();
045: }
046:
047: /**
048: * Constructs a process connected to an OS process.
049: *
050: * @param progArray the array containing the program to execute as well as
051: * any arguments to the program.
052: * @param envp the array containing the environment to start the new process
053: * in.
054: * @param directory the directory to start the process in, or null
055: *
056: * @return The new process.
057: *
058: * @throws IOException when a problem occurs
059: * @throws NullPointerException when progArray or envp are null, or contain
060: * a null element in the array.
061: */
062: public static Process create(String[] progArray, String[] envp,
063: final File directory) throws IOException {
064: final byte[][] progBytes, envBytes;
065: progBytes = new byte[progArray.length][];
066: for (int i = 0; i < progArray.length; i++) {
067: progBytes[i] = Util.getBytes(progArray[i]);
068: }
069: envBytes = new byte[envp.length][];
070: for (int i = 0; i < envp.length; i++) {
071: envBytes[i] = Util.getBytes(envp[i]);
072: }
073:
074: final SystemProcess p = new SystemProcess();
075:
076: p.lock = new Lock();
077:
078: Runnable waitingThread = new Runnable() {
079: public void run() {
080: long[] procVals = null;
081: try {
082: procVals = createImpl(p, progBytes, envBytes,
083: directory == null ? null : Util
084: .getBytes(directory.getPath()));
085: } catch (Throwable e) {
086: /* Creation errors need to be passed to the user thread. */
087: synchronized (p.lock) {
088: p.exception = e;
089: p.waiterStarted = true;
090: p.lock.notifyAll();
091: }
092: return;
093: }
094: p.handle = procVals[0];
095: p.in = new BufferedOutputStream(
096: new ProcessOutputStream(procVals[1]));
097: p.out = new BufferedInputStream(new ProcessInputStream(
098: procVals[2]));
099: p.err = new BufferedInputStream(new ProcessInputStream(
100: procVals[3]));
101:
102: synchronized (p.lock) {
103: p.waiterStarted = true;
104: p.lock.notifyAll();
105: }
106:
107: p.exitCode = p.waitForCompletionImpl();
108: synchronized (p.lock) {
109: p.closeImpl();
110: p.handle = -1;
111: }
112: p.exitCodeAvailable = true;
113: synchronized (p.lock) {
114: try {
115: p.in.close();
116: } catch (IOException e) {
117: }
118: p.lock.notifyAll();
119: }
120: }
121: };
122: Thread wait = new Thread(waitingThread);
123: wait.setDaemon(true);
124: wait.start();
125:
126: try {
127: synchronized (p.lock) {
128: while (!p.waiterStarted) {
129: p.lock.wait();
130: }
131: if (p.exception != null) {
132: /* Re-throw exception that originated in the helper thread */
133: p.exception.fillInStackTrace();
134: if (p.exception instanceof IOException) {
135: throw (IOException) p.exception;
136: } else if (p.exception instanceof Error) {
137: throw (Error) p.exception;
138: } else {
139: throw (RuntimeException) p.exception;
140: }
141: }
142: }
143: } catch (InterruptedException e) {
144: throw new InternalError();
145: }
146:
147: return p;
148: }
149:
150: protected synchronized static native long[] createImpl(Process p,
151: byte[][] progArray, byte[][] envp, byte[] dir)
152: throws IOException;
153:
154: private InputStream err; // STDERR for the process
155:
156: private InputStream out; // STDOUT for the process
157:
158: private OutputStream in; // STDIN for the process
159:
160: private long handle = -1; // Handle to OS process struct
161:
162: /**
163: * When exitCodeAvailable == 1, exitCode has a meaning. When exitCode is
164: * available, it means that the underlying process had finished (for sure).
165: */
166: private boolean exitCodeAvailable;
167:
168: private int exitCode;
169:
170: private static class Lock {
171: }
172:
173: private Object lock;
174:
175: private boolean waiterStarted;
176:
177: private Throwable exception;
178:
179: /**
180: * Prevents this class from being instantiated outside of this class.
181: */
182: private SystemProcess() {
183: super ();
184: }
185:
186: /**
187: * Internal implementation of the code that stops the process associated
188: * with the receiver.
189: */
190: private native void destroyImpl();
191:
192: /**
193: * Internal implementation of the code that closes the handle.
194: */
195: native void closeImpl();
196:
197: /**
198: * Internal implementation of the code that waits for the process to
199: * complete.
200: *
201: * @return int The exit value of the process.
202: */
203: native int waitForCompletionImpl();
204:
205: @Override
206: public void destroy() {
207: synchronized (lock) {
208: if (handle != -1) {
209: destroyImpl();
210: }
211: }
212: }
213:
214: @Override
215: public int exitValue() {
216: synchronized (lock) {
217: if (!exitCodeAvailable) {
218: throw new IllegalThreadStateException();
219: }
220: return exitCode;
221: }
222: }
223:
224: @Override
225: public InputStream getErrorStream() {
226: return err;
227: }
228:
229: @Override
230: public InputStream getInputStream() {
231: return out;
232: }
233:
234: @Override
235: public OutputStream getOutputStream() {
236: return in;
237: }
238:
239: @Override
240: public int waitFor() throws InterruptedException {
241: synchronized (lock) {
242: /*
243: * if the exitCode is available, it means that the underlying OS
244: * process is already dead, so the exitCode is just returned without
245: * any other OS checks
246: */
247: while (!exitCodeAvailable) {
248: lock.wait();
249: }
250: return exitCode;
251: }
252: }
253: }
|