/Users/lyon/j4p/src/javagroup/process/StandardJProcess.java

1    /* 
2     * Copyright (C) 1997, 1998 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.Resource; 
23   import javagroup.util.ResourceDisposalListener; 
24   import javagroup.util.URLClassLoader; 
25    
26   import java.lang.reflect.InvocationTargetException; 
27   import java.lang.reflect.Method; 
28   import java.lang.reflect.Modifier; 
29   import java.net.URL; 
30   import java.util.Vector; 
31    
32   /** 
33    * An implementation of the JProcess interface. Should only be used by a 
34    * ProcessManager. 
35    * 
36    * @author Luke Gorrie 
37    * @version $Id: StandardJProcess.java,v 1.4 1999/01/05 11:45:07 luke Exp 
38    *          $ 
39    */ 
40   public class StandardJProcess implements Runnable, 
41                                            JProcess, 
42                                            ResourceDisposalListener { 
43       /** 
44        * Unique process-id 
45        */ 
46       protected long _pid; 
47       /** 
48        * Process name 
49        */ 
50       protected String _name; 
51       /** 
52        * Classloader used to load target class 
53        */ 
54       protected URLClassLoader _classLoader; 
55       /** 
56        * Threadgroup for threads in this process 
57        */ 
58       protected ThreadGroup _processThreadGroup; 
59       /** 
60        * Resources locked by this process. * 
61        */ 
62       protected Vector _lockedResources; 
63       /** 
64        * Resources this process is locked to * 
65        */ 
66       protected Vector _resourcesLockedTo; 
67       /** 
68        * state of process * 
69        */ 
70       protected int _state; 
71       /** 
72        * exit code 
73        */ 
74       protected int _exitCode; 
75    
76       // "public static void main(String[])" method of target class 
77       private Method _targetMethod; 
78       // args to pass to main method 
79       private String[] _args; 
80       // thread used to invoke main method 
81       private Thread _mainThread; 
82    
83       /** 
84        * Creates a process.   This should invoked by a ProcessManager. 
85        * 
86        * @param className The fully-qualified name of the target class. 
87        * @param args      The arguments to pass to the target's 
88        *                  main(String[]) method. 
89        * @param pid       The process-id number. 
90        * @param classpath A list of URLs to check for classes. 
91        */ 
92       public StandardJProcess(String className, long pid, String[] args, 
93                               ThreadGroup parent, URL[] classpath) 
94               throws ProcessCreationException { 
95    
96           _name = className; 
97           _args = args; 
98    
99           setState(UNSTARTED); 
100   
101          _lockedResources = new Vector(); 
102          _resourcesLockedTo = new Vector(); 
103   
104          if (_args == null) 
105              _args = new String[0]; 
106   
107          // append the arguments to the process' name to make it more descriptive 
108          for (int i = 0; i < _args.length; i++) 
109              _name += " " + _args[i]; 
110   
111          _pid = pid; 
112   
113          // class factory method to create a classloader 
114          _classLoader = createClassLoader(); 
115   
116          // add classpath 
117          if (classpath != null) 
118              _classLoader.addClassPath(classpath); 
119   
120          // create a threadgroup, to be used exclusively by this process 
121          _processThreadGroup = createThreadGroup(parent); 
122   
123          // get a Method object for 'public static void main(String[])' 
124          try { 
125              setTargetClass(className); 
126          } catch (Exception e) { 
127              throw new ProcessCreationException(e.toString()); 
128          } 
129   
130      } 
131   
132      /** 
133       * Start the process running. * 
134       */ 
135      public void launch() { 
136          // a 'main' thread for the process 
137          _mainThread = new Thread(_processThreadGroup, this, 
138                  "Main thread for " + _name); 
139          // start process 
140          _mainThread.start(); 
141          setState(RUNNING); 
142          Thread.yield(); 
143   
144      } 
145   
146      public void addClassPath(URL[] classpath) { 
147          _classLoader.addClassPath(classpath); 
148      } 
149   
150      /** 
151       * Should not be called directly. * 
152       */ 
153      public void run() { 
154          try { 
155              // create argument array 
156              Object[] args_array = {_args}; 
157              // invoke target method, starting the target application 
158              _targetMethod.invoke(null, args_array); 
159          } catch (InvocationTargetException e) { 
160              //System.out.println("Invocation target exception: "+e); 
161              //e.getTargetException().printStackTrace(); 
162          } catch (Exception e) { 
163              e.printStackTrace(); 
164              //System.out.println(e); 
165              //throw new RuntimeException(e.getMessage()); 
166          } 
167      } 
168   
169      /** 
170       * Factory method for creating a classloader. 
171       * 
172       * @return A URLClassLoader for loading the target class. 
173       */ 
174      protected URLClassLoader createClassLoader() { 
175          return new URLClassLoader(); 
176      } 
177   
178      /** 
179       * Factory method for creating a process ThreadGroup. 
180       * 
181       * @return A ThreadGroup for threads in the process. 
182       */ 
183      protected ThreadGroup createThreadGroup(ThreadGroup parent) { 
184          ThreadGroup group = new ThreadGroup(parent, this.toString()); 
185          group.setMaxPriority(Thread.MAX_PRIORITY - 1); 
186   
187          return group; 
188      } 
189   
190      // get main method from target class 
191      private void setTargetClass(String className) 
192              throws ClassNotFoundException, NoSuchMethodException { 
193   
194          // load target class 
195          Class target_class = _classLoader.loadMainClass(className); 
196          // argument list ( String[] ) 
197          Class[] arg_types = {String[].class}; 
198          // get target method 
199          _targetMethod = target_class.getMethod("main", arg_types); 
200          // check that the method is static 
201          if (((_targetMethod.getModifiers() & Modifier.STATIC) == 0) 
202                  || (_targetMethod.getModifiers() & Modifier.PUBLIC) == 0) 
203              throw new NoSuchMethodException("main(String[]) method of " + 
204                      target_class.getName() + 
205                      " is not public and static."); 
206   
207      } 
208   
209      /** 
210       * Return the classloader used to load the target class 
211       * 
212       * @return The ClassLoader for this process. 
213       */ 
214      public ClassLoader getClassLoader() { 
215          return _classLoader; 
216      } 
217   
218      /** 
219       * Return the ThreadGroup for threads in this process. 
220       * 
221       * @return The ThreadGroup for this process. 
222       */ 
223      public ThreadGroup getThreadGroup() { 
224          return _processThreadGroup; 
225      } 
226   
227      /** 
228       * Register a resource. 
229       * 
230       * @param resource The resource to be registered/locked. 
231       */ 
232      public synchronized void registerResource(Resource resource) { 
233          if (!_lockedResources.contains(resource)) { 
234              _lockedResources.addElement(resource); 
235              resource.addLock(); 
236          } 
237      } 
238   
239      /** 
240       * Register and bind to a resource. 
241       * 
242       * @param resource The resource to lock and bind to. 
243       */ 
244      public synchronized void registerAndBindToResource(Resource resource) { 
245          registerResource(resource); 
246          bindToResource(resource); 
247      } 
248   
249      public synchronized void bindToResource(Resource resource) { 
250          if (!_resourcesLockedTo.contains(resource)) { 
251              _resourcesLockedTo.addElement(resource); 
252              resource.addDisposalListener(this); 
253          } 
254      } 
255   
256      /** 
257       * Release all resource locks. * 
258       */ 
259      public synchronized void releaseResources() { 
260          Resource[] resources = new Resource[_lockedResources.size()]; 
261          _lockedResources.copyInto(resources); 
262          for (int i = 0; i < resources.length; i++) { 
263              releaseResource(resources[i]); 
264              //resource.releaseLock(); 
265          } 
266      } 
267   
268      protected synchronized void releaseResource(Resource resource) { 
269          if (_lockedResources.contains(resource)) { 
270              try { 
271                  resource.releaseLock(); 
272              } catch (Exception e) { 
273              } 
274              try { 
275                  _lockedResources.removeElement(resource); 
276              } catch (Exception e) { 
277              } 
278          } 
279   
280          if (_resourcesLockedTo.contains(resource)) 
281              _resourcesLockedTo.removeElement(resource); 
282   
283      } 
284   
285      public void resourceDisposed(Resource resource) { 
286          releaseResource(resource); 
287      } 
288   
289   
290   
291      /** 
292       * This is the second stage in killing a process. Just doing stop on 
293       * its threadgroup does not have an immediate effect. For this reason, 
294       * the threads of the process are destroyed after a while, when the 
295       * garbage collector wakes up. 
296       */ 
297      private synchronized void kill_destroyThreadGroup() { 
298          if (_processThreadGroup != null) { 
299              _processThreadGroup.destroy(); 
300              _processThreadGroup = null; 
301          } 
302      } 
303   
304      /** 
305       * Checks if the process is finished running, and if so kills it off to 
306       * clean up the process table and allow the ClassLoader etc to be 
307       * garbage collected. 
308       */ 
309      public synchronized void tryGarbageCollect() { 
310          // if ths process has no threads, and is not locked to any resources, 
311          // kill (garbage collect) it. 
312   
313          if (_state != UNSTARTED && _state != DEAD) { 
314   
315              if (_resourcesLockedTo.isEmpty() && 
316                      _processThreadGroup.activeCount() <= 0) { 
317                  kill_destroyThreadGroup(); 
318              } 
319          } 
320      } 
321   
322      /** 
323       * Return the process-id. 
324       * 
325       * @return Process-id for this process. 
326       */ 
327      public long getPid() { 
328          return _pid; 
329      } 
330   
331      /** 
332       * Get the name of this process. 
333       * 
334       * @return A reasonably meaningful name. 
335       */ 
336      public String getName() { 
337          return _name; 
338      } 
339   
340      public int getState() { 
341          return _state; 
342      } 
343   
344      protected synchronized void setState(int state) { 
345          _state = state; 
346          notifyAll(); 
347      } 
348   
349      /** 
350       * Wait for the process to finish. 
351       */ 
352      public synchronized void waitFor() { 
353          while (getState() != DEAD) { 
354              try { 
355                  wait(); 
356              } catch (InterruptedException e) { 
357              } 
358          } 
359      } 
360   
361      public int getExitCode() { 
362          return _exitCode; 
363      } 
364   
365      public void setExitCode(int code) { 
366          _exitCode = code; 
367      } 
368   
369      public String toString() { 
370          return String.valueOf(_pid) + ": " + _name; 
371      } 
372   
373  } 
374   
375