KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > launching > StandardVMDebugger


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.jdt.internal.launching;
12
13
14 import java.io.File JavaDoc;
15 import java.io.IOException JavaDoc;
16 import java.io.InterruptedIOException JavaDoc;
17 import java.util.ArrayList JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.List JavaDoc;
20 import java.util.Map JavaDoc;
21
22 import org.eclipse.core.runtime.CoreException;
23 import org.eclipse.core.runtime.IPath;
24 import org.eclipse.core.runtime.IProgressMonitor;
25 import org.eclipse.core.runtime.IStatus;
26 import org.eclipse.core.runtime.NullProgressMonitor;
27 import org.eclipse.core.runtime.Path;
28 import org.eclipse.core.runtime.Platform;
29 import org.eclipse.core.runtime.Status;
30 import org.eclipse.core.runtime.SubProgressMonitor;
31 import org.eclipse.debug.core.DebugPlugin;
32 import org.eclipse.debug.core.ILaunch;
33 import org.eclipse.debug.core.IStatusHandler;
34 import org.eclipse.debug.core.model.IDebugTarget;
35 import org.eclipse.debug.core.model.IProcess;
36 import org.eclipse.debug.core.model.IStreamsProxy;
37 import org.eclipse.jdi.Bootstrap;
38 import org.eclipse.jdt.debug.core.JDIDebugModel;
39 import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
40 import org.eclipse.jdt.launching.IVMInstall;
41 import org.eclipse.jdt.launching.JavaRuntime;
42 import org.eclipse.jdt.launching.SocketUtil;
43 import org.eclipse.jdt.launching.VMRunnerConfiguration;
44
45 import com.sun.jdi.VirtualMachine;
46 import com.sun.jdi.connect.Connector;
47 import com.sun.jdi.connect.IllegalConnectorArgumentsException;
48 import com.sun.jdi.connect.ListeningConnector;
49
50 /**
51  * A launcher for debugging Java main classes. Uses JDI to launch a vm in debug
52  * mode.
53  */

54 public class StandardVMDebugger extends StandardVMRunner {
55     
56     
57     /**
58      * @since 3.3 OSX environment variable specifying JRE to use
59      */

60     protected static final String JavaDoc JAVA_JVM_VERSION = "JAVA_JVM_VERSION"; //$NON-NLS-1$
61

62     /**
63      * Jre path segment descriptor
64      *
65      * String equals the word: <code>jre</code>
66      *
67      * @since 3.3.1
68      */

69     protected static final String JavaDoc JRE = "jre"; //$NON-NLS-1$
70

71     /**
72      * Bin path segment descriptor
73      *
74      * String equals the word: <code>bin</code>
75      *
76      * @since 3.3.1
77      */

78     protected static final String JavaDoc BIN = "bin"; //$NON-NLS-1$
79

80     /**
81      * Used to attach to a VM in a separate thread, to allow for cancellation
82      * and detect that the associated System process died before the connect
83      * occurred.
84      */

85     class ConnectRunnable implements Runnable JavaDoc {
86         
87         private VirtualMachine fVirtualMachine = null;
88         private ListeningConnector fConnector = null;
89         private Map JavaDoc fConnectionMap = null;
90         private Exception JavaDoc fException = null;
91         
92         /**
93          * Constructs a runnable to connect to a VM via the given connector
94          * with the given connection arguments.
95          *
96          * @param connector
97          * @param map
98          */

99         public ConnectRunnable(ListeningConnector connector, Map JavaDoc map) {
100             fConnector = connector;
101             fConnectionMap = map;
102         }
103         
104         public void run() {
105             try {
106                 fVirtualMachine = fConnector.accept(fConnectionMap);
107             } catch (IOException JavaDoc e) {
108                 fException = e;
109             } catch (IllegalConnectorArgumentsException e) {
110                 fException = e;
111             }
112         }
113         
114         /**
115          * Returns the VM that was attached to, or <code>null</code> if none.
116          *
117          * @return the VM that was attached to, or <code>null</code> if none
118          */

119         public VirtualMachine getVirtualMachine() {
120             return fVirtualMachine;
121         }
122         
123         /**
124          * Returns any exception that occurred while attaching, or <code>null</code>.
125          *
126          * @return IOException or IllegalConnectorArgumentsException
127          */

128         public Exception JavaDoc getException() {
129             return fException;
130         }
131     }
132
133     /**
134      * Creates a new launcher
135      */

136     public StandardVMDebugger(IVMInstall vmInstance) {
137         super(vmInstance);
138     }
139
140     /* (non-Javadoc)
141      * @see org.eclipse.jdt.launching.IVMRunner#run(org.eclipse.jdt.launching.VMRunnerConfiguration, org.eclipse.debug.core.ILaunch, org.eclipse.core.runtime.IProgressMonitor)
142      */

143     public void run(VMRunnerConfiguration config, ILaunch launch, IProgressMonitor monitor) throws CoreException {
144
145         if (monitor == null) {
146             monitor = new NullProgressMonitor();
147         }
148         
149         IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1);
150         subMonitor.beginTask(LaunchingMessages.StandardVMDebugger_Launching_VM____1, 4);
151         subMonitor.subTask(LaunchingMessages.StandardVMDebugger_Finding_free_socket____2);
152
153         int port= SocketUtil.findFreePort();
154         if (port == -1) {
155             abort(LaunchingMessages.StandardVMDebugger_Could_not_find_a_free_socket_for_the_debugger_1, null, IJavaLaunchConfigurationConstants.ERR_NO_SOCKET_AVAILABLE);
156         }
157         
158         subMonitor.worked(1);
159         
160         // check for cancellation
161
if (monitor.isCanceled()) {
162             return;
163         }
164         
165         subMonitor.subTask(LaunchingMessages.StandardVMDebugger_Constructing_command_line____3);
166                 
167         String JavaDoc program= constructProgramString(config);
168
169         List JavaDoc arguments= new ArrayList JavaDoc(12);
170
171         arguments.add(program);
172
173         // VM arguments are the first thing after the java program so that users can specify
174
// options like '-client' & '-server' which are required to be the first options
175
String JavaDoc[] allVMArgs = combineVmArgs(config, fVMInstance);
176         addArguments(allVMArgs, arguments);
177         addBootClassPathArguments(arguments, config);
178         
179         String JavaDoc[] cp= config.getClassPath();
180         if (cp.length > 0) {
181             arguments.add("-classpath"); //$NON-NLS-1$
182
arguments.add(convertClassPath(cp));
183         }
184         double version = getJavaVersion();
185         if (version < 1.5) {
186             arguments.add("-Xdebug"); //$NON-NLS-1$
187
arguments.add("-Xnoagent"); //$NON-NLS-1$
188
}
189         
190         //check if java 1.4 or greater
191
if (version < 1.4) {
192             arguments.add("-Djava.compiler=NONE"); //$NON-NLS-1$
193
}
194         if (version < 1.5) {
195             arguments.add("-Xrunjdwp:transport=dt_socket,suspend=y,address=localhost:" + port); //$NON-NLS-1$
196
} else {
197             arguments.add("-agentlib:jdwp=transport=dt_socket,suspend=y,address=localhost:" + port); //$NON-NLS-1$
198
}
199
200         arguments.add(config.getClassToLaunch());
201         addArguments(config.getProgramArguments(), arguments);
202         String JavaDoc[] cmdLine= new String JavaDoc[arguments.size()];
203         arguments.toArray(cmdLine);
204         
205         //With the newer VMs and no backwards compatibility we have to always prepend the current env path (only the runtime one)
206
//with a 'corrected' path that points to the location to load the debug dlls from, this location is of the standard JDK installation
207
//format: <jdk path>/jre/bin
208
String JavaDoc[] envp = prependJREPath(config.getEnvironment(), new Path(program));
209         
210         // check for cancellation
211
if (monitor.isCanceled()) {
212             return;
213         }
214         
215         subMonitor.worked(1);
216         subMonitor.subTask(LaunchingMessages.StandardVMDebugger_Starting_virtual_machine____4);
217
218         ListeningConnector connector= getConnector();
219         if (connector == null) {
220             abort(LaunchingMessages.StandardVMDebugger_Couldn__t_find_an_appropriate_debug_connector_2, null, IJavaLaunchConfigurationConstants.ERR_CONNECTOR_NOT_AVAILABLE);
221         }
222         Map JavaDoc map= connector.defaultArguments();
223         
224         specifyArguments(map, port);
225         Process JavaDoc p= null;
226         try {
227             try {
228                 // check for cancellation
229
if (monitor.isCanceled()) {
230                     return;
231                 }
232                 
233                 connector.startListening(map);
234                 
235                 File JavaDoc workingDir = getWorkingDir(config);
236                 p = exec(cmdLine, workingDir, envp);
237                 if (p == null) {
238                     return;
239                 }
240                 
241                 // check for cancellation
242
if (monitor.isCanceled()) {
243                     p.destroy();
244                     return;
245                 }
246                 
247                 IProcess process= newProcess(launch, p, renderProcessLabel(cmdLine), getDefaultProcessMap());
248                 process.setAttribute(IProcess.ATTR_CMDLINE, renderCommandLine(cmdLine));
249                 subMonitor.worked(1);
250                 subMonitor.subTask(LaunchingMessages.StandardVMDebugger_Establishing_debug_connection____5);
251                 boolean retry= false;
252                 do {
253                     try {
254                         
255                         ConnectRunnable runnable = new ConnectRunnable(connector, map);
256                         Thread JavaDoc connectThread = new Thread JavaDoc(runnable, "Listening Connector"); //$NON-NLS-1$
257
connectThread.setDaemon(true);
258                         connectThread.start();
259                         while (connectThread.isAlive()) {
260                             if (monitor.isCanceled()) {
261                                 try {
262                                     connector.stopListening(map);
263                                 } catch (IOException JavaDoc ioe) {
264                                     //expected
265
}
266                                 p.destroy();
267                                 return;
268                             }
269                             try {
270                                 p.exitValue();
271                                 // process has terminated - stop waiting for a connection
272
try {
273                                     connector.stopListening(map);
274                                 } catch (IOException JavaDoc e) {
275                                     // expected
276
}
277                                 checkErrorMessage(process);
278                             } catch (IllegalThreadStateException JavaDoc e) {
279                                 // expected while process is alive
280
}
281                             try {
282                                 Thread.sleep(100);
283                             } catch (InterruptedException JavaDoc e) {
284                             }
285                         }
286
287                         Exception JavaDoc ex = runnable.getException();
288                         if (ex instanceof IllegalConnectorArgumentsException) {
289                             throw (IllegalConnectorArgumentsException)ex;
290                         }
291                         if (ex instanceof InterruptedIOException JavaDoc) {
292                             throw (InterruptedIOException JavaDoc)ex;
293                         }
294                         if (ex instanceof IOException JavaDoc) {
295                             throw (IOException JavaDoc)ex;
296                         }
297                         
298                         VirtualMachine vm= runnable.getVirtualMachine();
299                         if (vm != null) {
300                             createDebugTarget(config, launch, port, process, vm);
301                             subMonitor.worked(1);
302                             subMonitor.done();
303                         }
304                         return;
305                     } catch (InterruptedIOException JavaDoc e) {
306                         checkErrorMessage(process);
307                         
308                         // timeout, consult status handler if there is one
309
IStatus status = new Status(IStatus.ERROR, LaunchingPlugin.getUniqueIdentifier(), IJavaLaunchConfigurationConstants.ERR_VM_CONNECT_TIMEOUT, "", e); //$NON-NLS-1$
310
IStatusHandler handler = DebugPlugin.getDefault().getStatusHandler(status);
311                         
312                         retry= false;
313                         if (handler == null) {
314                             // if there is no handler, throw the exception
315
throw new CoreException(status);
316                         }
317                         Object JavaDoc result = handler.handleStatus(status, this);
318                         if (result instanceof Boolean JavaDoc) {
319                             retry = ((Boolean JavaDoc)result).booleanValue();
320                         }
321                     }
322                 } while (retry);
323             } finally {
324                 connector.stopListening(map);
325             }
326         } catch (IOException JavaDoc e) {
327             abort(LaunchingMessages.StandardVMDebugger_Couldn__t_connect_to_VM_4, e, IJavaLaunchConfigurationConstants.ERR_CONNECTION_FAILED);
328         } catch (IllegalConnectorArgumentsException e) {
329             abort(LaunchingMessages.StandardVMDebugger_Couldn__t_connect_to_VM_5, e, IJavaLaunchConfigurationConstants.ERR_CONNECTION_FAILED);
330         }
331         if (p != null) {
332             p.destroy();
333         }
334     }
335
336     /**
337      * This method performs platform specific operations to modify the runtime path for JREs prior to launching.
338      * Nothing is written back to the original system path.
339      *
340      * <p>
341      * For Windows:
342      * Prepends the location of the JRE bin directory for the given JDK path to the PATH variable in Windows.
343      * This method assumes that the JRE is located within the JDK install directory
344      * in: <code><JDK install dir>/jre/bin/</code> where the JDK itself would be located
345      * in: <code><JDK install dir>/bin/</code>
346      * </p>
347      * <p>
348      * For Mac OS:
349      * Searches for and sets the correct state of the JAVA_VM_VERSION environment variable to ensure it matches
350      * the currently chosen VM of the launch config
351      * </p>
352      *
353      * @param env the current array of environment variables to run with
354      * @param jdkpath the path to the executable (javaw).
355      * @since 3.3
356      */

357     protected String JavaDoc[] prependJREPath(String JavaDoc[] env, IPath jdkpath) {
358         if(Platform.OS_WIN32.equals(Platform.getOS())) {
359             IPath jrepath = jdkpath.removeLastSegments(1);
360             if(jrepath.lastSegment().equals(BIN)) {
361                 if(!jrepath.segment(jrepath.segmentCount()-2).equals(JRE)) {
362                     jrepath = jrepath.removeLastSegments(1).append(JRE).append(BIN);
363                 }
364             }
365             else {
366                 jrepath = jrepath.append(JRE).append(BIN);
367             }
368             if(jrepath.toFile().exists()) {
369                 String JavaDoc jrestr = jrepath.toOSString();
370                 if(env == null){
371                     Map JavaDoc map = DebugPlugin.getDefault().getLaunchManager().getNativeEnvironment();
372                     env = new String JavaDoc[map.size()];
373                     String JavaDoc var = null;
374                     int index = 0;
375                     for(Iterator JavaDoc iter = map.keySet().iterator(); iter.hasNext();) {
376                         var = (String JavaDoc) iter.next();
377                         String JavaDoc value = (String JavaDoc) map.get(var);
378                         if (value == null) {
379                             value = ""; //$NON-NLS-1$
380
}
381                         if (var.equalsIgnoreCase("path")) { //$NON-NLS-1$
382
if(value.indexOf(jrestr) == -1) {
383                                 value = jrestr+';'+value;
384                             }
385                         }
386                         env[index] = var+"="+value; //$NON-NLS-1$
387
index++;
388                     }
389                 } else {
390                     String JavaDoc var = null;
391                     int esign = -1;
392                     for(int i = 0; i < env.length; i++) {
393                         esign = env[i].indexOf('=');
394                         if(esign > -1) {
395                             var = env[i].substring(0, esign);
396                             if(var != null && var.equalsIgnoreCase("path")) { //$NON-NLS-1$
397
if(env[i].indexOf(jrestr) == -1) {
398                                     env[i] = var + "="+jrestr+';'+(esign == env.length ? "" : env[i].substring(esign+1)); //$NON-NLS-1$ //$NON-NLS-2$
399
break;
400                                 }
401                             }
402                         }
403                     }
404                 }
405             }
406         }
407         return super.prependJREPath(env, jdkpath);
408     }
409     
410     /**
411      * Creates a new debug target for the given virtual machine and system process
412      * that is connected on the specified port for the given launch.
413      *
414      * @param config run configuration used to launch the VM
415      * @param launch launch to add the target to
416      * @param port port the VM is connected to
417      * @param process associated system process
418      * @param vm JDI virtual machine
419      */

420     protected IDebugTarget createDebugTarget(VMRunnerConfiguration config, ILaunch launch, int port, IProcess process, VirtualMachine vm) {
421         return JDIDebugModel.newDebugTarget(launch, vm, renderDebugTarget(config.getClassToLaunch(), port), process, true, false, config.isResumeOnStartup());
422     }
423     
424     /**
425      * Returns the version of the current VM in use
426      * @return the VM version
427      */

428     private double getJavaVersion() {
429         LibraryInfo libInfo = LaunchingPlugin.getLibraryInfo(fVMInstance.getInstallLocation().getAbsolutePath());
430         if (libInfo == null) {
431             return 0D;
432         }
433         String JavaDoc version = libInfo.getVersion();
434         int index = version.indexOf("."); //$NON-NLS-1$
435
int nextIndex = version.indexOf(".", index+1); //$NON-NLS-1$
436
try {
437             if (index > 0 && nextIndex>index) {
438                 return Double.parseDouble(version.substring(0,nextIndex));
439             }
440             return Double.parseDouble(version);
441         } catch (NumberFormatException JavaDoc e) {
442             return 0D;
443         }
444
445     }
446
447     /**
448      * Checks and forwards an error from the specified process
449      * @param process
450      * @throws CoreException
451      */

452     protected void checkErrorMessage(IProcess process) throws CoreException {
453         IStreamsProxy streamsProxy = process.getStreamsProxy();
454         if (streamsProxy != null) {
455             String JavaDoc errorMessage= streamsProxy.getErrorStreamMonitor().getContents();
456             if (errorMessage.length() == 0) {
457                 errorMessage= streamsProxy.getOutputStreamMonitor().getContents();
458             }
459             if (errorMessage.length() != 0) {
460                 abort(errorMessage, null, IJavaLaunchConfigurationConstants.ERR_VM_LAUNCH_ERROR);
461             }
462         }
463     }
464         
465     /**
466      * Allows arguments to be specified
467      * @param map
468      * @param portNumber
469      */

470     protected void specifyArguments(Map JavaDoc map, int portNumber) {
471         // XXX: Revisit - allows us to put a quote (") around the classpath
472
Connector.IntegerArgument port= (Connector.IntegerArgument) map.get("port"); //$NON-NLS-1$
473
port.setValue(portNumber);
474         
475         Connector.IntegerArgument timeoutArg= (Connector.IntegerArgument) map.get("timeout"); //$NON-NLS-1$
476
if (timeoutArg != null) {
477             int timeout = JavaRuntime.getPreferences().getInt(JavaRuntime.PREF_CONNECT_TIMEOUT);
478             timeoutArg.setValue(timeout);
479         }
480     }
481
482     /**
483      * Returns the default 'com.sun.jdi.SocketListen' connector
484      * @return
485      */

486     protected ListeningConnector getConnector() {
487         List JavaDoc connectors= Bootstrap.virtualMachineManager().listeningConnectors();
488         for (int i= 0; i < connectors.size(); i++) {
489             ListeningConnector c= (ListeningConnector) connectors.get(i);
490             if ("com.sun.jdi.SocketListen".equals(c.name())) //$NON-NLS-1$
491
return c;
492         }
493         return null;
494     }
495     
496 }
497
Popular Tags