/Users/lyon/j4p/src/javagroup/process/StandardProcessManager.java
|
1 /*
2 * Copyright (C) 1997, 1988 Luke Gorrie
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
17 * MA 02139, USA.
18 */
19
20 package javagroup.process;
21
22 import javagroup.util.*;
23
24 import java.net.URL;
25 import java.util.Enumeration;
26 import java.util.Hashtable;
27 import java.util.StringTokenizer;
28 import java.util.Vector;
29
30 /**
31 * An implementation of the ProcessManager interface. Further documentation
32 * to come, please refer to ProcessManager for usage information.
33 *
34 * @author Luke Gorrie
35 * @version $Id: StandardProcessManager.java,v 1.4 1999/01/05 11:46:07 luke
36 * Exp $
37 */
38 public class StandardProcessManager
39 implements ProcessManager, ResourceDisposalListener {
40
41 /**
42 * default classpath. *
43 */
44 protected URL[] _classpath;
45
46 /**
47 * Hashtable of all processes. *
48 */
49 protected Hashtable _processes;
50
51 /**
52 * SecurityManager *
53 */
54 protected ProcessSecurityManager _securityManager;
55
56 /**
57 * Vector of registered ProcessEventListeners *
58 */
59 protected Vector _processEventListeners;
60
61 /**
62 * Garbage collector *
63 */
64 protected ProcessGarbageCollector _garbageCollector;
65
66 /**
67 * Per-process namespace manager *
68 */
69 protected ProcessNamespace _namespace;
70
71 /**
72 * Queue of garbage processes for the gc to remove. *
73 */
74 protected Vector _garbageQueue;
75
76 protected ThreadGroup _rootProcessGroup;
77
78 // next pid number
79 private long _nextpid;
80
81 /**
82 * Constructs a ProcessManager with no default classpath. This should
83 * not be invoked directly, instances are obtained via getInstance().
84 */
85 public StandardProcessManager() {
86
87 _rootProcessGroup = new ThreadGroup("ThreadGroup for JProcesses");
88
89 // initialize containers
90 _processes = new Hashtable();
91 _processEventListeners = new Vector();
92
93 _garbageQueue = new Vector();
94
95 // spawn a garbage collector
96 _garbageCollector = new ProcessGarbageCollector(this);
97
98 // create and install a namespace for per-process resources
99 _namespace = new ProcessNamespace(this);
100 Namespace.getNamespace().registerNamespace(_namespace);
101
102 // install security manager
103 _securityManager = new ProcessSecurityManager(this);
104 System.setSecurityManager(_securityManager);
105
106 }
107
108 /**
109 * Constructs a ProcessManager with a given default classpath. This
110 * should not be invoked directly, instances are obtained via
111 * getInstance().
112 */
113 public StandardProcessManager(URL[] classpath) {
114 this();
115
116 _classpath = classpath;
117
118 }
119
120 /**
121 * Return the next pid-number and increment the pid counter.
122 *
123 * @return A unique process-id.
124 */
125 protected synchronized long getNextPid() {
126 return _nextpid++;
127 }
128
129 /**
130 * Create a process.
131 *
132 * @param className The name of the target class.
133 * @return A process for the target.
134 */
135 public JProcess createProcess(String className)
136 throws ProcessCreationException {
137 return createProcess(className, new String[0], null);
138 }
139
140 /**
141 * Create a process with given arguments.
142 *
143 * @param className The name of the target class.
144 * @param args The arguments to pass to the main(String[])
145 * method.
146 * @return A process for the target.
147 */
148 public JProcess createProcess(String className, String[] args)
149 throws ProcessCreationException {
150 return createProcess(className, args, null);
151 }
152
153 /**
154 * Create a process with given args, and additional classpath(s).
155 *
156 * @param className The name of the target class.
157 * @param args The arguments to pass to the main(String[])
158 * method.
159 * @param classpath An array of URLs to search for classes in.
160 */
161 public synchronized JProcess createProcess(String className, String[] args,
162 URL[] classpath)
163 throws ProcessCreationException {
164
165 // ensure standard io redirectors are in place
166 StandardIO.ensureSystemWrappersInstalled();
167
168 if (classpath == null) {
169 classpath = _classpath;
170 } else {
171 classpath = mergeWithDefaultClasspath(classpath);
172 }
173
174 // instantiate process
175 StandardJProcess process =
176 new StandardJProcess(className,
177 getNextPid(),
178 args,
179 _rootProcessGroup,
180 classpath);
181
182 // create a token resource and bind it to the process.
183 // when the resource is released, it will indicate that the process
184 // is dead and it can be garbage collected.
185 ProcessLifeToken token = new ProcessLifeToken(process);
186 token.addDisposalListener(this);
187 process.registerResource(token);
188
189 // inform listeners that a process has been created
190 fireProcessCreationEvent(process);
191
192 // register process in process table
193 _processes.put(new Long(process.getPid()), process);
194
195 // create an empty namespace for the process
196 _namespace.registerNamespaceForProcess(new Namespace(), process);
197
198 return process;
199
200 }
201
202 /**
203 * Creates a process from the information in a given String of the
204 * format: "<classname> <arg>*".
205 *
206 * @param string The string to decode process info from.
207 * @return The created JProcess.
208 */
209 public JProcess createProcessFromString(String string)
210 throws ProcessCreationException {
211
212 return createProcessFromString(string, null);
213
214 }
215
216 /**
217 * Creates a process from the information in a given String of the
218 * format: "[-eclasspath url[,url]*] <classname> <arg>*".
219 *
220 * @param string The string to decode process info from.
221 * @param classpath Classpaths to search in addition to defaults.
222 * @return The created JProcess.
223 */
224 public JProcess createProcessFromString(String string,
225 URL[] classpath)
226 throws ProcessCreationException {
227
228 // check that a classname can be extracted
229 StringTokenizer tok = new StringTokenizer(string, " ");
230 if (tok.countTokens() < 1)
231 throw new ProcessCreationException(
232 "Invalid parameter string.");
233
234 // class name
235 String class_name = tok.nextToken();
236
237 if (class_name.equals("-eclasspath")) {
238 if (!tok.hasMoreTokens()) {
239 throw new ProcessCreationException(
240 "Must specify a classpath with -eclasspath option");
241 }
242 URL[] arg_classpath = URLClassLoader.decodePathString(
243 tok.nextToken());
244 if (classpath == null || classpath.length == 0) {
245 classpath = arg_classpath;
246 } else if (arg_classpath != null || arg_classpath.length != 0) {
247 int combined_length = classpath.length +
248 arg_classpath.length;
249 URL[] combined_classpath = new URL[combined_length];
250 System.arraycopy(arg_classpath,
251 0,
252 combined_classpath,
253 0,
254 arg_classpath.length);
255 System.arraycopy(classpath, 0, combined_classpath,
256 arg_classpath.length, combined_classpath.length);
257 classpath = combined_classpath;
258 }
259 if (!tok.hasMoreTokens()) {
260 throw new ProcessCreationException(
261 "Must specify a classname");
262 }
263 class_name = tok.nextToken();
264 }
265
266 // read args from string
267 String[] args = new String[tok.countTokens()];
268 for (int i = 0; i < args.length; i++)
269 args[i] = tok.nextToken();
270
271
272 // create and return process
273 return createProcess(class_name, args, classpath);
274
275 }
276
277 /**
278 * Get the process that a threadgroup belongs to.
279 *
280 * @param group The ThreadGroup to attribute a process to.
281 * @return The process owning the group, or null if none found.
282 */
283 public JProcess getProcessFor(ThreadGroup group) {
284
285 // enumerate all registered processes
286 Enumeration processes = _processes.elements();
287
288 JProcess match = null;
289
290 // cycle until a match is found, or all processes are tested
291 while ((match == null) && (processes.hasMoreElements())) {
292 JProcess process = (JProcess) processes.nextElement();
293 // get threadgroup from process
294 ThreadGroup process_group = process.getThreadGroup();
295 // if the process' group is an ancestor of the group being checked,
296 // make match
297 if (process_group.parentOf(group))
298 match = process;
299 }
300
301 // return matched process, or null if none were found
302 return match;
303
304 }
305
306 /**
307 * Get the process whose target class was loaded by a given
308 * ClassLoader.
309 *
310 * @param loader The classloader to check for.
311 * @return The process owning the loader, or null if none found.
312 */
313 public JProcess getProcessFor(ClassLoader loader) {
314
315 // enumerate all registered processes
316 Enumeration processes = _processes.elements();
317
318 JProcess match = null;
319
320 // cycle until a match if found, or all processes are tested
321 while ((match == null) && (processes.hasMoreElements())) {
322 JProcess process = (JProcess) processes.nextElement();
323 // get classloader from process
324 ClassLoader process_loader = process.getClassLoader();
325 // if both classloader variables reference the same instance,
326 // make a match
327 if (loader == process_loader)
328 match = process;
329 }
330
331 // return mached process, or null if none found
332 return match;
333
334 }
335
336 protected URL[] mergeWithDefaultClasspath(URL[] classpath) {
337
338 // check if either is blank
339 if (_classpath == null) return classpath;
340 if (classpath == null) return _classpath;
341
342 URL[] result = new URL[classpath.length + _classpath.length];
343 for (int i = 0; i < classpath.length; i++) {
344 result[i] = classpath[i];
345 }
346 for (int i = 0; i < _classpath.length; i++) {
347 result[i + classpath.length] = _classpath[i];
348 }
349 return result;
350 }
351
352 /**
353 * Return the current process.
354 *
355 * @return The process making the invocation, or null if none can be
356 * determined.
357 */
358 public JProcess getCurrentProcess() {
359 // get security manager to determine current process and return it
360 return _securityManager.getCurrentProcess();
361 }
362
363 /**
364 * Get a process by process-id.
365 *
366 * @param pid The id-number of the process.
367 * @return The process, or null if no match.
368 */
369 public JProcess getProcess(long pid) {
370 return (JProcess) _processes.get(new Long(pid));
371 }
372
373 /**
374 * Get an enumeration of all processes.
375 *
376 * @return Enumeration of all processes.
377 */
378 public Enumeration getProcesses() {
379 return _processes.elements();
380 }
381
382 /**
383 * Kill a process by pid.
384 *
385 * @param pid The process-id to kill.
386 */
387 public synchronized void kill(long pid) {
388
389 // get process by pid
390
391 // KIRR: in my opinion, remove is not needed as it will be invoked by the listener
392 JProcess process = (JProcess) _processes.remove(new Long(pid));
393 if (process != null) {
394 // queue process for garbage collection
395 _garbageQueue.addElement(process);
396 // alert the garbage collector
397 _garbageCollector.wakeUp();
398 // no longer killed here. the process garbage collector is used to
399 // perform the kill because it runs on a safe thread
400 //process.kill();
401 }
402 }
403
404 /* Event stuff - self-explanatory
405 */
406
407 public void addProcessEventListener(ProcessEventListener listener) {
408 _processEventListeners.addElement(listener);
409 }
410
411 public void removeProcessEventListener(ProcessEventListener listener) {
412 _processEventListeners.removeElement(listener);
413 }
414
415 protected void fireProcessDestructionEvent(JProcess process) {
416 synchronized (_processEventListeners) {
417 for (int i = 0; i < _processEventListeners.size(); i++) {
418 ProcessEventListener listener =
419 (ProcessEventListener) _processEventListeners.elementAt(
420 i);
421 listener.processDestroyed(process);
422 }
423 }
424 }
425
426 protected void fireProcessCreationEvent(JProcess process) {
427 synchronized (_processEventListeners) {
428 for (int i = 0; i < _processEventListeners.size(); i++) {
429 ProcessEventListener listener =
430 (ProcessEventListener) _processEventListeners.elementAt(
431 i);
432 listener.processCreated(process);
433 }
434 }
435 }
436
437 /**
438 * For ResourceDisposalListener interface. *
439 */
440 public void resourceDisposed(Resource resource) {
441
442 if (resource instanceof ProcessLifeToken) {
443 JProcess process = ((ProcessLifeToken) resource).getProcess();
444 kill(process.getPid());
445 }
446
447 }
448
449 /**
450 * Perform garbage collection. Usually invoked by the garbage
451 * collector.
452 */
453 public synchronized void doGarbageCollect() {
454
455 // restore the standard-io redirectors if someone's tampered
456 // with them
457 StandardIO.ensureSystemWrappersInstalled();
458
459 Enumeration garbage = _garbageQueue.elements();
460 while (garbage.hasMoreElements()) {
461
462 StandardJProcess process = (StandardJProcess) garbage.nextElement();
463 _namespace.removeNamespaceForProcess(process);
464 fireProcessDestructionEvent(process);
465
466 }
467
468 _garbageQueue.removeAllElements();
469
470 Enumeration processes = _processes.elements();
471 while (processes.hasMoreElements()) {
472
473 StandardJProcess process = (StandardJProcess) processes.nextElement();
474 process.tryGarbageCollect();
475
476 }
477
478 System.gc();
479
480 }
481
482 /**
483 * Get the Namespace object for the given process.
484 *
485 * @param process The process to get Namespace for.
486 * @return The appropriate namespace.
487 */
488 public Namespace getNamespaceForProcess(JProcess process) {
489 return _namespace.getNamespaceForProcess(process);
490 }
491
492 /**
493 * Set the streams to be used when the Process asks for standard io.
494 *
495 * @param process The process to set for.
496 * @param stdio The set of IO streams to use as standard io.
497 */
498 public void setStandardIOForProcess(JProcess process,
499 StandardIO stdio) {
500 Namespace namespace = getNamespaceForProcess(process);
501 namespace.registerInstanceForClass(StandardIO.class, stdio);
502 }
503
504 /**
505 * Get the streams a process uses for standard io
506 *
507 * @param process The process to get streams for.
508 * @return The set of streams being used as standard io.
509 */
510 public StandardIO getStandardIOForProcess(JProcess process) {
511 Namespace namespace = getNamespaceForProcess(process);
512 return (StandardIO) namespace.getInstanceForClass(
513 StandardIO.class);
514 }
515
516 }
517
518
519