/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