KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > debugger > jpda > ant > JPDAStart


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.debugger.jpda.ant;
21
22 import java.io.File JavaDoc;
23 import java.net.MalformedURLException JavaDoc;
24 import java.net.URL JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Date JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.HashSet JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.Map JavaDoc;
32 import java.util.Set JavaDoc;
33 import java.beans.PropertyChangeEvent JavaDoc;
34
35 import com.sun.jdi.Bootstrap;
36 import com.sun.jdi.connect.ListeningConnector;
37 import com.sun.jdi.connect.Transport;
38 import com.sun.jdi.connect.Connector;
39 import java.util.logging.Level JavaDoc;
40 import java.util.logging.Logger JavaDoc;
41
42 import org.apache.tools.ant.BuildException;
43 import org.apache.tools.ant.Task;
44 import org.apache.tools.ant.Project;
45 import org.apache.tools.ant.types.Path;
46 import org.netbeans.api.debugger.jpda.DebuggerStartException;
47 import org.netbeans.api.java.classpath.GlobalPathRegistry;
48
49 import org.openide.ErrorManager;
50 import org.openide.filesystems.FileStateInvalidException;
51 import org.openide.util.RequestProcessor;
52 import org.openide.filesystems.FileObject;
53 import org.openide.filesystems.FileUtil;
54
55 import org.netbeans.api.java.classpath.ClassPath;
56 import org.netbeans.api.java.queries.SourceForBinaryQuery;
57 import org.netbeans.api.debugger.DebuggerManager;
58 import org.netbeans.api.debugger.DebuggerInfo;
59 import org.netbeans.api.debugger.jpda.JPDADebugger;
60 import org.netbeans.spi.java.classpath.support.ClassPathSupport;
61 import org.netbeans.api.debugger.DebuggerEngine;
62 import org.netbeans.api.debugger.DebuggerManagerAdapter;
63 import org.netbeans.api.debugger.jpda.MethodBreakpoint;
64 import org.netbeans.api.java.platform.JavaPlatform;
65
66
67 /**
68  * Ant task to start the NetBeans JPDA debugger in listening mode.
69  *
70  * @author Jesse Glick, David Konecny
71  */

72 public class JPDAStart extends Task implements Runnable JavaDoc {
73
74     private static final Logger JavaDoc logger = Logger.getLogger("org.netbeans.modules.debugger.jpda.ant"); // NOI18N
75

76     /** Name of the property to which the JPDA address will be set.
77      * Target VM should use this address and connect to it
78      */

79     private String JavaDoc addressProperty;
80     /** Default transport is socket*/
81     private String JavaDoc transport = "dt_socket"; // NOI18N
82
/** Name which will represent this debugging session in debugger UI.
83      * If known in advance it should be name of the app which will be debugged.
84      */

85     private String JavaDoc name;
86     /** Explicit sourcepath of the debugged process. */
87     private Path sourcepath = null;
88     /** Explicit classpath of the debugged process. */
89     private Path classpath = null;
90     /** Explicit bootclasspath of the debugged process. */
91     private Path bootclasspath = null;
92     private Object JavaDoc [] lock = null;
93     /** The class debugger should stop in, or null. */
94     private String JavaDoc stopClassName = null;
95
96     
97     // properties ..............................................................
98

99     public void setAddressProperty (String JavaDoc propertyName) {
100         this.addressProperty = propertyName;
101     }
102     
103     private String JavaDoc getAddressProperty () {
104         return addressProperty;
105     }
106     
107     public void setTransport (String JavaDoc transport) {
108         this.transport = transport;
109     }
110     
111     private String JavaDoc getTransport () {
112         return transport;
113     }
114     
115     public void setName (String JavaDoc name) {
116         this.name = name;
117     }
118     
119     private String JavaDoc getName () {
120         return name;
121     }
122     
123     public void setStopClassName (String JavaDoc stopClassName) {
124         this.stopClassName = stopClassName;
125     }
126     
127     private String JavaDoc getStopClassName () {
128         return stopClassName;
129     }
130     
131     public void addClasspath (Path path) {
132         if (classpath != null)
133             throw new BuildException ("Only one classpath subelement is supported");
134         classpath = path;
135     }
136     
137     public void addBootclasspath (Path path) {
138         if (bootclasspath != null)
139             throw new BuildException ("Only one bootclasspath subelement is supported");
140         bootclasspath = path;
141     }
142     
143     public void addSourcepath (Path path) {
144         if (sourcepath != null)
145             throw new BuildException ("Only one sourcepath subelement is supported");
146         sourcepath = path;
147     }
148     
149     static void verifyPaths(Project project, Path path) {
150         if (path == null) return ;
151         String JavaDoc[] paths = path.list();
152         for (int i = 0; i < paths.length; i++) {
153             String JavaDoc pathName = project.replaceProperties(paths[i]);
154             File JavaDoc file = FileUtil.normalizeFile
155                 (project.resolveFile (pathName));
156             if (!file.exists()) {
157                 project.log("Non-existing path \""+pathName+"\" provided.", Project.MSG_WARN);
158                 //throw new BuildException("Non-existing path \""+paths[i]+"\" provided.");
159
}
160         }
161     }
162
163     
164     // main methods ............................................................
165

166     public void execute () throws BuildException {
167         verifyPaths(getProject(), classpath);
168         //verifyPaths(getProject(), bootclasspath); Do not check the paths on bootclasspath (see issue #70930).
169
verifyPaths(getProject(), sourcepath);
170         try {
171             logger.fine("JPDAStart.execute()"); // NOI18N
172
debug ("Execute started"); // NOI18N
173
if (name == null)
174                 throw new BuildException ("name attribute must specify name of this debugging session", getLocation ());
175             if (addressProperty == null)
176                 throw new BuildException ("addressproperty attribute must specify name of property to which address will be set", getLocation ());
177             if (transport == null)
178                 transport = "dt_socket"; // NOI18N
179
debug ("Entering synch lock"); // NOI18N
180
lock = new Object JavaDoc [2];
181             synchronized (lock) {
182                 debug ("Entered synch lock"); // NOI18N
183
RequestProcessor.getDefault ().post (this);
184                 try {
185                     debug ("Entering wait"); // NOI18N
186
lock.wait ();
187                     debug ("Wait finished"); // NOI18N
188
if (lock [1] != null) {
189                         if (lock[1] instanceof DebuggerStartException) {
190                             //getProject().log(((DebuggerStartException) lock[1]).getLocalizedMessage(), Project.MSG_ERR);
191
throw new BuildException(((DebuggerStartException) lock[1]).getLocalizedMessage());
192                         } else {
193                             throw new BuildException ((Throwable JavaDoc) lock [1]);
194                         }
195                     }
196                 } catch (InterruptedException JavaDoc e) {
197                     throw new BuildException (e);
198                 }
199             }
200         } catch (Throwable JavaDoc t) {
201             t.printStackTrace ();
202             throw new BuildException (t);
203         }
204     }
205     
206     public void run () {
207         logger.fine("JPDAStart.run()"); // NOI18N
208
debug ("Entering synch lock"); // NOI18N
209
synchronized (lock) {
210             debug("Entered synch lock"); // NOI18N
211
try {
212
213                 ListeningConnector lc = null;
214                 Iterator JavaDoc i = Bootstrap.virtualMachineManager ().
215                     listeningConnectors ().iterator ();
216                 for (; i.hasNext ();) {
217                     lc = (ListeningConnector) i.next ();
218                     Transport t = lc.transport ();
219                     if (t != null && t.name ().equals (transport)) break;
220                 }
221                 if (lc == null)
222                     throw new BuildException
223                         ("No trasports named " + transport + " found!");
224
225                 // TODO: revisit later when http://developer.java.sun.com/developer/bugParade/bugs/4932074.html gets integrated into JDK
226
// This code parses the address string "HOST:PORT" to extract PORT and then point debugee to localhost:PORT
227
// This is NOT a clean solution to the problem but it SHOULD work in 99% cases
228
final Map JavaDoc args = lc.defaultArguments ();
229                 String JavaDoc address;
230                 try {
231                     address = lc.startListening (args);
232                 } catch (java.io.IOException JavaDoc ioex) {
233                     getProject().log("Listening failed with arguments: "+args);
234                     throw ioex;
235                 } catch (com.sun.jdi.connect.IllegalConnectorArgumentsException iaex) {
236                     getProject().log("Listening failed with arguments: "+args);
237                     throw iaex;
238                 }
239                 int port = -1;
240                 try {
241                     port = Integer.parseInt (address.substring (address.indexOf (':') + 1));
242                     getProject ().setNewProperty (getAddressProperty (), "localhost:" + port); // NOI18N
243
Connector.IntegerArgument portArg = (Connector.IntegerArgument) args.get("port"); // NOI18N
244
portArg.setValue (port);
245                 } catch (Exception JavaDoc e) {
246                     // this address format is not known, use default
247
getProject ().setNewProperty (getAddressProperty (), address);
248                 }
249
250                 debug ("Creating source path"); // NOI18N
251
ClassPath sourcePath = createSourcePath (
252                     getProject (),
253                     classpath,
254                     sourcepath
255                 );
256                 ClassPath jdkSourcePath = createJDKSourcePath (
257                     getProject (),
258                     bootclasspath
259                 );
260                 if (logger.isLoggable(Level.FINE)) {
261                     logger.fine("Create sourcepath:"); // NOI18N
262
logger.fine(" classpath : " + classpath); // NOI18N
263
logger.fine(" sourcepath : " + sourcepath); // NOI18N
264
logger.fine(" bootclasspath : " + bootclasspath); // NOI18N
265
logger.fine(" >> sourcePath : " + sourcePath); // NOI18N
266
logger.fine(" >> jdkSourcePath : " + jdkSourcePath); // NOI18N
267
}
268                 
269                 if (stopClassName != null && stopClassName.length() > 0) {
270                     logger.fine(
271                             "create method breakpoint, class name = " + // NOI18N
272
stopClassName
273                         );
274                     MethodBreakpoint b = createBreakpoint (stopClassName);
275                     DebuggerManager.getDebuggerManager ().addDebuggerListener (
276                         DebuggerManager.PROP_DEBUGGER_ENGINES,
277                         new Listener (b)
278                     );
279                 }
280                 
281                 debug ("Debugger started"); // NOI18N
282
logger.fine("start listening on port " + port); // NOI18N
283

284                 final Map JavaDoc properties = new HashMap JavaDoc ();
285                 // uncomment to implement smart stepping with step-outs
286
// rather than step-ins (for J2ME)
287
// props.put("SS_ACTION_STEPOUT", Boolean.TRUE);
288
properties.put ("sourcepath", sourcePath); // NOI18N
289
properties.put ("name", getName ()); // NOI18N
290
properties.put ("jdksources", jdkSourcePath); // NOI18N
291
final ListeningConnector flc = lc;
292                 // Let it start asynchronously so that the script can go on and start the debuggee
293
RequestProcessor.getDefault().post(new Runnable JavaDoc() {
294                     public void run() {
295                         try {
296                             JPDADebugger.startListening (
297                                 flc,
298                                 args,
299                                 new Object JavaDoc[] { properties }
300                             );
301                         } catch (DebuggerStartException dsex) {
302                             // Was not able to start up
303
}
304                     }
305                 });
306             } catch (java.io.IOException JavaDoc ioex) {
307                 lock[1] = ioex;
308             } catch (com.sun.jdi.connect.IllegalConnectorArgumentsException icaex) {
309                 lock[1] = icaex;
310             } finally {
311                 debug ("Notifying"); // NOI18N
312
lock.notify ();
313             }
314         }
315     } // run ()
316

317     
318     // support methods .........................................................
319

320     private MethodBreakpoint createBreakpoint (String JavaDoc stopClassName) {
321         MethodBreakpoint breakpoint = MethodBreakpoint.create (
322             stopClassName,
323             "*"
324         );
325         breakpoint.setHidden (true);
326         DebuggerManager.getDebuggerManager ().addBreakpoint (breakpoint);
327         return breakpoint;
328     }
329
330     private final static void debug (String JavaDoc msg) {
331         if (!logger.isLoggable(Level.FINER)) return;
332         logger.finer (
333             new Date JavaDoc() + " [" + Thread.currentThread().getName() +
334             "] - " + msg
335         );
336     }
337
338     static ClassPath createSourcePath (
339         Project project,
340         Path classpath,
341         Path sourcepath
342     ) {
343         ClassPath cp = convertToSourcePath (project, classpath);
344         ClassPath sp = convertToClassPath (project, sourcepath);
345         
346         ClassPath sourcePath = ClassPathSupport.createProxyClassPath (
347             new ClassPath[] {cp, sp}
348         );
349         return sourcePath;
350     }
351
352     static ClassPath createJDKSourcePath (
353         Project project,
354         Path bootclasspath
355     ) {
356         if (bootclasspath == null) {
357             // if current platform is default one, bootclasspath is set to null
358
JavaPlatform jp = JavaPlatform.getDefault();
359             if (jp != null) {
360                 return jp.getSourceFolders ();
361             } else {
362                 return ClassPathSupport.createClassPath(java.util.Collections.EMPTY_LIST);
363             }
364         } else {
365             return convertToSourcePath (project, bootclasspath);
366         }
367     }
368     
369     private static ClassPath convertToClassPath (Project project, Path path) {
370         String JavaDoc[] paths = path == null ? new String JavaDoc [0] : path.list ();
371         List JavaDoc l = new ArrayList JavaDoc ();
372         int i, k = paths.length;
373         for (i = 0; i < k; i++) {
374             String JavaDoc pathName = project.replaceProperties(paths[i]);
375             File JavaDoc f = FileUtil.normalizeFile (project.resolveFile (pathName));
376             if (!isValid (f, project)) continue;
377             URL JavaDoc url = fileToURL (f, project);
378             if (f == null) continue;
379             l.add (url);
380         }
381         URL JavaDoc[] urls = (URL JavaDoc[]) l.toArray (new URL JavaDoc [l.size ()]);
382         return ClassPathSupport.createClassPath (urls);
383     }
384     
385     /**
386      * This method uses SourceForBinaryQuery to find sources for each
387      * path item and returns them as ClassPath instance. All path items for which
388      * the sources were not found are omitted.
389      *
390      */

391     private static ClassPath convertToSourcePath (Project project, Path path) {
392         String JavaDoc[] paths = path == null ? new String JavaDoc [0] : path.list ();
393         List JavaDoc l = new ArrayList JavaDoc ();
394         Set JavaDoc exist = new HashSet JavaDoc ();
395         int i, k = paths.length;
396         for (i = 0; i < k; i++) {
397             String JavaDoc pathName = project.replaceProperties(paths[i]);
398             File JavaDoc file = FileUtil.normalizeFile
399                 (project.resolveFile (pathName));
400             if (!isValid (file, project)) continue;
401             URL JavaDoc url = fileToURL (file, project);
402             if (url == null) continue;
403             logger.fine("convertToSourcePath - class: " + url); // NOI18N
404
try {
405                 FileObject fos[] = SourceForBinaryQuery.findSourceRoots
406                     (url).getRoots();
407                 int j, jj = fos.length;
408                 /* ?? (#60640)
409                 if (jj == 0) { // no sourcepath defined
410                     // Take all registered source roots
411                     Set allSourceRoots = GlobalPathRegistry.getDefault().getSourceRoots();
412                     fos = (FileObject[]) allSourceRoots.toArray(new FileObject[0]);
413                     jj = fos.length;
414                 }
415                  */

416                 for (j = 0; j < jj; j++) {
417                     logger.fine("convertToSourcePath - source : " + fos [j]); // NOI18N
418
if (FileUtil.isArchiveFile (fos [j]))
419                         fos [j] = FileUtil.getArchiveRoot (fos [j]);
420                     try {
421                         url = fos [j].getURL ();
422                     } catch (FileStateInvalidException ex) {
423                         ErrorManager.getDefault ().notify
424                             (ErrorManager.EXCEPTION, ex);
425                         continue;
426                     }
427                     if (url == null) continue;
428                     if (!exist.contains (url)) {
429                         l.add (ClassPathSupport.createResource (url));
430                         exist.add (url);
431                     }
432                 } // for
433
} catch (IllegalArgumentException JavaDoc ex) {
434                 ErrorManager.getDefault().notify(ErrorManager.EXCEPTION, ex);
435                 logger.fine("Have illegal url! "+ex.getLocalizedMessage()); // NOI18N
436
}
437         }
438         return ClassPathSupport.createClassPath (l);
439     }
440
441
442     private static URL JavaDoc fileToURL (File JavaDoc file, Project project) {
443         try {
444             FileObject fileObject = FileUtil.toFileObject (file);
445             if (fileObject == null) return null;
446             if (FileUtil.isArchiveFile (fileObject)) {
447                 fileObject = FileUtil.getArchiveRoot (fileObject);
448                 if (fileObject == null) {
449                     project.log("Bad archive "+file.getAbsolutePath(), Project.MSG_WARN);
450                     /*
451                     ErrorManager.getDefault().notify(ErrorManager.getDefault().annotate(
452                             new NullPointerException("Bad archive "+file.toString()),
453                             NbBundle.getMessage(JPDAStart.class, "MSG_WrongArchive", file.getAbsolutePath())));
454                      */

455                     return null;
456                 }
457             }
458             return fileObject.getURL ();
459         } catch (FileStateInvalidException e) {
460             ErrorManager.getDefault ().notify (ErrorManager.EXCEPTION, e);
461             return null;
462         }
463     }
464
465     private static boolean isValid (File JavaDoc f, Project project) {
466         if (f.getPath ().indexOf ("${") != -1 && !f.exists ()) { // NOI18N
467
project.log (
468                 "Classpath item " + f + " will be ignored.", // NOI18N
469
Project.MSG_VERBOSE
470             );
471             return false;
472         }
473         return true;
474     }
475
476     
477     // innerclasses ............................................................
478

479     private static class Listener extends DebuggerManagerAdapter {
480         
481         private MethodBreakpoint breakpoint;
482         private Set JavaDoc debuggers = new HashSet JavaDoc ();
483         
484         
485         Listener (MethodBreakpoint breakpoint) {
486             this.breakpoint = breakpoint;
487         }
488         
489         public void propertyChange (PropertyChangeEvent JavaDoc e) {
490             if (e.getPropertyName () == JPDADebugger.PROP_STATE) {
491                 int state = ((Integer JavaDoc) e.getNewValue ()).intValue ();
492                 if ( (state == JPDADebugger.STATE_DISCONNECTED) ||
493                      (state == JPDADebugger.STATE_STOPPED)
494                 ) {
495                     RequestProcessor.getDefault ().post (new Runnable JavaDoc () {
496                         public void run () {
497                             if (breakpoint != null) {
498                                 DebuggerManager.getDebuggerManager ().
499                                     removeBreakpoint (breakpoint);
500                                 breakpoint = null;
501                             }
502                         }
503                     });
504                     dispose ();
505                 }
506             }
507             return;
508         }
509         
510         private void dispose () {
511             DebuggerManager.getDebuggerManager ().removeDebuggerListener (
512                 DebuggerManager.PROP_DEBUGGER_ENGINES,
513                 this
514             );
515             Iterator JavaDoc it = debuggers.iterator ();
516             while (it.hasNext ()) {
517                 JPDADebugger d = (JPDADebugger) it.next ();
518                 d.removePropertyChangeListener (
519                     JPDADebugger.PROP_STATE,
520                     this
521                 );
522             }
523         }
524         
525         public void engineAdded (DebuggerEngine engine) {
526             JPDADebugger debugger = (JPDADebugger) engine.lookupFirst
527                 (null, JPDADebugger.class);
528             if (debugger == null) return;
529             debugger.addPropertyChangeListener (
530                 JPDADebugger.PROP_STATE,
531                 this
532             );
533             debuggers.add (debugger);
534         }
535         
536         public void engineRemoved (DebuggerEngine engine) {
537             JPDADebugger debugger = (JPDADebugger) engine.lookupFirst
538                 (null, JPDADebugger.class);
539             if (debugger == null) return;
540             debugger.removePropertyChangeListener (
541                 JPDADebugger.PROP_STATE,
542                 this
543             );
544             debuggers.remove (debugger);
545         }
546     }
547 }
548
Popular Tags