PageRenderTime 57ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/projects/netbeans-7.3/debugger.jpda.ant/antsrc/org/netbeans/modules/debugger/jpda/ant/JPDAStart.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 990 lines | 767 code | 83 blank | 140 comment | 151 complexity | 18f28afc539f1e5e054beda77553796c MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.netbeans.modules.debugger.jpda.ant;
  45. import java.io.File;
  46. import java.net.URL;
  47. import java.util.ArrayList;
  48. import java.util.Date;
  49. import java.util.HashMap;
  50. import java.util.HashSet;
  51. import java.util.List;
  52. import java.util.Map;
  53. import java.util.Set;
  54. import java.beans.PropertyChangeEvent;
  55. import com.sun.jdi.Bootstrap;
  56. import com.sun.jdi.connect.ListeningConnector;
  57. import com.sun.jdi.connect.Transport;
  58. import com.sun.jdi.connect.Connector;
  59. import java.beans.PropertyChangeListener;
  60. import java.lang.ref.WeakReference;
  61. import java.net.InetAddress;
  62. import java.net.UnknownHostException;
  63. import java.util.Collection;
  64. import java.util.LinkedList;
  65. import java.util.Map.Entry;
  66. import java.util.logging.Level;
  67. import java.util.logging.Logger;
  68. import java.util.regex.Pattern;
  69. import org.apache.tools.ant.BuildEvent;
  70. import org.apache.tools.ant.BuildException;
  71. import org.apache.tools.ant.BuildListener;
  72. import org.apache.tools.ant.Task;
  73. import org.apache.tools.ant.Project;
  74. import org.apache.tools.ant.types.Path;
  75. import org.netbeans.api.debugger.Breakpoint;
  76. import org.netbeans.api.debugger.jpda.DebuggerStartException;
  77. import org.openide.ErrorManager;
  78. import org.openide.filesystems.FileStateInvalidException;
  79. import org.openide.util.RequestProcessor;
  80. import org.openide.filesystems.FileObject;
  81. import org.openide.filesystems.FileUtil;
  82. import org.netbeans.api.java.classpath.ClassPath;
  83. import org.netbeans.api.java.queries.SourceForBinaryQuery;
  84. import org.netbeans.api.debugger.DebuggerManager;
  85. import org.netbeans.api.debugger.jpda.JPDADebugger;
  86. import org.netbeans.spi.java.classpath.support.ClassPathSupport;
  87. import org.netbeans.api.debugger.DebuggerEngine;
  88. import org.netbeans.api.debugger.DebuggerManagerAdapter;
  89. import org.netbeans.api.debugger.Session;
  90. import org.netbeans.api.debugger.jpda.ExceptionBreakpoint;
  91. import org.netbeans.api.debugger.jpda.JPDAClassType;
  92. import org.netbeans.api.debugger.jpda.JPDAThread;
  93. import org.netbeans.api.debugger.jpda.MethodBreakpoint;
  94. import org.netbeans.api.debugger.jpda.ObjectVariable;
  95. import org.netbeans.api.debugger.jpda.Variable;
  96. import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent;
  97. import org.netbeans.api.debugger.jpda.event.JPDABreakpointListener;
  98. import org.netbeans.api.java.platform.JavaPlatform;
  99. import org.netbeans.api.java.source.BuildArtifactMapper;
  100. import org.netbeans.api.java.source.BuildArtifactMapper.ArtifactsUpdated;
  101. import org.openide.util.Lookup;
  102. import org.openide.util.NbBundle;
  103. import org.openide.util.WeakListeners;
  104. /**
  105. * Ant task to start the NetBeans JPDA debugger in listening mode.
  106. *
  107. * @author Jesse Glick, David Konecny
  108. */
  109. public class JPDAStart extends Task implements Runnable {
  110. private static final Logger logger = Logger.getLogger("org.netbeans.modules.debugger.jpda.ant"); // NOI18N
  111. private static final String SOCKET_TRANSPORT = "dt_socket"; // NOI18N
  112. private static final String SHMEM_TRANSPORT = "dt_shmem"; // NOI18N
  113. private static final String SOCKET_CONNECTOR = "com.sun.jdi.SocketListen"; // NOI18N
  114. private static final String SHMEM_CONNECTOR = "com.sun.jdi.SharedMemoryListen"; // NOI18N
  115. /** Name of the property to which the JPDA address will be set.
  116. * Target VM should use this address and connect to it
  117. */
  118. private String addressProperty;
  119. /** Default transport is socket*/
  120. private String transport = SOCKET_TRANSPORT;
  121. /** Preferred connector name. May be null. */
  122. private String connector;
  123. /** Name which will represent this debugging session in debugger UI.
  124. * If known in advance it should be name of the app which will be debugged.
  125. */
  126. private String name;
  127. /** Explicit sourcepath of the debugged process. */
  128. private Sourcepath sourcepath = null;
  129. private Path plainSourcepath = null;
  130. private boolean isSourcePathExclusive;
  131. /** Explicit classpath of the debugged process. */
  132. private Path classpath = null;
  133. /** Explicit bootclasspath of the debugged process. */
  134. private Path bootclasspath = null;
  135. private final Object [] lock = new Object[2];
  136. /** The class debugger should stop in, or null. */
  137. private String stopClassName = null;
  138. private String listeningCP = null;
  139. private RequestProcessor rp = new RequestProcessor("JPDAStart", 1);
  140. // properties ..............................................................
  141. public void setAddressProperty (String propertyName) {
  142. this.addressProperty = propertyName;
  143. }
  144. private String getAddressProperty () {
  145. return addressProperty;
  146. }
  147. public void setTransport (String transport) {
  148. logger.fine("Set transport: '"+transport+"'");
  149. this.transport = transport;
  150. }
  151. private String getTransport () {
  152. return transport;
  153. }
  154. public void setConnector(String connector) {
  155. this.connector = connector;
  156. }
  157. public String getConnector() {
  158. return connector;
  159. }
  160. public void setName (String name) {
  161. this.name = name;
  162. }
  163. private String getName () {
  164. return name;
  165. }
  166. public void setStopClassName (String stopClassName) {
  167. this.stopClassName = stopClassName;
  168. }
  169. private String getStopClassName () {
  170. return stopClassName;
  171. }
  172. public void setListeningcp(String listeningCP) {
  173. this.listeningCP = listeningCP;
  174. }
  175. public void addClasspath (Path path) {
  176. logger.fine("addClasspath("+path+")");
  177. if (classpath != null)
  178. throw new BuildException ("Only one classpath subelement is supported");
  179. classpath = path;
  180. }
  181. public void addBootclasspath (Path path) {
  182. logger.fine("addBootclasspath("+path+")");
  183. if (bootclasspath != null)
  184. throw new BuildException ("Only one bootclasspath subelement is supported");
  185. bootclasspath = path;
  186. }
  187. public void addSourcepath (Sourcepath path) {
  188. logger.fine("addSourcepath("+path+")");
  189. if (sourcepath != null)
  190. throw new BuildException ("Only one sourcepath subelement is supported");
  191. sourcepath = path;
  192. }
  193. static void verifyPaths(Project project, Path path) {
  194. if (path == null) return ;
  195. String[] paths = path.list();
  196. for (int i = 0; i < paths.length; i++) {
  197. String pathName = project.replaceProperties(paths[i]);
  198. File file = FileUtil.normalizeFile
  199. (project.resolveFile (pathName));
  200. if (!file.exists()) {
  201. project.log("Non-existing path \""+pathName+"\" provided.", Project.MSG_WARN);
  202. //throw new BuildException("Non-existing path \""+paths[i]+"\" provided.");
  203. }
  204. }
  205. }
  206. /** Searching for a connector in given collection.
  207. * @param name - name of the connector
  208. * @param connectors
  209. * @return the connector or null
  210. */
  211. private static ListeningConnector findConnector(String name, final Collection<ListeningConnector> connectors) {
  212. assert name != null;
  213. for (ListeningConnector c : connectors) {
  214. if (name.equals(c.name())) {
  215. return c;
  216. }
  217. }
  218. return null;
  219. }
  220. // main methods ............................................................
  221. @Override
  222. public void execute () throws BuildException {
  223. verifyPaths(getProject(), classpath);
  224. //verifyPaths(getProject(), bootclasspath); Do not check the paths on bootclasspath (see issue #70930).
  225. if (sourcepath != null) {
  226. isSourcePathExclusive = sourcepath.isExclusive();
  227. plainSourcepath = sourcepath.getPlainPath();
  228. }
  229. verifyPaths(getProject(), plainSourcepath);
  230. try {
  231. logger.fine("JPDAStart.execute()"); // NOI18N
  232. debug ("Execute started"); // NOI18N
  233. if (name == null)
  234. throw new BuildException ("name attribute must specify name of this debugging session", getLocation ());
  235. if (addressProperty == null)
  236. throw new BuildException ("addressproperty attribute must specify name of property to which address will be set", getLocation ());
  237. if (transport == null)
  238. transport = SOCKET_TRANSPORT;
  239. debug ("Entering synch lock"); // NOI18N
  240. lock[0] = lock[1] = null;
  241. synchronized (lock) {
  242. debug ("Entered synch lock"); // NOI18N
  243. rp.post (this);
  244. try {
  245. debug ("Entering wait"); // NOI18N
  246. lock.wait ();
  247. debug ("Wait finished"); // NOI18N
  248. if (lock [1] != null) {
  249. if (lock[1] instanceof DebuggerStartException) {
  250. //getProject().log(((DebuggerStartException) lock[1]).getLocalizedMessage(), Project.MSG_ERR);
  251. throw new BuildException(((DebuggerStartException) lock[1]).getLocalizedMessage());
  252. } else if (lock[1] instanceof ThreadDeath) {
  253. throw (ThreadDeath) lock[1];
  254. } else {
  255. throw new BuildException ((Throwable) lock [1]);
  256. }
  257. }
  258. } catch (InterruptedException e) {
  259. throw new BuildException (e);
  260. }
  261. }
  262. } catch (Throwable t) {
  263. t.printStackTrace ();
  264. throw new BuildException (t);
  265. }
  266. }
  267. public void run () {
  268. logger.fine("JPDAStart.run()"); // NOI18N
  269. debug ("Entering synch lock"); // NOI18N
  270. synchronized (lock) {
  271. debug("Entered synch lock"); // NOI18N
  272. try {
  273. ListeningConnector lc = null;
  274. final Set<ListeningConnector> connectors = new HashSet<ListeningConnector>();
  275. // search for connectors registered by NetBeans modules
  276. // In JavaFX listening connectors are registered as Connector.class.
  277. final Lookup.Result<Connector> r = Lookup.getDefault().lookupResult(Connector.class);
  278. for(Connector c: r.allInstances()) {
  279. if (c instanceof ListeningConnector) connectors.add((ListeningConnector) c);
  280. }
  281. // use JDI default as well
  282. connectors.addAll(Bootstrap.virtualMachineManager().listeningConnectors());
  283. // if name of the connector has been specified, try to use it
  284. if (connector != null) {
  285. logger.log(Level.FINE, "Looking for connector {0}", connector);
  286. lc = findConnector(connector, connectors);
  287. }
  288. if (lc == null) {
  289. // if dt_socket then use default socket as specified by JDI
  290. if (transport.equals(SOCKET_TRANSPORT)) {
  291. logger.log(Level.FINE, "Looking for default connector {0}", SOCKET_CONNECTOR);
  292. lc = findConnector(SOCKET_CONNECTOR, connectors);
  293. // if dt_shmem then use the default socket as specified by JDI
  294. } else if (transport.equals(SHMEM_TRANSPORT)) {
  295. logger.log(Level.FINE, "Looking for default connector {0}", SHMEM_CONNECTOR);
  296. lc = findConnector(SHMEM_CONNECTOR, connectors);
  297. }
  298. }
  299. // fallback to the original, i.e. find first connector whose transport
  300. // name matches given transport
  301. if (lc == null) {
  302. logger.log(Level.FINE, "Fall back, looking for a connector with transport {0}", transport);
  303. for (ListeningConnector c: connectors) {
  304. Transport t = c.transport ();
  305. if (t != null && t.name ().equals (transport)) {
  306. lc = c;
  307. break;
  308. }
  309. }
  310. }
  311. if (lc == null)
  312. throw new BuildException
  313. ("No transports named " + transport + " found!");
  314. logger.log(Level.FINE, "Listening using connector {0}, transport {1}", new Object[] {lc.name(), lc.transport().name()});
  315. final Map args = lc.defaultArguments ();
  316. Connector.StringArgument localAddress = (Connector.StringArgument) args.get("localAddress"); // NOI18N
  317. if (localAddress != null) {
  318. localAddress.setValue("127.0.0.1"); // NOI18N
  319. }
  320. String address = null;
  321. try {
  322. address = lc.startListening (args);
  323. } catch (java.io.IOException ioex) {
  324. boolean passed = false;
  325. // workaround for issue 148490
  326. if (SHMEM_TRANSPORT.equals(transport)) {
  327. Connector.StringArgument argName = (Connector.StringArgument) args.get("name"); // NOI18N
  328. for (int x = 0; x < 5; x++) {
  329. String tryAddress = "javadebug" + Math.round(Math.random() * 10000); // NOI18N
  330. try {
  331. argName.setValue (tryAddress);
  332. address = lc.startListening (args);
  333. passed = true;
  334. break;
  335. } catch (Exception e) {
  336. // ignore
  337. }
  338. } // for
  339. }
  340. if (!passed) {
  341. getProject().log("Listening failed with arguments: "+args);
  342. throw ioex;
  343. }
  344. } catch (com.sun.jdi.connect.IllegalConnectorArgumentsException iaex) {
  345. getProject().log("Listening failed with arguments: "+args);
  346. throw iaex;
  347. }
  348. /* A fix to bug http://developer.java.sun.com/developer/bugParade/bugs/4932074.html has been integrated into JDK 1.5
  349. // Uncomment if the fix is not complete in all cases
  350. // This code parses the address string "HOST:PORT" to extract PORT and then point debugee to localhost:PORT
  351. // This is NOT a clean solution to the problem but it SHOULD work in 99% cases
  352. if (SOCKET_TRANSPORT.equals(transport)) {
  353. int port = -1;
  354. try {
  355. port = Integer.parseInt (address.substring (address.indexOf (':') + 1));
  356. Connector.IntegerArgument portArg = (Connector.IntegerArgument) args.get("port"); // NOI18N
  357. portArg.setValue (port);
  358. address = "localhost:" + port; // NOI18N
  359. } catch (Exception e) {
  360. // this address format is not known, use default
  361. }
  362. }*/
  363. if (SOCKET_TRANSPORT.equals(transport)) {
  364. try {
  365. int port = Integer.parseInt (address.substring (address.indexOf (':') + 1));
  366. Connector.IntegerArgument portArg = (Connector.IntegerArgument) args.get("port"); // NOI18N
  367. portArg.setValue (port);
  368. // Since some users have badly configured host addresses,
  369. // perform a check for the address and use "localhost"
  370. // if the address can not be resolved: (see http://www.netbeans.org/issues/show_bug.cgi?id=154974)
  371. String host = address.substring(0, address.indexOf (':'));
  372. logger.fine(" socket listening at " + address+", host = "+host+", port = "+port); // NOI18N
  373. try {
  374. InetAddress.getByName(host);
  375. } catch (UnknownHostException uhex) {
  376. logger.fine( "unknown host '"+host+"'");
  377. address = "localhost:" + port; // NOI18N
  378. } catch (SecurityException se) {}
  379. } catch (Exception e) {
  380. // ignore
  381. }
  382. }
  383. if (SHMEM_TRANSPORT.equals(transport)) {
  384. try {
  385. Connector.StringArgument name = (Connector.StringArgument) args.get("name"); // NOI18N
  386. name.setValue (address);
  387. } catch (Exception e) {
  388. // ignore
  389. }
  390. }
  391. getProject ().setNewProperty (getAddressProperty (), address);
  392. debug ("Creating source path"); // NOI18N
  393. ClassPath sourcePath = createSourcePath (
  394. getProject (),
  395. classpath,
  396. plainSourcepath,
  397. isSourcePathExclusive
  398. );
  399. ClassPath jdkSourcePath = createJDKSourcePath (
  400. getProject (),
  401. bootclasspath
  402. );
  403. if (logger.isLoggable(Level.FINE)) {
  404. logger.fine("Create sourcepath:"); // NOI18N
  405. logger.fine(" classpath : " + classpath); // NOI18N
  406. logger.fine(" sourcepath : " + plainSourcepath); // NOI18N
  407. logger.fine(" bootclasspath : " + bootclasspath); // NOI18N
  408. logger.fine(" >> sourcePath : " + sourcePath); // NOI18N
  409. logger.fine(" >> jdkSourcePath : " + jdkSourcePath); // NOI18N
  410. }
  411. Breakpoint first = null;
  412. if (stopClassName != null && stopClassName.length() > 0) {
  413. logger.fine(
  414. "create method breakpoint, class name = " + // NOI18N
  415. stopClassName
  416. );
  417. first = createBreakpoint (stopClassName);
  418. }
  419. debug ("Debugger started"); // NOI18N
  420. logger.fine("start listening at " + address); // NOI18N
  421. final Map properties = new HashMap ();
  422. // uncomment to implement smart stepping with step-outs
  423. // rather than step-ins (for J2ME)
  424. // props.put("SS_ACTION_STEPOUT", Boolean.TRUE);
  425. properties.put ("sourcepath", sourcePath); // NOI18N
  426. properties.put ("name", getName ()); // NOI18N
  427. properties.put ("jdksources", jdkSourcePath); // NOI18N
  428. properties.put ("listeningCP", listeningCP); // NOI18N
  429. String workDir = getProject().getProperty("work.dir");
  430. File baseDir;
  431. if (workDir != null) {
  432. baseDir = new File(workDir);
  433. } else {
  434. baseDir = getProject().getBaseDir();
  435. }
  436. properties.put ("baseDir", baseDir); // NOI18N
  437. logger.fine("JPDAStart: properties = "+properties);
  438. final ListeningConnector flc = lc;
  439. final WeakReference<Session> startedSessionRef[] = new WeakReference[] { new WeakReference<Session>(null) };
  440. Map<URL, ArtifactsUpdated> listeners = new HashMap<URL, ArtifactsUpdated>();
  441. List<Breakpoint> artificialBreakpoints = new LinkedList<Breakpoint>();
  442. if (listeningCP != null) {
  443. ExceptionBreakpoint b = createCompilationErrorBreakpoint();
  444. DebuggerManager.getDebuggerManager ().addBreakpoint (b);
  445. artificialBreakpoints.add(b);
  446. }
  447. DebuggerManager.getDebuggerManager().addDebuggerListener(
  448. DebuggerManager.PROP_DEBUGGER_ENGINES,
  449. new Listener(first, artificialBreakpoints, listeners, startedSessionRef, rp));
  450. // Let it start asynchronously so that the script can go on and start the debuggee
  451. final Thread[] listeningThreadPtr = new Thread[] { null };
  452. final boolean[] listeningStarted = new boolean[] { false };
  453. rp.post(new Runnable() {
  454. public void run() {
  455. synchronized (listeningStarted) {
  456. listeningThreadPtr[0] = Thread.currentThread();
  457. listeningStarted[0] = true;
  458. listeningStarted.notifyAll();
  459. }
  460. try {
  461. DebuggerEngine[] engines = JPDADebugger.startListeningAndGetEngines (
  462. flc,
  463. args,
  464. new Object[] { properties }
  465. );
  466. startedSessionRef[0] = new WeakReference(engines[0].lookupFirst(null, Session.class));
  467. } catch (DebuggerStartException dsex) {
  468. // Was not able to start up
  469. } finally {
  470. synchronized (listeningStarted) {
  471. listeningThreadPtr[0] = null;
  472. listeningStarted.notifyAll();
  473. }
  474. }
  475. }
  476. });
  477. logger.log(Level.FINE, "adding a BuildListener to project {0} in {1}", new Object[] {getProject().getName(), getProject().getBaseDir()});
  478. getProject().addBuildListener(new BuildListener() {
  479. public void messageLogged(BuildEvent event) {}
  480. public void taskStarted(BuildEvent event) { }
  481. public void taskFinished(BuildEvent event) {}
  482. public void targetStarted(BuildEvent event) {}
  483. public void targetFinished(BuildEvent event) {}
  484. public void buildStarted(BuildEvent event) {}
  485. public void buildFinished(BuildEvent event) {
  486. // First wait until listening actually starts:
  487. logger.fine("buildFinished: waiting for listening start...");
  488. synchronized (listeningStarted) {
  489. if (!listeningStarted[0]) {
  490. try {
  491. listeningStarted.wait();
  492. } catch (InterruptedException ex) {}
  493. }
  494. }
  495. logger.fine("buildFinished: stopping listening...");
  496. // Then stop it:
  497. try {
  498. flc.stopListening(args);
  499. } catch (java.io.IOException ioex) {
  500. } catch (com.sun.jdi.connect.IllegalConnectorArgumentsException iaex) {
  501. }
  502. logger.fine("buildFinished: interrupting listening thread...");
  503. // If the listening is still running, interrupt it:
  504. for (int i = 0; i < 10; i++) {
  505. synchronized (listeningStarted) {
  506. logger.fine("buildFinished: listening thread = "+listeningThreadPtr[0]);
  507. if (listeningThreadPtr[0] != null) {
  508. listeningThreadPtr[0].interrupt();
  509. try {
  510. listeningStarted.wait(500);
  511. } catch (InterruptedException ex) {}
  512. } else {
  513. break;
  514. }
  515. }
  516. }
  517. // Finally, kill the started session:
  518. Session s = startedSessionRef[0].get();
  519. logger.fine("buildFinished: killing session "+s);
  520. if (s != null) {
  521. s.kill();
  522. }
  523. }
  524. });
  525. } catch (java.io.IOException ioex) {
  526. lock[1] = ioex;
  527. } catch (com.sun.jdi.connect.IllegalConnectorArgumentsException icaex) {
  528. lock[1] = icaex;
  529. } catch (ThreadDeath td) {
  530. // Session was canceled - see issue #148483
  531. lock[1] = td;
  532. } finally {
  533. debug ("Notifying"); // NOI18N
  534. lock.notify ();
  535. }
  536. }
  537. } // run ()
  538. // support methods .........................................................
  539. private MethodBreakpoint createBreakpoint (String stopClassName) {
  540. MethodBreakpoint breakpoint = MethodBreakpoint.create (
  541. stopClassName,
  542. "*"
  543. );
  544. breakpoint.setHidden (true);
  545. DebuggerManager.getDebuggerManager ().addBreakpoint (breakpoint);
  546. return breakpoint;
  547. }
  548. private ExceptionBreakpoint createCompilationErrorBreakpoint() {
  549. ExceptionBreakpoint b = ExceptionBreakpoint.create("java.lang.RuntimeException", ExceptionBreakpoint.TYPE_EXCEPTION_UNCATCHED);
  550. b.setHidden (true);
  551. b.addJPDABreakpointListener(new JPDABreakpointListener() {
  552. public void breakpointReached(JPDABreakpointEvent event) {
  553. boolean suspend = false;
  554. try {
  555. if (event.getVariable() instanceof ObjectVariable) {
  556. ObjectVariable ov = (ObjectVariable) event.getVariable();
  557. JPDAClassType ct = ov.getClassType();
  558. if (ct != null) {
  559. suspend = "java.lang.RuntimeException".equals(ct.getName());
  560. if (suspend) {
  561. java.lang.reflect.Method invokeMethodMethod = ov.getClass().getMethod("invokeMethod", JPDAThread.class, String.class, String.class, Variable[].class);
  562. invokeMethodMethod.setAccessible(true);
  563. Variable message = (Variable) invokeMethodMethod.invoke(ov, event.getThread(), "getMessage", "()Ljava/lang/String;", new Variable[0]);
  564. if (message != null) {
  565. suspend = message.getValue().startsWith("\"Uncompilable source code");
  566. }
  567. //suspend = suspend && ov.invokeMethod("getMessage", "()Ljava/lang/String;", new Variable[0]).getValue().startsWith("\"Uncompilable source code");
  568. }
  569. }
  570. }
  571. } catch (IllegalAccessException iaex) {
  572. logger.log(Level.FINE, null, iaex);
  573. } catch (java.lang.reflect.InvocationTargetException itex) {
  574. logger.log(Level.FINE, null, itex);
  575. } catch (NoSuchMethodException ex) {
  576. logger.log(Level.FINE, null, ex);
  577. //} catch (InvalidExpressionException ex) {
  578. // logger.log(Level.FINE, null, ex);
  579. }
  580. if (!suspend) {
  581. event.resume();
  582. }
  583. }
  584. });
  585. b.setPrintText(NbBundle.getBundle("org/netbeans/modules/debugger/jpda/ant/Bundle").getString("MSG_StoppedOnCompileError"));
  586. return b;
  587. }
  588. private final static void debug (String msg) {
  589. if (!logger.isLoggable(Level.FINER)) return;
  590. logger.finer (
  591. new Date() + " [" + Thread.currentThread().getName() +
  592. "] - " + msg
  593. );
  594. }
  595. static ClassPath createSourcePath (
  596. Project project,
  597. Path classpath,
  598. Path sourcepath,
  599. boolean isSourcePathExclusive
  600. ) {
  601. if (sourcepath != null && isSourcePathExclusive) {
  602. return convertToClassPath (project, sourcepath);
  603. }
  604. ClassPath cp = convertToSourcePath (project, classpath, true);
  605. ClassPath sp = convertToClassPath (project, sourcepath);
  606. ClassPath sourcePath = ClassPathSupport.createProxyClassPath (
  607. new ClassPath[] {cp, sp}
  608. );
  609. return sourcePath;
  610. }
  611. static ClassPath createJDKSourcePath (
  612. Project project,
  613. Path bootclasspath
  614. ) {
  615. if (bootclasspath == null) {
  616. // if current platform is default one, bootclasspath is set to null
  617. JavaPlatform jp = JavaPlatform.getDefault();
  618. if (jp != null) {
  619. return jp.getSourceFolders ();
  620. } else {
  621. return ClassPathSupport.createClassPath(java.util.Collections.EMPTY_LIST);
  622. }
  623. } else {
  624. return convertToSourcePath (project, bootclasspath, false);
  625. }
  626. }
  627. private static ClassPath convertToClassPath (Project project, Path path) {
  628. String[] paths = path == null ? new String [0] : path.list ();
  629. List l = new ArrayList ();
  630. int i, k = paths.length;
  631. for (i = 0; i < k; i++) {
  632. String pathName = project.replaceProperties(paths[i]);
  633. File f = FileUtil.normalizeFile (project.resolveFile (pathName));
  634. if (!isValid (f, project)) continue;
  635. URL url = fileToURL (f, project, true, false);
  636. if (url == null) continue;
  637. l.add (url);
  638. }
  639. URL[] urls = (URL[]) l.toArray (new URL [l.size ()]);
  640. return ClassPathSupport.createClassPath (urls);
  641. }
  642. /**
  643. * This method uses SourceForBinaryQuery to find sources for each
  644. * path item and returns them as ClassPath instance. All path items for which
  645. * the sources were not found are omitted.
  646. *
  647. */
  648. private static ClassPath convertToSourcePath (Project project, Path path, boolean reportNonExistingFiles) {
  649. String[] paths = path == null ? new String [0] : path.list ();
  650. List l = new ArrayList ();
  651. Set exist = new HashSet ();
  652. int i, k = paths.length;
  653. for (i = 0; i < k; i++) {
  654. String pathName = project.replaceProperties(paths[i]);
  655. File file = FileUtil.normalizeFile
  656. (project.resolveFile (pathName));
  657. if (!isValid (file, project)) continue;
  658. URL url = fileToURL (file, project, reportNonExistingFiles, true);
  659. if (url == null) continue;
  660. logger.fine("convertToSourcePath - class: " + url); // NOI18N
  661. try {
  662. SourceForBinaryQuery.Result srcRootsResult = SourceForBinaryQuery.findSourceRoots(url);
  663. FileObject fos[] = srcRootsResult.getRoots();
  664. int j, jj = fos.length;
  665. logger.fine(" source roots = "+java.util.Arrays.asList(fos)+"; jj = "+jj);
  666. /* ?? (#60640)
  667. if (jj == 0) { // no sourcepath defined
  668. // Take all registered source roots
  669. Set allSourceRoots = GlobalPathRegistry.getDefault().getSourceRoots();
  670. fos = (FileObject[]) allSourceRoots.toArray(new FileObject[0]);
  671. jj = fos.length;
  672. }
  673. */
  674. for (j = 0; j < jj; j++) {
  675. logger.fine("convertToSourcePath - source : " + fos [j]); // NOI18N
  676. if (FileUtil.isArchiveFile (fos [j]))
  677. fos [j] = FileUtil.getArchiveRoot (fos [j]);
  678. try {
  679. url = fos [j].getURL ();
  680. } catch (FileStateInvalidException ex) {
  681. ErrorManager.getDefault ().notify
  682. (ErrorManager.EXCEPTION, ex);
  683. continue;
  684. }
  685. if (url == null) continue;
  686. if (!exist.contains (url)) {
  687. l.add (ClassPathSupport.createResource (url));
  688. exist.add (url);
  689. }
  690. } // for
  691. } catch (IllegalArgumentException ex) {
  692. ErrorManager.getDefault().notify(ErrorManager.EXCEPTION, ex);
  693. logger.fine("Have illegal url! "+ex.getLocalizedMessage()); // NOI18N
  694. }
  695. }
  696. return ClassPathSupport.createClassPath (l);
  697. }
  698. private static URL fileToURL (File file, Project project, boolean reportNonExistingFiles, boolean withSlash) {
  699. try {
  700. FileObject fileObject = FileUtil.toFileObject (file);
  701. if (fileObject == null) {
  702. if (reportNonExistingFiles) {
  703. String path = file.getAbsolutePath();
  704. project.log("Have no file for "+path, Project.MSG_WARN);
  705. }
  706. return null;
  707. }
  708. if (FileUtil.isArchiveFile (fileObject)) {
  709. fileObject = FileUtil.getArchiveRoot (fileObject);
  710. if (fileObject == null) {
  711. project.log("Bad archive "+file.getAbsolutePath(), Project.MSG_WARN);
  712. /*
  713. ErrorManager.getDefault().notify(ErrorManager.getDefault().annotate(
  714. new NullPointerException("Bad archive "+file.toString()),
  715. NbBundle.getMessage(JPDAStart.class, "MSG_WrongArchive", file.getAbsolutePath())));
  716. */
  717. return null;
  718. }
  719. }
  720. if (withSlash) {
  721. return FileUtil.urlForArchiveOrDir(file);
  722. } else {
  723. return fileObject.getURL ();
  724. }
  725. } catch (FileStateInvalidException e) {
  726. ErrorManager.getDefault ().notify (ErrorManager.EXCEPTION, e);
  727. return null;
  728. }
  729. }
  730. private static boolean isValid (File f, Project project) {
  731. if (f.getPath ().indexOf ("${") != -1 && !f.exists ()) { // NOI18N
  732. project.log (
  733. "Classpath item " + f + " will be ignored.", // NOI18N
  734. Project.MSG_VERBOSE
  735. );
  736. return false;
  737. }
  738. return true;
  739. }
  740. // innerclasses ............................................................
  741. public static class Sourcepath extends Path {
  742. private boolean isExclusive;
  743. private String path = null;
  744. private Path plainPath;
  745. public Sourcepath(Project p) {
  746. super(p);
  747. logger.fine("new Sourcepath("+p+")");
  748. }
  749. public Sourcepath(Project p, String path) {
  750. super(p, path);
  751. this.path = path;
  752. logger.fine("new Sourcepath("+p+", "+path+")");
  753. }
  754. public void setExclusive(String exclusive) {
  755. isExclusive = "true".equalsIgnoreCase(exclusive);
  756. }
  757. boolean isExclusive() {
  758. return isExclusive;
  759. }
  760. public Path getPlainPath() {
  761. if (plainPath == null) {
  762. if (getRefid() != null) {
  763. Path pp;
  764. if (path != null) {
  765. pp = new Path(getProject(), path);
  766. } else {
  767. pp = new Path(getProject());
  768. }
  769. pp.setLocation(getLocation());
  770. pp.setDescription(getDescription());
  771. pp.setRefid(getRefid());
  772. //pp.setChecked(isChecked());
  773. //pp.union = union == null ? union : (Union) union.clone();
  774. plainPath = pp;
  775. } else {
  776. plainPath = this;
  777. }
  778. }
  779. return plainPath;
  780. }
  781. }
  782. private static class Listener extends DebuggerManagerAdapter {
  783. private final PropertyChangeListener pcl = WeakListeners.propertyChange(this, null);
  784. private Set<DebuggerEngine> engines = new HashSet<DebuggerEngine>();
  785. private Breakpoint first;
  786. private final List<Breakpoint> artificalBreakpoints;
  787. private final Map<URL, ArtifactsUpdated> listeners;
  788. private final WeakReference<Session> startedSessionRef[];
  789. private boolean enginesCheckDone = false;
  790. private final RequestProcessor rp;
  791. private Listener(Breakpoint first,
  792. List<Breakpoint> artificalBreakpoints,
  793. Map<URL, ArtifactsUpdated> listeners,
  794. WeakReference<Session> startedSessionRef[],
  795. RequestProcessor rp) {
  796. this.first = first;
  797. this.artificalBreakpoints = artificalBreakpoints;
  798. this.listeners = listeners;
  799. this.startedSessionRef = startedSessionRef;
  800. this.rp = rp;
  801. }
  802. @Override
  803. public void propertyChange (final PropertyChangeEvent e) {
  804. if (JPDADebugger.PROP_STATE.equals(e.getPropertyName ())) {
  805. int state = ((Integer) e.getNewValue ()).intValue ();
  806. if (state == JPDADebugger.STATE_STOPPED || state == JPDADebugger.STATE_DISCONNECTED) {
  807. rp.post(new Runnable() {
  808. public void run() {
  809. if (first != null) {
  810. DebuggerManager.getDebuggerManager().removeBreakpoint(first);
  811. first = null;
  812. ((JPDADebugger) e.getSource()).removePropertyChangeListener(JPDADebugger.PROP_STATE, pcl);
  813. }
  814. }
  815. });
  816. }
  817. }
  818. return;
  819. }
  820. private void dispose() {
  821. DebuggerManager.getDebuggerManager ().removeDebuggerListener (
  822. DebuggerManager.PROP_DEBUGGER_ENGINES,
  823. this
  824. );
  825. rp.post (new Runnable () {
  826. public void run () {
  827. if (artificalBreakpoints != null) {
  828. for (Breakpoint b : artificalBreakpoints) {
  829. DebuggerManager.getDebuggerManager().removeBreakpoint(b);
  830. }
  831. }
  832. if (first != null) {
  833. DebuggerManager.getDebuggerManager().removeBreakpoint(first);
  834. }
  835. if (listeners != null) {
  836. for (Entry<URL, ArtifactsUpdated> e : listeners.entrySet()) {
  837. BuildArtifactMapper.removeArtifactsUpdatedListener(e.getKey(), e.getValue());
  838. }
  839. }
  840. }
  841. });
  842. }
  843. @Override
  844. public void engineAdded (DebuggerEngine engine) {
  845. // Consider only engines from the started session.
  846. Session session;
  847. synchronized (startedSessionRef) {
  848. session = startedSessionRef[0].get();
  849. }
  850. if (session != null) {
  851. // perform check
  852. boolean hasEngine = false;
  853. for (String l : session.getSupportedLanguages()) {
  854. if (engine.equals(session.getEngineForLanguage(l))) {
  855. hasEngine = true;
  856. break;
  857. }
  858. }
  859. if (!hasEngine) {
  860. return;
  861. }
  862. }
  863. JPDADebugger debugger = engine.lookupFirst(null, JPDADebugger.class);
  864. if (debugger == null) return;
  865. debugger.addPropertyChangeListener (
  866. JPDADebugger.PROP_STATE,
  867. pcl
  868. );
  869. engines.add(engine);
  870. }
  871. @Override
  872. public void engineRemoved (DebuggerEngine engine) {
  873. Session session;
  874. synchronized (startedSessionRef) {
  875. session = startedSessionRef[0].get();
  876. }
  877. if (session != null && !enginesCheckDone) {
  878. // check each registered engine if it belong to the session
  879. enginesCheckDone = true;
  880. List<DebuggerEngine> list = new ArrayList<DebuggerEngine>(engines);
  881. for (DebuggerEngine eng : list) {
  882. boolean hasEngine = false;
  883. for (String l : session.getSupportedLanguages()) {
  884. if (engine.equals(session.getEngineForLanguage(l))) {
  885. hasEngine = true;
  886. break;
  887. }
  888. }
  889. if (!hasEngine) {
  890. engines.remove(eng);
  891. }
  892. }
  893. }
  894. JPDADebugger debugger = engine.lookupFirst(null, JPDADebugger.class);
  895. if (debugger == null) return;
  896. if (engines.remove(engine)) {
  897. debugger.removePropertyChangeListener (
  898. JPDADebugger.PROP_STATE,
  899. pcl
  900. );
  901. }
  902. if (engines.isEmpty()) {
  903. dispose();
  904. }
  905. }
  906. }
  907. }