001: /*
002: * @(#)BackgroundProcess.java 1.0.0 11/17/2000 - 13:41:07
003: *
004: * Copyright (C) 2000,,2003 2002 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a
009: * copy of this software and associated documentation files (the "Software"),
010: * to deal in the Software without restriction, including without limitation
011: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
012: * and/or sell copies of the Software, and to permit persons to whom the
013: * Software is furnished to do so, subject to the following conditions:
014: *
015: * The above copyright notice and this permission notice shall be included in
016: * all copies or substantial portions of the Software.
017: *
018: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
021: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
023: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
024: * DEALINGS IN THE SOFTWARE.
025: */
026:
027: package net.sourceforge.groboutils.util.thread.v1;
028:
029: import java.io.File;
030: import java.io.IOException;
031: import java.io.InputStream;
032: import java.io.OutputStream;
033: import java.io.PipedInputStream;
034: import java.io.PipedOutputStream;
035:
036: import java.lang.reflect.Method;
037:
038: /**
039: * Creates and executes the given process. Ensures that all the output
040: * is properly read without overflowing or dead-locking the process.
041: * Outside of the streaming, this class has an identical API to that
042: * of {@link java.lang.Process}.
043: * <P>
044: * Creation of a background process begins at the creation of this object.
045: * <P>
046: * <H3>Changes for 0.9.1d</H3>
047: * <UL>
048: * <LI>Each IOThreadRunner which handles the background process's
049: * IO have been changed such that they now close the streams when
050: * an EOL is encountered, and have no delay between reads.
051: * </UL>
052: *
053: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
054: * @since June 4, 2000 (0.9.1d Alpha)
055: * @version $Date: 2003/02/10 22:52:48 $
056: */
057: public class BackgroundProcess {
058: private Process proc;
059: private OutputStream stdIn;
060: private PipedInputStream outReader;
061: private PipedOutputStream outWriter;
062: private PipedInputStream errReader;
063: private PipedOutputStream errWriter;
064:
065: private IOThreadRunner outThread;
066: private IOThreadRunner errThread;
067:
068: /**
069: *
070: *
071: * @see java.lang.Runtime#exec( String )
072: */
073: public BackgroundProcess(String command) throws IOException {
074: setupProcess(exec(command, null, null));
075: }
076:
077: /**
078: * @see java.lang.Runtime#exec( String[] )
079: */
080: public BackgroundProcess(String[] cmdarray) throws IOException {
081: setupProcess(exec(cmdarray, null, null));
082: }
083:
084: /**
085: * @see java.lang.Runtime#exec( String[], String[], File )
086: */
087: public BackgroundProcess(String[] cmdarray, String[] envp, File dir)
088: throws IOException {
089: setupProcess(exec(cmdarray, envp, dir));
090: }
091:
092: /**
093: * @see java.lang.Runtime#exec( String, String[], File )
094: */
095: public BackgroundProcess(String command, String[] envp, File dir)
096: throws IOException {
097: setupProcess(exec(command, envp, dir));
098: }
099:
100: /**
101: * @see java.lang.Runtime#exec( String[], String[] )
102: */
103: public BackgroundProcess(String[] cmdarray, String[] envp)
104: throws IOException {
105: setupProcess(exec(cmdarray, envp, null));
106: }
107:
108: /**
109: * @see java.lang.Runtime#exec( String, String[] )
110: */
111: public BackgroundProcess(String command, String[] envp)
112: throws IOException {
113: setupProcess(exec(command, envp, null));
114: }
115:
116: /**
117: * Initializes the background process with an existing process.
118: */
119: public BackgroundProcess(Process p) throws IOException {
120: if (p == null) {
121: throw new IllegalArgumentException("no null args");
122: }
123: setupProcess(p);
124: }
125:
126: /**
127: * Get the OutputStream that is sent to the StdIn of the process.
128: */
129: public OutputStream getStdIn() {
130: return this .stdIn;
131: }
132:
133: /**
134: * Get the InputStream that retrieves the data from the StdOut of the
135: * process.
136: */
137: public InputStream getStdOut() {
138: return this .outReader;
139: }
140:
141: /**
142: * Get the InputStream that retrieves the data from the StdErr of the
143: * process.
144: */
145: public InputStream getStdErr() {
146: return this .errReader;
147: }
148:
149: /**
150: * @see java.lang.Process#destroy()
151: */
152: public void destroy() {
153: this .proc.destroy();
154: }
155:
156: /**
157: * @see java.lang.Process#exitValue()
158: */
159: public int exitValue() {
160: return this .proc.exitValue();
161: }
162:
163: /**
164: * @see java.lang.Process#waitFor()
165: */
166: public int waitFor() throws InterruptedException {
167: return this .proc.waitFor();
168: }
169:
170: //-------------------------------------------------
171: // Protected methods
172:
173: /**
174: * Initalize the process for internal use.
175: */
176: protected void setupProcess(Process p) throws IOException {
177: this .proc = p;
178:
179: this .stdIn = p.getOutputStream();
180:
181: this .outReader = new PipedInputStream();
182: this .outWriter = new PipedOutputStream(this .outReader);
183:
184: this .errReader = new PipedInputStream();
185: this .errWriter = new PipedOutputStream(this .errReader);
186:
187: this .outThread = new IOThreadRunner(p.getInputStream(),
188: this .outWriter);
189: this .outThread.setCloseInputOnStop(true);
190: this .outThread.setCloseOutputOnStop(true);
191: this .outThread.getThread().setSleepTime(0);
192:
193: this .errThread = new IOThreadRunner(p.getErrorStream(),
194: this .errWriter);
195: this .errThread.setCloseInputOnStop(true);
196: this .errThread.setCloseOutputOnStop(true);
197: this .errThread.getThread().setSleepTime(0);
198:
199: this .outThread.start();
200: this .errThread.start();
201: }
202:
203: /**
204: * Invoke the correct Exec method for the given parameters.
205: */
206: protected Process exec(String cmd, String[] env, File dir)
207: throws IOException {
208: Runtime r = Runtime.getRuntime();
209: Process p;
210: if (dir == null) {
211: if (env == null) {
212: p = r.exec(cmd);
213: } else {
214: p = r.exec(cmd, env);
215: }
216: } else {
217: p = reflectExec(cmd, env, dir);
218: if (p == null) {
219: p = r.exec(cmd, setPWD(env, dir));
220: }
221: }
222: return p;
223: }
224:
225: /**
226: *
227: */
228: protected static Process exec(String[] cmd, String[] env, File dir)
229: throws IOException {
230: Runtime r = Runtime.getRuntime();
231: Process p;
232: if (dir == null) {
233: if (env == null) {
234: p = r.exec(cmd);
235: } else {
236: p = r.exec(cmd, env);
237: }
238: } else {
239: p = reflectExec(cmd, env, dir);
240: if (p == null) {
241: p = r.exec(cmd, setPWD(env, dir));
242: }
243: }
244: return p;
245: }
246:
247: /**
248: * Attempts to invoke the JDK 1.3 exec method with the given dir.
249: * <tt>cmd</tt> is either a String or String[] type.
250: *
251: * @return the generated Process. If the process is <tt>null</tt>,
252: * then another technique must be used to execute the command.
253: */
254: protected static Process reflectExec(Object cmd, String[] env,
255: File dir) throws IOException {
256: try {
257: Runtime r = Runtime.getRuntime();
258: Method m = r.getClass().getMethod(
259: "exec",
260: new Class[] { cmd.getClass(), String[].class,
261: File.class });
262: if (m == null) {
263: return null;
264: }
265: return (Process) m
266: .invoke(r, new Object[] { cmd, env, dir });
267: } catch (java.lang.reflect.InvocationTargetException ite) {
268: Throwable t = ite.getTargetException();
269: if (t instanceof IOException) {
270: throw (IOException) t;
271: }
272: if (t instanceof RuntimeException) {
273: throw (RuntimeException) t;
274: }
275: if (t instanceof Error) {
276: throw (Error) t;
277: }
278: // else swallow it
279: t.printStackTrace();
280: } catch (IllegalAccessException e) {
281: // gulp!
282: } catch (NoSuchMethodException e) {
283: // gulp!
284: } catch (IllegalArgumentException e) {
285: // gulp!
286: }
287: /* allow this to be thrown - it indicates a programatic error
288: catch (NullPointerException e)
289: {
290: }
291: */
292: /* allow this to be thrown
293: catch (ExceptionInInitializerError e)
294: {
295: }
296: */
297: return null;
298: }
299:
300: /**
301: * Don't change <tt>env</tt>!
302: */
303: protected static String[] setPWD(String env[], File dir) {
304: String tmp[];
305: String pwd = "PWD=" + dir.getAbsolutePath();
306: if (env == null) {
307: tmp = new String[] { pwd };
308: } else {
309: int len = env.length;
310: tmp = new String[len];
311: System.arraycopy(env, 0, tmp, 0, len);
312: String s[] = new String[len + 1];
313: for (int i = 0; i < len; ++i) {
314: if (tmp[i].startsWith("PWD=")) {
315: tmp[i] = pwd;
316: s = null;
317: break;
318: } else {
319: s[i] = tmp[i];
320: }
321: }
322: if (s != null) {
323: s[env.length] = pwd;
324: tmp = s;
325: }
326: }
327: return tmp;
328: }
329: }
|