KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > process > LinkedJavaProcess


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tc.process;
5
6 import com.tc.util.Assert;
7 import com.tc.util.runtime.Os;
8
9 import java.io.File JavaDoc;
10 import java.io.IOException JavaDoc;
11 import java.io.InputStream JavaDoc;
12 import java.io.OutputStream JavaDoc;
13 import java.util.ArrayList JavaDoc;
14 import java.util.Arrays JavaDoc;
15 import java.util.Collections JavaDoc;
16 import java.util.HashMap JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.LinkedList JavaDoc;
19 import java.util.List JavaDoc;
20 import java.util.Map JavaDoc;
21
22 /**
23  * A child Java process that uses a socket-based ping protocol to make sure that if the parent dies, the child dies a
24  * short time thereafter. Useful for avoiding 'zombie child processes' when writing tests, etc. — otherwise, if
25  * the parent process crashes or otherwise terminates abnormally, you'll get child processes accumulating until all hell
26  * breaks loose on the box.
27  * </p>
28  * <p>
29  * Although it can't actually be related through Java inheritance (because {@link Process}is a class, not an
30  * interface), this class behaves essentially identical to {@link Process}with two differences:
31  * <ul>
32  * <li>You instantiate this class directly, rather than getting an instance from {@link Runtime#exec}.</li>
33  * <li>The process doesn't start until you call {@link #start}.</li>
34  * </ul>
35  */

36 public class LinkedJavaProcess {
37
38   private final String JavaDoc mainClassName;
39   private String JavaDoc[] javaArguments;
40   private final String JavaDoc[] arguments;
41   private String JavaDoc[] environment;
42   private File JavaDoc directory;
43   private File JavaDoc javaExecutable;
44   private boolean isDSO = false;
45
46   private Process JavaDoc process;
47   private boolean running;
48   private final List JavaDoc copiers = Collections.synchronizedList(new ArrayList JavaDoc());
49
50   public LinkedJavaProcess(String JavaDoc mainClassName, String JavaDoc[] classArguments) {
51     Assert.assertNotBlank(mainClassName);
52
53     if (classArguments == null) classArguments = new String JavaDoc[0];
54
55     this.mainClassName = mainClassName;
56     this.javaArguments = null;
57     this.arguments = classArguments;
58     this.environment = null;
59     this.directory = null;
60     this.javaExecutable = null;
61     this.process = null;
62     this.running = false;
63   }
64
65   public LinkedJavaProcess(String JavaDoc mainClassName) {
66     this(mainClassName, null);
67   }
68
69   public void setJavaExecutable(File JavaDoc javaExecutable) {
70     Assert.assertNotNull(javaExecutable);
71
72     this.javaExecutable = javaExecutable;
73   }
74
75   public void setJavaArguments(String JavaDoc[] javaArguments) {
76     this.javaArguments = javaArguments;
77   }
78
79   public void setEnvironment(String JavaDoc[] environment) {
80     this.environment = environment;
81   }
82
83   public void setDirectory(File JavaDoc directory) {
84     this.directory = directory;
85   }
86
87   public void setDSOTarget(boolean isDSO) {
88     this.isDSO = isDSO;
89   }
90
91   public synchronized void destroy() {
92     if (!this.running) throw new IllegalStateException JavaDoc("This LinkedJavaProcess is not running.");
93     this.process.destroy();
94     this.running = false;
95   }
96
97   private synchronized void setJavaExecutableIfNecessary() throws IOException JavaDoc {
98     if (this.javaExecutable == null) {
99       File JavaDoc javaHome = new File JavaDoc(System.getProperty("java.home"));
100       File JavaDoc javaBin = new File JavaDoc(javaHome, "bin");
101       File JavaDoc javaPlain = new File JavaDoc(javaBin, "java");
102       File JavaDoc javaExe = new File JavaDoc(javaBin, "java.exe");
103
104       if (this.javaExecutable == null) {
105         if (javaPlain.exists() && javaPlain.isFile()) this.javaExecutable = javaPlain;
106       }
107
108       if (this.javaExecutable == null) {
109         if (javaExe.exists() && javaExe.isFile()) this.javaExecutable = javaExe;
110       }
111
112       if (this.javaExecutable == null) {
113         // formatting
114
throw new IOException JavaDoc("Can't find the Java binary; perhaps you need to set it yourself? Tried "
115                               + javaPlain.getAbsolutePath() + " and " + javaExe.getAbsolutePath());
116       }
117     }
118   }
119
120   public synchronized void start() throws IOException JavaDoc {
121     if (this.running) throw new IllegalStateException JavaDoc("This LinkedJavaProcess is already running.");
122
123     LinkedJavaProcessPollingAgent.startHeartBeatServer();
124     
125     List JavaDoc fullCommandList = new LinkedList JavaDoc();
126     List JavaDoc allJavaArguments = new ArrayList JavaDoc();
127
128     allJavaArguments.add("-Djava.class.path=" + System.getProperty("java.class.path"));
129     if (this.javaArguments != null) allJavaArguments.addAll(Arrays.asList(this.javaArguments));
130
131     setJavaExecutableIfNecessary();
132
133     int socketPort = LinkedJavaProcessPollingAgent.getChildProcessHeartbeatServerPort();
134
135     Map JavaDoc env = makeEnvMap(Arrays.asList(this.environment == null ? new String JavaDoc[] {} : this.environment));
136     fixupEnvironment(env);
137
138     fullCommandList.add(this.javaExecutable.getAbsolutePath());
139     fullCommandList.addAll(allJavaArguments);
140     fullCommandList.add(isDSO ? DSOLinkedJavaProcessStarter.class.getName() : LinkedJavaProcessStarter.class.getName());
141     fullCommandList.add(Integer.toString(socketPort));
142     fullCommandList.add(this.mainClassName);
143     if (this.arguments != null) fullCommandList.addAll(Arrays.asList(this.arguments));
144
145     String JavaDoc[] fullCommand = (String JavaDoc[]) fullCommandList.toArray(new String JavaDoc[fullCommandList.size()]);
146
147     this.process = Runtime.getRuntime().exec(fullCommand, makeEnv(env), this.directory);
148     this.running = true;
149   }
150
151   private Map JavaDoc makeEnvMap(List JavaDoc list) {
152     Map JavaDoc rv = new HashMap JavaDoc();
153
154     for (Iterator JavaDoc iter = list.iterator(); iter.hasNext();) {
155       String JavaDoc[] nameValue = ((String JavaDoc) iter.next()).split("=", 2);
156       rv.put(nameValue[0], nameValue[1]);
157     }
158
159     return rv;
160   }
161
162   private String JavaDoc[] makeEnv(Map JavaDoc env) {
163     int i = 0;
164     String JavaDoc[] rv = new String JavaDoc[env.size()];
165     for (Iterator JavaDoc iter = env.keySet().iterator(); iter.hasNext(); i++) {
166       String JavaDoc key = (String JavaDoc) iter.next();
167       rv[i] = key + "=" + env.get(key);
168     }
169     return rv;
170   }
171
172   private static void fixupEnvironment(Map JavaDoc env) {
173     if (Os.isWindows()) {
174       // A bunch of name lookup stuff will fail w/o setting SYSTEMROOT. Also, if
175
// you have apple's rendevous/bonjour
176
// client installed, it needs to be in the PATH such that dnssd.dll will
177
// be found when using DNS
178

179       if (!env.containsKey("SYSTEMROOT")) {
180         String JavaDoc root = Os.findWindowsSystemRoot();
181         if (root == null) { throw new RuntimeException JavaDoc("cannot find %SYSTEMROOT% in the environment"); }
182         env.put("SYSTEMROOT", root);
183       }
184
185       String JavaDoc crappleDirs = "C:\\Program Files\\Rendezvous\\" + File.pathSeparator + "C:\\Program Files\\Bonjour\\";
186
187       if (!env.containsKey("PATH")) {
188         env.put("PATH", crappleDirs);
189       } else {
190         String JavaDoc path = (String JavaDoc) env.get("PATH");
191         path = path + File.pathSeparator + crappleDirs;
192         env.put("PATH", path);
193       }
194     }
195   }
196
197   /**
198    * Java names these things a bit funny &mdash; this is the spawned process's <tt>stdout</tt>.
199    */

200   public synchronized InputStream JavaDoc getInputStream() {
201     if (!this.running) throw new IllegalStateException JavaDoc("This LinkedJavaProcess is not yet running.");
202     return this.process.getInputStream();
203   }
204
205   public InputStream JavaDoc STDOUT() {
206     return getInputStream();
207   }
208
209   public OutputStream JavaDoc STDIN() {
210     return getOutputStream();
211   }
212
213   public InputStream JavaDoc STDERR() {
214     return getErrorStream();
215   }
216
217   public void mergeSTDOUT() {
218     mergeStream(STDOUT(), System.out);
219   }
220
221   public void mergeSTDERR() {
222     mergeStream(STDERR(), System.err);
223   }
224
225   private void mergeStream(InputStream JavaDoc in, OutputStream JavaDoc out) {
226     StreamCopier copier = new StreamCopier(in, out);
227     copiers.add(copier);
228     copier.start();
229   }
230
231   /**
232    * This is the spawned process's <tt>stderr</tt>.
233    */

234   public synchronized InputStream JavaDoc getErrorStream() {
235     if (!this.running) throw new IllegalStateException JavaDoc("This LinkedJavaProcess is not yet running.");
236     return this.process.getErrorStream();
237   }
238
239   /**
240    * Java names these things a bit funny &mdash; this is the spawned process's <tt>stdin</tt>.
241    */

242   public synchronized OutputStream JavaDoc getOutputStream() {
243     if (!this.running) throw new IllegalStateException JavaDoc("This LinkedJavaProcess is not yet running.");
244     return this.process.getOutputStream();
245   }
246
247   public synchronized int exitValue() {
248     if (this.process == null) throw new IllegalStateException JavaDoc("This LinkedJavaProcess has not been started.");
249     int out = this.process.exitValue();
250     // Process.exitValue() throws an exception if not yet terminated, so we know
251
// it's terminated now.
252
this.running = false;
253     return out;
254   }
255
256   public int waitFor() throws InterruptedException JavaDoc {
257     Process JavaDoc theProcess = null;
258
259     synchronized (this) {
260       if (!this.running) throw new IllegalStateException JavaDoc("This LinkedJavaProcess is not running.");
261       theProcess = this.process;
262       Assert.assertNotNull(theProcess);
263     }
264
265     int exitCode = theProcess.waitFor();
266
267     for (Iterator JavaDoc i = copiers.iterator(); i.hasNext();) {
268       Thread JavaDoc t = (Thread JavaDoc) i.next();
269       t.join();
270       i.remove();
271     }
272
273     synchronized (this) {
274       this.running = false;
275     }
276
277     return exitCode;
278
279   }
280
281 }
Popular Tags