1 19 20 package org.netbeans.modules.debugger.jpda.ant; 21 22 import java.io.File ; 23 import java.net.MalformedURLException ; 24 import java.net.URL ; 25 import java.util.ArrayList ; 26 import java.util.Date ; 27 import java.util.HashMap ; 28 import java.util.HashSet ; 29 import java.util.Iterator ; 30 import java.util.List ; 31 import java.util.Map ; 32 import java.util.Set ; 33 import java.beans.PropertyChangeEvent ; 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 ; 40 import java.util.logging.Logger ; 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 72 public class JPDAStart extends Task implements Runnable { 73 74 private static final Logger logger = Logger.getLogger("org.netbeans.modules.debugger.jpda.ant"); 76 79 private String addressProperty; 80 81 private String transport = "dt_socket"; 85 private String name; 86 87 private Path sourcepath = null; 88 89 private Path classpath = null; 90 91 private Path bootclasspath = null; 92 private Object [] lock = null; 93 94 private String stopClassName = null; 95 96 97 99 public void setAddressProperty (String propertyName) { 100 this.addressProperty = propertyName; 101 } 102 103 private String getAddressProperty () { 104 return addressProperty; 105 } 106 107 public void setTransport (String transport) { 108 this.transport = transport; 109 } 110 111 private String getTransport () { 112 return transport; 113 } 114 115 public void setName (String name) { 116 this.name = name; 117 } 118 119 private String getName () { 120 return name; 121 } 122 123 public void setStopClassName (String stopClassName) { 124 this.stopClassName = stopClassName; 125 } 126 127 private String 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 [] paths = path.list(); 152 for (int i = 0; i < paths.length; i++) { 153 String pathName = project.replaceProperties(paths[i]); 154 File file = FileUtil.normalizeFile 155 (project.resolveFile (pathName)); 156 if (!file.exists()) { 157 project.log("Non-existing path \""+pathName+"\" provided.", Project.MSG_WARN); 158 } 160 } 161 } 162 163 164 166 public void execute () throws BuildException { 167 verifyPaths(getProject(), classpath); 168 verifyPaths(getProject(), sourcepath); 170 try { 171 logger.fine("JPDAStart.execute()"); debug ("Execute started"); 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"; debug ("Entering synch lock"); lock = new Object [2]; 181 synchronized (lock) { 182 debug ("Entered synch lock"); RequestProcessor.getDefault ().post (this); 184 try { 185 debug ("Entering wait"); lock.wait (); 187 debug ("Wait finished"); if (lock [1] != null) { 189 if (lock[1] instanceof DebuggerStartException) { 190 throw new BuildException(((DebuggerStartException) lock[1]).getLocalizedMessage()); 192 } else { 193 throw new BuildException ((Throwable ) lock [1]); 194 } 195 } 196 } catch (InterruptedException e) { 197 throw new BuildException (e); 198 } 199 } 200 } catch (Throwable t) { 201 t.printStackTrace (); 202 throw new BuildException (t); 203 } 204 } 205 206 public void run () { 207 logger.fine("JPDAStart.run()"); debug ("Entering synch lock"); synchronized (lock) { 210 debug("Entered synch lock"); try { 212 213 ListeningConnector lc = null; 214 Iterator 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 final Map args = lc.defaultArguments (); 229 String address; 230 try { 231 address = lc.startListening (args); 232 } catch (java.io.IOException 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); Connector.IntegerArgument portArg = (Connector.IntegerArgument) args.get("port"); portArg.setValue (port); 245 } catch (Exception e) { 246 getProject ().setNewProperty (getAddressProperty (), address); 248 } 249 250 debug ("Creating source path"); 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:"); logger.fine(" classpath : " + classpath); logger.fine(" sourcepath : " + sourcepath); logger.fine(" bootclasspath : " + bootclasspath); logger.fine(" >> sourcePath : " + sourcePath); logger.fine(" >> jdkSourcePath : " + jdkSourcePath); } 268 269 if (stopClassName != null && stopClassName.length() > 0) { 270 logger.fine( 271 "create method breakpoint, class name = " + 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"); logger.fine("start listening on port " + port); 284 final Map properties = new HashMap (); 285 properties.put ("sourcepath", sourcePath); properties.put ("name", getName ()); properties.put ("jdksources", jdkSourcePath); final ListeningConnector flc = lc; 292 RequestProcessor.getDefault().post(new Runnable () { 294 public void run() { 295 try { 296 JPDADebugger.startListening ( 297 flc, 298 args, 299 new Object [] { properties } 300 ); 301 } catch (DebuggerStartException dsex) { 302 } 304 } 305 }); 306 } catch (java.io.IOException ioex) { 307 lock[1] = ioex; 308 } catch (com.sun.jdi.connect.IllegalConnectorArgumentsException icaex) { 309 lock[1] = icaex; 310 } finally { 311 debug ("Notifying"); lock.notify (); 313 } 314 } 315 } 317 318 320 private MethodBreakpoint createBreakpoint (String 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 msg) { 331 if (!logger.isLoggable(Level.FINER)) return; 332 logger.finer ( 333 new Date () + " [" + 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 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 [] paths = path == null ? new String [0] : path.list (); 371 List l = new ArrayList (); 372 int i, k = paths.length; 373 for (i = 0; i < k; i++) { 374 String pathName = project.replaceProperties(paths[i]); 375 File f = FileUtil.normalizeFile (project.resolveFile (pathName)); 376 if (!isValid (f, project)) continue; 377 URL url = fileToURL (f, project); 378 if (f == null) continue; 379 l.add (url); 380 } 381 URL [] urls = (URL []) l.toArray (new URL [l.size ()]); 382 return ClassPathSupport.createClassPath (urls); 383 } 384 385 391 private static ClassPath convertToSourcePath (Project project, Path path) { 392 String [] paths = path == null ? new String [0] : path.list (); 393 List l = new ArrayList (); 394 Set exist = new HashSet (); 395 int i, k = paths.length; 396 for (i = 0; i < k; i++) { 397 String pathName = project.replaceProperties(paths[i]); 398 File file = FileUtil.normalizeFile 399 (project.resolveFile (pathName)); 400 if (!isValid (file, project)) continue; 401 URL url = fileToURL (file, project); 402 if (url == null) continue; 403 logger.fine("convertToSourcePath - class: " + url); try { 405 FileObject fos[] = SourceForBinaryQuery.findSourceRoots 406 (url).getRoots(); 407 int j, jj = fos.length; 408 416 for (j = 0; j < jj; j++) { 417 logger.fine("convertToSourcePath - source : " + fos [j]); 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 } } catch (IllegalArgumentException ex) { 434 ErrorManager.getDefault().notify(ErrorManager.EXCEPTION, ex); 435 logger.fine("Have illegal url! "+ex.getLocalizedMessage()); } 437 } 438 return ClassPathSupport.createClassPath (l); 439 } 440 441 442 private static URL fileToURL (File 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 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 f, Project project) { 466 if (f.getPath ().indexOf ("${") != -1 && !f.exists ()) { project.log ( 468 "Classpath item " + f + " will be ignored.", Project.MSG_VERBOSE 470 ); 471 return false; 472 } 473 return true; 474 } 475 476 477 479 private static class Listener extends DebuggerManagerAdapter { 480 481 private MethodBreakpoint breakpoint; 482 private Set debuggers = new HashSet (); 483 484 485 Listener (MethodBreakpoint breakpoint) { 486 this.breakpoint = breakpoint; 487 } 488 489 public void propertyChange (PropertyChangeEvent e) { 490 if (e.getPropertyName () == JPDADebugger.PROP_STATE) { 491 int state = ((Integer ) e.getNewValue ()).intValue (); 492 if ( (state == JPDADebugger.STATE_DISCONNECTED) || 493 (state == JPDADebugger.STATE_STOPPED) 494 ) { 495 RequestProcessor.getDefault ().post (new Runnable () { 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 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 |