I use Javascript to remote control Netbeans
using a modified version of JRUNSCRIPTIN (http://kenai.com/projects/jrunscriptin).
Most of the time my code works, but sometimes I was not getting the results I
expected. When this happened, Netbeans would beep at me while performing
certain editor actions. I was wondering if the beeping was related to why my
code didn’t work? I was pretty sure the beeping was from
java.awt.Toolkit.getDefaultToolkit().beep(). So I thought, let’s attach a
debugger, see who is calling that. Well, it was a bit tricky to get Netbeans to
debug itself. Possibly, I could have started two instances of Netbeans and used
one to debug another – Netbeans won’t do this by default, but there is a way (http://wiki.netbeans.org/FaqAlternateUserdir).
However, my code which remote controls Netbeans would get confused if more than
one Netbeans was running, so I decided to look for another debugger.
I could have tried Eclipse, I could have
tried JDeveloper. I’ve got both on my system, but possibly not the latest
versions, so I might have had to muck around to get them to work with JDK7.
Then I thought – there is a debugger that comes with the JDK – jdb. Let me try
that. First I start Netbeans with debug on:
C:\Program Files\NetBeans 7.3 Beta
2\bin>netbeans64 -J-Xdebug
-J-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044
Use “jps –v” to find out pid of netbeans.
In another window, attach to it with JDB:
jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=1044
Now, using Windows, I know the beep()
method is actually sun.awt.windows.WToolkit.beep(). You can find out the
toolkit class your platform uses with “System.println(Toolkit.getDefaultToolkit().getClass().getName())”. The
platform default is overridden by “-Dawt.toolkit=com.example.MyAWTToolkit”. So
I try to set a breakpoint with jdb on “sun.awt.windows.WToolkit.beep”:
> stop in sun.awt.windows.WToolkit.beep
Internal exception:
com.sun.jdi.NativeMethodException: Cannot
set breakpoints on native methods
at
com.sun.tools.jdi.EventRequestManagerImpl.createBreakpointRequest(EventRequestManagerImpl.java:814)
This seems to be some JDB limitation –
Netbeans debugger can put a breakpoint on a native method, jdb can't. Hmm, what
should I do.
So I decided upon the following somewhat
unorthodox approach – I wrote my own AWT toolkit. My custom AWT Toolkit
actually delegates everything to the platform one, except the beep() method
also does “new Throwable().printStackTrace()”. It solved my problem. My toolkit
isn’t perfect – I didn’t bother to delegate every method, it probably still has
some StackOverflowError bugs (any method in java.awt.Toolkit which tests if the
current Toolkit is the default one will tend to cause this) – but it seems to
be enough for Netbeans to run – the fonts look ugly, but when it beeps, it
tells me why!
Code is attached. Run it like so:
netbeans64 -J-Dawt.toolkit=
awtbug.AWTBugToolkit -J-Xbootclasspath/a:
awtbug.jar
-J-Dawtbug.realToolkit=sun.awt.windows.WToolkit
Here is a sample of the output:
java.lang.Throwable: AWTBugToolkit.beep()
at
awtbug.AWTBugToolkit.beep(AWTBugToolkit.java:393)
at
org.netbeans.modules.java.editor.imports.JavaFixAllImports.performFixImports(JavaFixAllImports.java:260)
at org.netbeans.modules.java.editor.imports.JavaFixAllImports.access$200(JavaFixAllImports.java:104)
at
org.netbeans.modules.java.editor.imports.JavaFixAllImports$1.run(JavaFixAllImports.java:148)
at
org.netbeans.modules.java.editor.imports.JavaFixAllImports$1.run(JavaFixAllImports.java:125)
at
org.netbeans.api.java.source.JavaSource$1.run(JavaSource.java:644)
at
org.netbeans.api.java.source.JavaSource$1.run(JavaSource.java:634)
at
org.netbeans.api.java.source.JavaSource$MultiTask.run(JavaSource.java:488)
at org.netbeans.modules.parsing.impl.TaskProcessor.callUserTask(TaskProcessor.java:584)
at
org.netbeans.modules.parsing.api.ParserManager$UserTaskAction.run(ParserManager.java:153)
at
org.netbeans.modules.parsing.api.ParserManager$UserTaskAction.run(ParserManager.java:137)
at
org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:201)
at
org.netbeans.modules.parsing.impl.TaskProcessor$2.call(TaskProcessor.java:198)
at
org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager.priorityIO(FileChangedManager.java:176)
at
org.netbeans.modules.masterfs.providers.ProvidedExtensions.priorityIO(ProvidedExtensions.java:360)
at
org.netbeans.modules.parsing.impl.Utilities.runPriorityIO(Utilities.java:74)
at org.netbeans.modules.parsing.impl.TaskProcessor.runUserTask(TaskProcessor.java:198)
at
org.netbeans.modules.parsing.api.ParserManager.parse(ParserManager.java:104)
at
org.netbeans.api.java.source.JavaSource.runUserActionTaskImpl(JavaSource.java:438)
at org.netbeans.api.java.source.JavaSource.runUserActionTask(JavaSource.java:409)
at
org.netbeans.api.java.source.JavaSource.runModificationTask(JavaSource.java:655)
at
org.netbeans.modules.java.editor.imports.JavaFixAllImports$2.run(JavaFixAllImports.java:164)
at org.netbeans.modules.progress.ui.RunOffEDTImpl$1.run(RunOffEDTImpl.java:150)
at
org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1454)
at
org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:2036)
Now off to read the Netbeans source code,
to find out why. (Having done so, I’m ironically left with the conclusion, that
I don’t think the beeps have anything to do with why my script doesn’t work.)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The code:
package awtbug;
import java.awt.AWTError;
import java.awt.Button;
import java.awt.Canvas;
import java.awt.Checkbox;
import java.awt.CheckboxMenuItem;
import java.awt.Choice;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Dialog;
import java.awt.Dialog.ModalExclusionType;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FileDialog;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.Insets;
import java.awt.JobAttributes;
import java.awt.KeyboardFocusManager;
import java.awt.Label;
import java.awt.List;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.PageAttributes;
import java.awt.Panel;
import java.awt.Point;
import java.awt.PopupMenu;
import java.awt.PrintJob;
import java.awt.ScrollPane;
import java.awt.Scrollbar;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.datatransfer.Clipboard;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import
java.awt.dnd.InvalidDnDOperationException;
import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.AWTEventListener;
import java.awt.font.TextAttribute;
import java.awt.im.InputMethodHighlight;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.awt.peer.ButtonPeer;
import java.awt.peer.CanvasPeer;
import java.awt.peer.CheckboxMenuItemPeer;
import java.awt.peer.CheckboxPeer;
import java.awt.peer.ChoicePeer;
import java.awt.peer.DesktopPeer;
import java.awt.peer.DialogPeer;
import java.awt.peer.FileDialogPeer;
import java.awt.peer.FontPeer;
import java.awt.peer.FramePeer;
import java.awt.peer.KeyboardFocusManagerPeer;
import java.awt.peer.LabelPeer;
import java.awt.peer.LightweightPeer;
import java.awt.peer.ListPeer;
import java.awt.peer.MenuBarPeer;
import java.awt.peer.MenuItemPeer;
import java.awt.peer.MenuPeer;
import java.awt.peer.MouseInfoPeer;
import java.awt.peer.PanelPeer;
import java.awt.peer.PopupMenuPeer;
import java.awt.peer.ScrollPanePeer;
import java.awt.peer.ScrollbarPeer;
import java.awt.peer.TextAreaPeer;
import java.awt.peer.TextFieldPeer;
import java.awt.peer.WindowPeer;
import java.beans.PropertyChangeListener;
import
java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.Properties;
import sun.awt.HeadlessToolkit;
import sun.awt.KeyboardFocusManagerPeerProvider;
/**
* AWTBUG
is a dummy AWT toolkit implementation used for debugging. It delegates
* all
calls to the standard AWT toolkit, but it intercepts beep() and prints
* the
stack trace.
*
* @author
Simon Kissane
*/
public final class AWTBugToolkit extends Toolkit
implements
KeyboardFocusManagerPeerProvider {
private
Toolkit toolkit = getNamedToolkit();
private
static synchronized Toolkit getNamedToolkit() {
final String nm = System.getProperty("awtbug.realToolkit");
return (Toolkit) java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
@Override
public Object run() {
Toolkit toolkit = null;
Class cls = null;
try {
try {
cls = Class.forName(nm);
} catch (ClassNotFoundException e) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl != null)
try {
cls = cl.loadClass(nm);
} catch
(ClassNotFoundException ee) {
throw new
AWTError("Toolkit not found: " + nm);
}
}
if (cls != null) {
toolkit = (Toolkit) cls.newInstance();
if (GraphicsEnvironment.isHeadless())
toolkit = new
HeadlessToolkit(toolkit);
}
} catch (InstantiationException e) {
throw new AWTError("Could not instantiate Toolkit: " + nm);
} catch (IllegalAccessException e) {
throw new AWTError("Could not access Toolkit: " + nm);
}
return toolkit;
}
});
}
@Override
public
KeyboardFocusManagerPeer createKeyboardFocusManagerPeer(
KeyboardFocusManager kfm) {
return ((KeyboardFocusManagerPeerProvider) toolkit).
createKeyboardFocusManagerPeer(kfm);
}
private
Object call(String method) {
return call(method, new Class[0], new Object[0]);
}
private
Object call(final String method,
final Class[] types, final Object[] args) {
return AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Object run() {
try {
Class c = Toolkit.class;
Method m = c.getDeclaredMethod(method, types);
m.setAccessible(true);
return m.invoke(toolkit, args);
} catch (NoSuchMethodException | SecurityException |
IllegalAccessException |
IllegalArgumentException |
InvocationTargetException
ex) {
throw new RuntimeException(ex);
}
}
});
}
@Override
public
DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException {
return (DesktopPeer) call("createDesktopPeer",
new Class[]{Desktop.class}, new Object[]{target});
}
@Override
public
void setDynamicLayout(boolean dynamic) throws HeadlessException {
toolkit.setDynamicLayout(dynamic);
}
@Override
public
boolean isDynamicLayoutActive() throws HeadlessException {
return toolkit.isDynamicLayoutActive();
}
@Override
public
Insets getScreenInsets(GraphicsConfiguration gc) throws
HeadlessException {
return toolkit.getScreenInsets(gc);
}
@Override
public
Image createImage(byte[] imagedata) {
return toolkit.createImage(imagedata);
}
@Override
public
PrintJob getPrintJob(Frame frame, String jobtitle,
JobAttributes jobAttributes, PageAttributes pageAttributes) {
return toolkit.getPrintJob(frame, jobtitle, jobAttributes,
pageAttributes);
}
@Override
public
int getMenuShortcutKeyMask() throws HeadlessException {
return toolkit.getMenuShortcutKeyMask();
}
@Override
public
boolean getLockingKeyState(int keyCode) throws
UnsupportedOperationException {
return toolkit.getLockingKeyState(keyCode);
}
@Override
public
void setLockingKeyState(int keyCode, boolean on) throws
UnsupportedOperationException {
toolkit.setLockingKeyState(keyCode, on);
}
@Override
public
Cursor createCustomCursor(Image cursor, Point hotSpot, String name)
throws IndexOutOfBoundsException, HeadlessException {
return toolkit.createCustomCursor(cursor, hotSpot, name);
}
@Override
public
Dimension getBestCursorSize(int preferredWidth, int preferredHeight)
throws HeadlessException {
return toolkit.getBestCursorSize(preferredWidth, preferredHeight);
}
@Override
public
int getMaximumCursorColors() throws HeadlessException {
return toolkit.getMaximumCursorColors();
}
@Override
public
boolean isFrameStateSupported(int state) throws HeadlessException {
return toolkit.isFrameStateSupported(state);
}
@Override
public
<T extends DragGestureRecognizer> T createDragGestureRecognizer(
Class<T> abstractRecognizerClass,
DragSource ds, Component c, int srcActions, DragGestureListener dgl) {
return toolkit.createDragGestureRecognizer(abstractRecognizerClass, ds,
c,
srcActions, dgl);
}
@Override
public
void addPropertyChangeListener(String name, PropertyChangeListener pcl) {
toolkit.addPropertyChangeListener(name, pcl);
}
@Override
public
void removePropertyChangeListener(String name,
PropertyChangeListener pcl) {
toolkit.removePropertyChangeListener(name,
pcl);
}
@Override
public
PropertyChangeListener[] getPropertyChangeListeners() {
return toolkit.getPropertyChangeListeners();
}
@Override
public
PropertyChangeListener[] getPropertyChangeListeners(
String propertyName) {
return toolkit.getPropertyChangeListeners(propertyName);
}
@Override
public
boolean isAlwaysOnTopSupported() {
return toolkit.isAlwaysOnTopSupported();
}
@Override
public
void addAWTEventListener(AWTEventListener listener, long eventMask) {
toolkit.addAWTEventListener(listener, eventMask);
}
@Override
public
void removeAWTEventListener(AWTEventListener listener) {
toolkit.removeAWTEventListener(listener);
}
@Override
public
AWTEventListener[] getAWTEventListeners() {
return toolkit.getAWTEventListeners();
}
@Override
public
AWTEventListener[] getAWTEventListeners(long eventMask) {
return toolkit.getAWTEventListeners(eventMask);
}
@Override
public
boolean areExtraMouseButtonsEnabled() throws HeadlessException {
return toolkit.areExtraMouseButtonsEnabled();
}
@Override
public
Dimension getScreenSize() throws HeadlessException {
return toolkit.getScreenSize();
}
@Override
public
int getScreenResolution() throws HeadlessException {
return toolkit.getScreenResolution();
}
@Override
public
ColorModel getColorModel() throws HeadlessException {
return toolkit.getColorModel();
}
@Override
public
String[] getFontList() {
return toolkit.getFontList();
}
@Override
public
FontMetrics getFontMetrics(Font font) {
return toolkit.getFontMetrics(font);
}
@Override
public
void sync() {
toolkit.sync();
}
@Override
public
Image getImage(String filename) {
return toolkit.getImage(filename);
}
@Override
public
Image getImage(URL url) {
return toolkit.getImage(url);
}
@Override
public
Image createImage(String filename) {
return toolkit.createImage(filename);
}
@Override
public
Image createImage(URL url) {
return toolkit.createImage(url);
}
@Override
public
boolean prepareImage(Image image, int width, int height,
ImageObserver observer) {
return toolkit.prepareImage(image, width, height, observer);
}
@Override
public
int checkImage(Image image, int width, int height,
ImageObserver observer) {
return toolkit.checkImage(image, width, height, observer);
}
@Override
public
Image createImage(ImageProducer producer) {
return toolkit.createImage(producer);
}
@Override
public
Image createImage(byte[] imagedata, int imageoffset, int imagelength) {
return toolkit.createImage(imagedata, imageoffset, imagelength);
}
@Override
public
PrintJob getPrintJob(Frame frame, String jobtitle, Properties props) {
return toolkit.getPrintJob(frame, jobtitle, props);
}
@Override
public
void beep() {
new
Throwable("AWTBugToolkit.beep()").printStackTrace();
toolkit.beep();
}
@Override
public
Clipboard getSystemClipboard() throws HeadlessException {
return toolkit.getSystemClipboard();
}
@Override
public
DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge)
throws InvalidDnDOperationException {
return toolkit.createDragSourceContextPeer(dge);
}
@Override
public
boolean isModalityTypeSupported(ModalityType modalityType) {
return
toolkit.isModalityTypeSupported(modalityType);
}
@Override
public
boolean isModalExclusionTypeSupported(
ModalExclusionType modalExclusionType) {
return toolkit.isModalExclusionTypeSupported(modalExclusionType);
}
@Override
public
Map<TextAttribute, ?> mapInputMethodHighlight(
InputMethodHighlight highlight)
throws HeadlessException {
return toolkit.mapInputMethodHighlight(highlight);
}
@Override
protected MouseInfoPeer getMouseInfoPeer() {
return (MouseInfoPeer) call("getMouseInfoPeer",
new Class[0], new Object[0]);
}
@Override
protected LightweightPeer createComponent(Component target) {
return (LightweightPeer) call("createComponent", new Class[]{
Component.class}, new Object[]{target});
}
@Override
protected void loadSystemColors(int[] systemColors) throws
HeadlessException {
call("loadSystemColors", new Class[]{new int[0].getClass()},
new Object[]{systemColors});
}
@Override
protected boolean isDynamicLayoutSet() throws HeadlessException {
return (Boolean) call("isDynamicLayoutSet", new Class[0], new
Object[0]);
}
@Override
protected Object lazilyLoadDesktopProperty(String name) {
return call("lazilyLoadDesktopProperty", new
Class[]{String.class},
new Object[]{name});
}
@Override
protected void initializeDesktopProperties() {
call("initializeDesktopProperties");
}
@Override
protected ButtonPeer createButton(Button target) throws
HeadlessException {
return (ButtonPeer) call("createButton",
new Class[]{Button.class}, new Object[]{target});
}
@Override
protected TextFieldPeer createTextField(TextField target) throws
HeadlessException {
return (TextFieldPeer) call("createTextField",
new Class[]{TextField.class},
new Object[]{target});
}
@Override
protected LabelPeer createLabel(Label target) throws HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected ListPeer createList(List target) throws HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected CheckboxPeer createCheckbox(Checkbox target) throws
HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected ScrollbarPeer createScrollbar(Scrollbar target) throws
HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected ScrollPanePeer createScrollPane(ScrollPane target) throws
HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected TextAreaPeer createTextArea(TextArea target) throws
HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected
ChoicePeer createChoice(Choice target) throws HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected FramePeer createFrame(Frame target) throws HeadlessException {
return (FramePeer) call("createFrame",
new Class[]{Frame.class},
new Object[]{target});
}
@Override
protected CanvasPeer createCanvas(Canvas target) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected PanelPeer createPanel(Panel target) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected WindowPeer createWindow(Window target) throws
HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected DialogPeer createDialog(Dialog target) throws
HeadlessException {
return (DialogPeer) call("createDialog",
new Class[]{Dialog.class},
new Object[]{target});
}
@Override
protected MenuBarPeer createMenuBar(MenuBar target) throws
HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected MenuPeer createMenu(Menu target) throws HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected PopupMenuPeer createPopupMenu(PopupMenu target) throws
HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public
MenuItemPeer createMenuItem(MenuItem target) throws
HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public
FileDialogPeer createFileDialog(FileDialog target) throws
HeadlessException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public
CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target)
throws HeadlessException {
return (CheckboxMenuItemPeer) call("createCheckboxMenuItem",
new Class[]{CheckboxMenuItem.class},
new Object[]{target});
}
@Override
public
FontPeer getFontPeer(String name, int style) {
return (FontPeer) call("getFontPeer",
new Class[]{String.class, Integer.TYPE},
new Object[]{name, style});
}
@Override
public
EventQueue getSystemEventQueueImpl() {
return (EventQueue) call("getSystemEventQueueImpl");
}
}