001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.api.debugger.jpda;
043:
044: import com.sun.jdi.Bootstrap;
045: import com.sun.jdi.VirtualMachine;
046: import com.sun.jdi.connect.AttachingConnector;
047: import com.sun.jdi.connect.Connector.Argument;
048: import com.sun.jdi.connect.IllegalConnectorArgumentsException;
049: import com.sun.jdi.connect.ListeningConnector;
050: import com.sun.jdi.request.EventRequest;
051:
052: import java.beans.PropertyChangeListener;
053: import java.io.File;
054: import java.io.IOException;
055: import java.util.Collections;
056: import java.util.Iterator;
057: import java.util.List;
058: import java.util.Map;
059: import org.netbeans.api.debugger.DebuggerEngine;
060: import org.netbeans.api.debugger.DebuggerInfo;
061: import org.netbeans.api.debugger.DebuggerManager;
062: import org.netbeans.api.debugger.jpda.InvalidExpressionException;
063: import org.netbeans.api.debugger.jpda.Variable;
064: import org.netbeans.api.debugger.jpda.event.JPDABreakpointEvent;
065: import org.openide.util.NbBundle;
066:
067: /**
068: * Represents one JPDA debugger session (one
069: * {@link com.sun.jdi.VirtualMachine}).
070: *
071: * <br><br>
072: * <b>How to obtain it from DebuggerEngine:</b>
073: * <pre style="background-color: rgb(255, 255, 153);">
074: * JPDADebugger jpdaDebugger = (JPDADebugger) debuggerEngine.lookup
075: * (JPDADebugger.class);</pre>
076: *
077: * @author Jan Jancura
078: */
079: public abstract class JPDADebugger {
080:
081: /** Name of property for state of debugger. */
082: public static final String PROP_STATE = "state";
083: /** Name of property for current thread. */
084: public static final String PROP_CURRENT_THREAD = "currentThread";
085: /** Name of property for current stack frame. */
086: public static final String PROP_CURRENT_CALL_STACK_FRAME = "currentCallStackFrame";
087: /** Property name constant. */
088: public static final String PROP_SUSPEND = "suspend"; // NOI18N
089:
090: /** Suspend property value constant. */
091: public static final int SUSPEND_ALL = EventRequest.SUSPEND_ALL;
092: /** Suspend property value constant. */
093: public static final int SUSPEND_EVENT_THREAD = EventRequest.SUSPEND_EVENT_THREAD;
094:
095: /** Debugger state constant. */
096: public static final int STATE_STARTING = 1;
097: /** Debugger state constant. */
098: public static final int STATE_RUNNING = 2;
099: /** Debugger state constant. */
100: public static final int STATE_STOPPED = 3;
101: /** Debugger state constant. */
102: public static final int STATE_DISCONNECTED = 4;
103:
104: /** ID of JPDA Debugger Engine. */
105: public static final String ENGINE_ID = "netbeans-JPDASession/Java";
106: /** ID of JPDA Debugger Engine. */
107: public static final String SESSION_ID = "netbeans-JPDASession";
108:
109: /**
110: * This utility method helps to start a new JPDA debugger session.
111: * Its implementation use {@link LaunchingDICookie} and
112: * {@link org.netbeans.api.debugger.DebuggerManager#getDebuggerManager}.
113: *
114: * @param mainClassName a name or main class
115: * @param args command line arguments
116: * @param classPath a classPath
117: * @param suspend if true session will be suspended
118: */
119: public static void launch(String mainClassName, String[] args,
120: String classPath, boolean suspend) {
121: DebuggerEngine[] es = DebuggerManager.getDebuggerManager()
122: .startDebugging(
123: DebuggerInfo.create(LaunchingDICookie.ID,
124: new Object[] { LaunchingDICookie
125: .create(mainClassName, args,
126: classPath, suspend) }));
127: if (es.length == 0) {
128: /* Can not throw DebuggerStartException, but it should...
129: throw new DebuggerStartException(
130: NbBundle.getMessage(JPDADebugger.class, "MSG_NO_DEBUGGER")); */
131: throw new RuntimeException(NbBundle.getMessage(
132: JPDADebugger.class, "MSG_NO_DEBUGGER"));
133: }
134: }
135:
136: /**
137: * This utility method helps to start a new JPDA debugger session.
138: * Its implementation use {@link ListeningDICookie} and
139: * {@link org.netbeans.api.debugger.DebuggerManager#getDebuggerManager}.
140: *
141: * @param connector The listening connector
142: * @param args The arguments
143: * @param services The additional services
144: */
145: public static JPDADebugger listen(ListeningConnector connector,
146: Map<String, ? extends Argument> args, Object[] services)
147: throws DebuggerStartException {
148: Object[] s = new Object[services.length + 1];
149: System.arraycopy(services, 0, s, 1, services.length);
150: s[0] = ListeningDICookie.create(connector, args);
151: DebuggerEngine[] es = DebuggerManager.getDebuggerManager()
152: .startDebugging(
153: DebuggerInfo.create(ListeningDICookie.ID, s));
154: int i, k = es.length;
155: for (i = 0; i < k; i++) {
156: JPDADebugger d = es[i]
157: .lookupFirst(null, JPDADebugger.class);
158: if (d == null)
159: continue;
160: d.waitRunning();
161: return d;
162: }
163: throw new DebuggerStartException(NbBundle.getMessage(
164: JPDADebugger.class, "MSG_NO_DEBUGGER"));
165: }
166:
167: /**
168: * This utility method helps to start a new JPDA debugger session.
169: * Its implementation use {@link ListeningDICookie} and
170: * {@link org.netbeans.api.debugger.DebuggerManager#getDebuggerManager}.
171: *
172: * @param connector The listening connector
173: * @param args The arguments
174: * @param services The additional services
175: */
176: public static void startListening(ListeningConnector connector,
177: Map<String, ? extends Argument> args, Object[] services)
178: throws DebuggerStartException {
179: Object[] s = new Object[services.length + 1];
180: System.arraycopy(services, 0, s, 1, services.length);
181: s[0] = ListeningDICookie.create(connector, args);
182: DebuggerEngine[] es = DebuggerManager.getDebuggerManager()
183: .startDebugging(
184: DebuggerInfo.create(ListeningDICookie.ID, s));
185: if (es.length == 0) {
186: throw new DebuggerStartException(NbBundle.getMessage(
187: JPDADebugger.class, "MSG_NO_DEBUGGER"));
188: }
189: }
190:
191: /**
192: * This utility method helps to start a new JPDA debugger session.
193: * Its implementation use {@link AttachingDICookie} and
194: * {@link org.netbeans.api.debugger.DebuggerManager#getDebuggerManager}.
195: *
196: * @param hostName a name of computer to attach to
197: * @param portNumber a port number
198: */
199: public static JPDADebugger attach(String hostName, int portNumber,
200: Object[] services) throws DebuggerStartException {
201: Object[] s = new Object[services.length + 1];
202: System.arraycopy(services, 0, s, 1, services.length);
203: s[0] = AttachingDICookie.create(hostName, portNumber);
204: DebuggerEngine[] es = DebuggerManager.getDebuggerManager()
205: .startDebugging(
206: DebuggerInfo.create(AttachingDICookie.ID, s));
207: int i, k = es.length;
208: for (i = 0; i < k; i++) {
209: JPDADebugger d = es[i]
210: .lookupFirst(null, JPDADebugger.class);
211: if (d == null)
212: continue;
213: d.waitRunning();
214: return d;
215: }
216: throw new DebuggerStartException(NbBundle.getMessage(
217: JPDADebugger.class, "MSG_NO_DEBUGGER"));
218: }
219:
220: /**
221: * This utility method helps to start a new JPDA debugger session.
222: * Its implementation use {@link AttachingDICookie} and
223: * {@link org.netbeans.api.debugger.DebuggerManager#getDebuggerManager}.
224: *
225: * @param name a name of shared memory block
226: */
227: public static JPDADebugger attach(String name, Object[] services)
228: throws DebuggerStartException {
229: Object[] s = new Object[services.length + 1];
230: System.arraycopy(services, 0, s, 1, services.length);
231: s[0] = AttachingDICookie.create(name);
232: DebuggerEngine[] es = DebuggerManager.getDebuggerManager()
233: .startDebugging(
234: DebuggerInfo.create(AttachingDICookie.ID, s));
235: int i, k = es.length;
236: for (i = 0; i < k; i++) {
237: JPDADebugger d = es[i]
238: .lookupFirst(null, JPDADebugger.class);
239: d.waitRunning();
240: if (d == null)
241: continue;
242: return d;
243: }
244: throw new DebuggerStartException(NbBundle.getMessage(
245: JPDADebugger.class, "MSG_NO_DEBUGGER"));
246: }
247:
248: /**
249: * Returns current state of JPDA debugger.
250: *
251: * @return current state of JPDA debugger
252: * @see #STATE_STARTING
253: * @see #STATE_RUNNING
254: * @see #STATE_STOPPED
255: * @see #STATE_DISCONNECTED
256: */
257: public abstract int getState();
258:
259: /**
260: * Gets value of suspend property.
261: *
262: * @return value of suspend property
263: */
264: public abstract int getSuspend();
265:
266: /**
267: * Sets value of suspend property.
268: *
269: * @param s a new value of suspend property
270: */
271: public abstract void setSuspend(int s);
272:
273: /**
274: * Returns current thread or null.
275: *
276: * @return current thread or null
277: */
278: public abstract JPDAThread getCurrentThread();
279:
280: /**
281: * Returns current stack frame or null.
282: *
283: * @return current stack frame or null
284: */
285: public abstract CallStackFrame getCurrentCallStackFrame();
286:
287: /**
288: * Evaluates given expression in the current context.
289: *
290: * @param expression a expression to be evaluated
291: *
292: * @return current value of given expression
293: */
294: public abstract Variable evaluate(String expression)
295: throws InvalidExpressionException;
296:
297: /**
298: * Waits till the Virtual Machine is started and returns
299: * {@link DebuggerStartException} if some problem occurres.
300: *
301: * @throws DebuggerStartException is some problems occurres during debugger
302: * start
303: *
304: * @see AbstractDICookie#getVirtualMachine()
305: */
306: public abstract void waitRunning() throws DebuggerStartException;
307:
308: /**
309: * Returns <code>true</code> if this debugger supports fix & continue
310: * (HotSwap).
311: *
312: * @return <code>true</code> if this debugger supports fix & continue
313: */
314: public abstract boolean canFixClasses();
315:
316: /**
317: * Returns <code>true</code> if this debugger supports Pop action.
318: *
319: * @return <code>true</code> if this debugger supports Pop action
320: */
321: public abstract boolean canPopFrames();
322:
323: /**
324: * Determines if the target debuggee can be modified.
325: *
326: * @return <code>true</code> if the target debuggee can be modified or when
327: * this information is not available (on JDK 1.4).
328: * @since 2.3
329: */
330: public boolean canBeModified() {
331: return true;
332: }
333:
334: /**
335: * Implements fix & continue (HotSwap). Map should contain class names
336: * as a keys, and byte[] arrays as a values.
337: *
338: * @param classes a map from class names to be fixed to byte[]
339: */
340: public abstract void fixClasses(Map<String, byte[]> classes);
341:
342: /**
343: * Returns instance of SmartSteppingFilter.
344: *
345: * @return instance of SmartSteppingFilter
346: */
347: public abstract SmartSteppingFilter getSmartSteppingFilter();
348:
349: /**
350: * Helper method that fires JPDABreakpointEvent on JPDABreakpoints.
351: *
352: * @param breakpoint a breakpoint to be changed
353: * @param event a event to be fired
354: */
355: protected void fireBreakpointEvent(JPDABreakpoint breakpoint,
356: JPDABreakpointEvent event) {
357: breakpoint.fireJPDABreakpointChange(event);
358: }
359:
360: /**
361: * Adds property change listener.
362: *
363: * @param l new listener.
364: */
365: public abstract void addPropertyChangeListener(
366: PropertyChangeListener l);
367:
368: /**
369: * Removes property change listener.
370: *
371: * @param l removed listener.
372: */
373: public abstract void removePropertyChangeListener(
374: PropertyChangeListener l);
375:
376: /**
377: * Adds property change listener.
378: *
379: * @param propertyName a name of property to listen on
380: * @param l new listener.
381: */
382: public abstract void addPropertyChangeListener(String propertyName,
383: PropertyChangeListener l);
384:
385: /**
386: * Removes property change listener.
387: *
388: * @param propertyName a name of property to listen on
389: * @param l removed listener.
390: */
391: public abstract void removePropertyChangeListener(
392: String propertyName, PropertyChangeListener l);
393:
394: /**
395: * Creates a new {@link JPDAStep}.
396: * Parameters correspond to {@link JPDAStep} constructor.
397: *
398: * @return {@link JPDAStep}
399: * @throws {@link java.lang.UnsupportedOperationException} If not overridden
400: */
401: public JPDAStep createJPDAStep(int size, int depth) {
402: throw new UnsupportedOperationException(
403: "This method must be overridden.");
404: }
405:
406: /**
407: * Test whether the debuggee supports accessing of class instances, instance counts, and referring objects.
408: *
409: * @see #getInstanceCounts
410: * @see JPDAClassType#getInstanceCount
411: * @see JPDAClassType#getInstances
412: * @see ObjectVariable#getReferringObjects
413: *
414: * @return <code>true</code> when the feature is supported, <code>false</code> otherwise.
415: */
416: public boolean canGetInstanceInfo() {
417: return false;
418: }
419:
420: /**
421: * Get the list of all classes in the debuggee.
422: * @return The list of all classes.
423: */
424: public List<JPDAClassType> getAllClasses() {
425: return Collections.emptyList();
426: }
427:
428: /**
429: * Get the list of all classes mathing the given name in the debuggee.
430: * @return The list of classes.
431: */
432: public List<JPDAClassType> getClassesByName(String name) {
433: return Collections.emptyList();
434: }
435:
436: /**
437: * Retrieves the number of instances of each class in the list.
438: * Use {@link #canGetInstanceInfo} to determine if this operation is supported.
439: * @return an array of <code>long</code> containing one instance counts for
440: * each respective element in the <code>classTypes</code> list.
441: */
442: public long[] getInstanceCounts(List<JPDAClassType> classTypes)
443: throws UnsupportedOperationException {
444: throw new UnsupportedOperationException("Not supported.");
445: }
446:
447: }
|