KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > debug > core > model > RuntimeProcess


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.debug.core.model;
12
13
14 import java.util.HashMap JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.Map JavaDoc;
17
18 import org.eclipse.core.runtime.IStatus;
19 import org.eclipse.core.runtime.PlatformObject;
20 import org.eclipse.core.runtime.Status;
21 import org.eclipse.debug.core.DebugEvent;
22 import org.eclipse.debug.core.DebugException;
23 import org.eclipse.debug.core.DebugPlugin;
24 import org.eclipse.debug.core.ILaunch;
25 import org.eclipse.debug.core.ILaunchConfiguration;
26 import org.eclipse.debug.internal.core.DebugCoreMessages;
27 import org.eclipse.debug.internal.core.NullStreamsProxy;
28 import org.eclipse.debug.internal.core.StreamsProxy;
29
30
31 /**
32  * Standard implementation of an <code>IProcess</code> that wrappers a system
33  * process (<code>java.lang.Process</code>).
34  * <p>
35  * Clients may subclass this class. Clients that need to replace the implementation
36  * of a streams proxy associated with an <code>IProcess</code> should subclass this
37  * class. Generally clients should not instantiate this class directly, but should
38  * instead call <code>DebugPlugin.newProcess(...)</code>, which can delegate to an
39  * <code>IProcessFactory</code> if one is referenced by the associated launch configuration.
40  * </p>
41  * @see org.eclipse.debug.core.model.IProcess
42  * @see org.eclipse.debug.core.IProcessFactory
43  * @since 3.0
44  */

45 public class RuntimeProcess extends PlatformObject implements IProcess {
46
47     private static final int MAX_WAIT_FOR_DEATH_ATTEMPTS = 10;
48     private static final int TIME_TO_WAIT_FOR_THREAD_DEATH = 500; // ms
49

50     /**
51      * The launch this process is contained in
52      */

53     private ILaunch fLaunch;
54     
55     /**
56      * The system process represented by this <code>IProcess</code>
57      */

58     private Process JavaDoc fProcess;
59     
60     /**
61      * This process's exit value
62      */

63     private int fExitValue;
64     
65     /**
66      * The monitor which listens for this runtime process' system process
67      * to terminate.
68      */

69     private ProcessMonitorThread fMonitor;
70     
71     /**
72      * The streams proxy for this process
73      */

74     private IStreamsProxy fStreamsProxy;
75
76     /**
77      * The name of the process
78      */

79     private String JavaDoc fName;
80
81     /**
82      * Whether this process has been terminated
83      */

84     private boolean fTerminated;
85     
86     /**
87      * Table of client defined attributes
88      */

89     private Map JavaDoc fAttributes;
90     
91     /**
92      * Whether output from the process should be captured or swallowed
93      */

94     private boolean fCaptureOutput = true;
95
96     /**
97      * Constructs a RuntimeProcess on the given system process
98      * with the given name, adding this process to the given
99      * launch.
100      *
101      * @param launch the parent launch of this process
102      * @param process underlying system process
103      * @param name the label used for this process
104      * @param attributes map of attributes used to initialize the attributes
105      * of this process, or <code>null</code> if none
106      */

107     public RuntimeProcess(ILaunch launch, Process JavaDoc process, String JavaDoc name, Map JavaDoc attributes) {
108         setLaunch(launch);
109         initializeAttributes(attributes);
110         fProcess= process;
111         fName= name;
112         fTerminated= true;
113         try {
114             process.exitValue();
115         } catch (IllegalThreadStateException JavaDoc e) {
116             fTerminated= false;
117         }
118         
119         String JavaDoc captureOutput = launch.getAttribute(DebugPlugin.ATTR_CAPTURE_OUTPUT);
120         fCaptureOutput = !("false".equals(captureOutput)); //$NON-NLS-1$
121

122         fStreamsProxy= createStreamsProxy();
123         fMonitor = new ProcessMonitorThread(this);
124         fMonitor.start();
125         launch.addProcess(this);
126         fireCreationEvent();
127     }
128
129     /**
130      * Initialize the attributes of this process to those in the given map.
131      *
132      * @param attributes attribute map or <code>null</code> if none
133      */

134     private void initializeAttributes(Map JavaDoc attributes) {
135         if (attributes != null) {
136             Iterator JavaDoc keys = attributes.keySet().iterator();
137             while (keys.hasNext()) {
138                 String JavaDoc key = (String JavaDoc)keys.next();
139                 setAttribute(key, (String JavaDoc)attributes.get(key));
140             }
141         }
142     }
143
144     /**
145      * @see ITerminate#canTerminate()
146      */

147     public boolean canTerminate() {
148         return !fTerminated;
149     }
150
151     /**
152      * @see IProcess#getLabel()
153      */

154     public String JavaDoc getLabel() {
155         return fName;
156     }
157     
158     /**
159      * Sets the launch this process is contained in
160      *
161      * @param launch the launch this process is contained in
162      */

163     protected void setLaunch(ILaunch launch) {
164         fLaunch = launch;
165     }
166
167     /**
168      * @see IProcess#getLaunch()
169      */

170     public ILaunch getLaunch() {
171         return fLaunch;
172     }
173
174     /**
175      * Returns the underlying system process associated with this process.
176      *
177      * @return system process
178      */

179     protected Process JavaDoc getSystemProcess() {
180         return fProcess;
181     }
182
183     /**
184      * @see ITerminate#isTerminated()
185      */

186     public boolean isTerminated() {
187         return fTerminated;
188     }
189
190     /**
191      * @see ITerminate#terminate()
192      */

193     public void terminate() throws DebugException {
194         if (!isTerminated()) {
195             if (fStreamsProxy instanceof StreamsProxy) {
196                 ((StreamsProxy)fStreamsProxy).kill();
197             }
198             Process JavaDoc process = getSystemProcess();
199             if (process != null) {
200                 process.destroy();
201             }
202             int attempts = 0;
203             while (attempts < MAX_WAIT_FOR_DEATH_ATTEMPTS) {
204                 try {
205                     process = getSystemProcess();
206                     if (process != null) {
207                         fExitValue = process.exitValue(); // throws exception if process not exited
208
}
209                     return;
210                 } catch (IllegalThreadStateException JavaDoc ie) {
211                 }
212                 try {
213                     Thread.sleep(TIME_TO_WAIT_FOR_THREAD_DEATH);
214                 } catch (InterruptedException JavaDoc e) {
215                 }
216                 attempts++;
217             }
218             // clean-up
219
if (fMonitor != null) {
220                 fMonitor.killThread();
221                 fMonitor = null;
222             }
223             IStatus status = new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugException.TARGET_REQUEST_FAILED, DebugCoreMessages.RuntimeProcess_terminate_failed, null);
224             throw new DebugException(status);
225         }
226     }
227
228     /**
229      * Notification that the system process associated with this process
230      * has terminated.
231      */

232     protected void terminated() {
233         if (fStreamsProxy instanceof StreamsProxy) {
234             ((StreamsProxy)fStreamsProxy).close();
235         }
236         fTerminated= true;
237         try {
238             fExitValue = fProcess.exitValue();
239         } catch (IllegalThreadStateException JavaDoc ie) {
240         }
241         fProcess= null;
242         fireTerminateEvent();
243     }
244         
245     /**
246      * @see IProcess#getStreamsProxy()
247      */

248     public IStreamsProxy getStreamsProxy() {
249         if (!fCaptureOutput) {
250             return null;
251         }
252         return fStreamsProxy;
253     }
254     
255     /**
256      * Creates and returns the streams proxy associated with this process.
257      *
258      * @return streams proxy
259      */

260     protected IStreamsProxy createStreamsProxy() {
261         if (!fCaptureOutput) {
262             return new NullStreamsProxy(getSystemProcess());
263         }
264         String JavaDoc encoding = getLaunch().getAttribute(DebugPlugin.ATTR_CONSOLE_ENCODING);
265         return new StreamsProxy(getSystemProcess(), encoding);
266     }
267     
268     /**
269      * Fires a creation event.
270      */

271     protected void fireCreationEvent() {
272         fireEvent(new DebugEvent(this, DebugEvent.CREATE));
273     }
274
275     /**
276      * Fires the given debug event.
277      *
278      * @param event debug event to fire
279      */

280     protected void fireEvent(DebugEvent event) {
281         DebugPlugin manager= DebugPlugin.getDefault();
282         if (manager != null) {
283             manager.fireDebugEventSet(new DebugEvent[]{event});
284         }
285     }
286
287     /**
288      * Fires a terminate event.
289      */

290     protected void fireTerminateEvent() {
291         fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
292     }
293
294     /**
295      * Fires a change event.
296      */

297     protected void fireChangeEvent() {
298         fireEvent(new DebugEvent(this, DebugEvent.CHANGE));
299     }
300
301     /**
302      * @see IProcess#setAttribute(String, String)
303      */

304     public void setAttribute(String JavaDoc key, String JavaDoc value) {
305         if (fAttributes == null) {
306             fAttributes = new HashMap JavaDoc(5);
307         }
308         Object JavaDoc origVal = fAttributes.get(key);
309         if (origVal != null && origVal.equals(value)) {
310             return; //nothing changed.
311
}
312         
313         fAttributes.put(key, value);
314         fireChangeEvent();
315     }
316     
317     /**
318      * @see IProcess#getAttribute(String)
319      */

320     public String JavaDoc getAttribute(String JavaDoc key) {
321         if (fAttributes == null) {
322             return null;
323         }
324         return (String JavaDoc)fAttributes.get(key);
325     }
326     
327     /* (non-Javadoc)
328      * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
329      */

330     public Object JavaDoc getAdapter(Class JavaDoc adapter) {
331         if (adapter.equals(IProcess.class)) {
332             return this;
333         }
334         if (adapter.equals(IDebugTarget.class)) {
335             ILaunch launch = getLaunch();
336             IDebugTarget[] targets = launch.getDebugTargets();
337             for (int i = 0; i < targets.length; i++) {
338                 if (this.equals(targets[i].getProcess())) {
339                     return targets[i];
340                 }
341             }
342             return null;
343         }
344         if (adapter.equals(ILaunch.class)) {
345             return getLaunch();
346         }
347         //CONTEXTLAUNCHING
348
if(adapter.equals(ILaunchConfiguration.class)) {
349             return getLaunch().getLaunchConfiguration();
350         }
351         return super.getAdapter(adapter);
352     }
353     /**
354      * @see IProcess#getExitValue()
355      */

356     public int getExitValue() throws DebugException {
357         if (isTerminated()) {
358             return fExitValue;
359         }
360         throw new DebugException(new Status(IStatus.ERROR, DebugPlugin.getUniqueIdentifier(), DebugException.TARGET_REQUEST_FAILED, DebugCoreMessages.RuntimeProcess_Exit_value_not_available_until_process_terminates__1, null));
361     }
362     
363     /**
364      * Monitors a system process, waiting for it to terminate, and
365      * then notifies the associated runtime process.
366      */

367     class ProcessMonitorThread extends Thread JavaDoc {
368         
369         /**
370          * Whether the thread has been told to exit.
371          */

372         protected boolean fExit;
373         /**
374          * The underlying <code>java.lang.Process</code> being monitored.
375          */

376         protected Process JavaDoc fOSProcess;
377         /**
378          * The <code>IProcess</code> which will be informed when this
379          * monitor detects that the underlying process has terminated.
380          */

381         protected RuntimeProcess fRuntimeProcess;
382
383         /**
384          * The <code>Thread</code> which is monitoring the underlying process.
385          */

386         protected Thread JavaDoc fThread;
387         
388         /**
389          * A lock protecting access to <code>fThread</code>.
390          */

391         private final Object JavaDoc fThreadLock = new Object JavaDoc();
392
393         /**
394          * @see Thread#run()
395          */

396         public void run() {
397             synchronized (fThreadLock) {
398                 if (fExit) {
399                     return;
400                 }
401                 fThread = Thread.currentThread();
402             }
403             while (fOSProcess != null) {
404                 try {
405                     fOSProcess.waitFor();
406                 } catch (InterruptedException JavaDoc ie) {
407                     // clear interrupted state
408
Thread.interrupted();
409                 } finally {
410                     fOSProcess = null;
411                     fRuntimeProcess.terminated();
412                 }
413             }
414             fThread = null;
415         }
416
417         /**
418          * Creates a new process monitor and starts monitoring the process for
419          * termination.
420          *
421          * @param process process to monitor for termination
422          */

423         public ProcessMonitorThread(RuntimeProcess process) {
424             super(DebugCoreMessages.ProcessMonitorJob_0);
425             setDaemon(true);
426             fRuntimeProcess= process;
427             fOSProcess= process.getSystemProcess();
428         }
429
430         /**
431          * Kills the monitoring thread.
432          *
433          * This method is to be useful for dealing with the error
434          * case of an underlying process which has not informed this
435          * monitor of its termination.
436          */

437         protected void killThread() {
438             synchronized (fThreadLock) {
439                 if (fThread == null) {
440                     fExit = true;
441                 } else {
442                     fThread.interrupt();
443                 }
444             }
445         }
446     }
447 }
448
Popular Tags