/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