Geertjan's Blog

  • May 10, 2008

Coding a Swing Explorer Plugin for NetBeans IDE

Geertjan Wielenga
Product Manager
One of the cool encounters at JavaOne was with Maxim Zakharenkov, who is from Latvia, and his https://swingexplorer.dev.java.net/ project. The tool lets you introspect Swing applications and is discussed on Javalobby as well. Max told me that there's already an Eclipse plugin for his tool, but nothing for NetBeans IDE. We spent some time together yesterday and made quite some progress.

First, you need to be able to launch the Swing Explorer together with your own application. You can simply use Ant to do so:

    <target name="swingexplorer-run" description="swingexplorer-run" depends="jar">
<property file="nbproject/project.properties"/>
<java classpath="${run.classpath}" classname="org.swingexplorer.Launcher" fork="true">
<arg value="${main.class}"/>
<jvmarg line="-Dswex.mport=1099 -Dcom.sun.management.jmxremote"/>

Put the swexpl.jar from the Swing Explorer project on your classpath and you're good to go. When you then run the above script, your own app will start as well as the Swing Explorer, where you can then inspect the insides of your application and play its creation back to you (which needs to be seen to be believed).

So, that's how it works from Ant. However, what do we need to do to create a plugin so that a menu item can be selected on the project which would then launch the above?

In addition, the Eclipse plugin lets you, when you play an application, jump from each played line to the line in the editor. So, as each method is played, you can click in the Swing Explorer and then the file that is being played is opened, with the cursor landing on the appropriate line. This is made possible via JMX. For diagnostics purposes, Max and I set up VisualVM and subscribed to notifications, so that we could see how this part of the application worked and to see the properties that are exposed:

(Would be nice if the columns in the table above could be resized and that a tooltip would appear displaying a column's content.)

Therefore, what the NetBeans plugin needs to do is listen for changes on the related JMX notifications and then open the editor when the "src" button is clicked in the Swing Explorer.

  1. First create an action on project nodes, from which you'll run the Swing Explorer in combination with your own applicaation:

    <folder name="Projects">
    <folder name="Actions">
    <file name="org-netbeans-swingexplorerplugin-LaunchSE.instance"/>
    <attr name="position" intvalue="0"/>

    Open issue: How to make the action available only to Java applications, but no other project type? Currently, the menu item is displayed on all project types, which is inappropriate.

  2. In the above action's performAction, create and run the Ant script:

    protected void performAction(Node[] activatedNodes) {
    Project project = activatedNodes[0].getLookup().lookup(Project.class);
    String script = "<?xml version=\\"1.0\\" encoding=\\"UTF-8\\"?>" +
    "<project name=\\"SwingExplorerRun\\" default=\\"swingexplorer-run\\" basedir=\\".\\">" +
    "<import file=\\"nbproject/build-impl.xml\\"/>" +
    "<target name=\\"swingexplorer-run\\" description=\\"swingexplorer-run\\" depends=\\"jar\\">" +
    "<property file=\\"nbproject/project.properties\\"/>" +
    "<java classpath=\\"${run.classpath}\\" classname=\\"org.swingexplorer.Launcher\\" fork=\\"true\\">" +
    "<arg value=\\"${main.class}\\"/>" +
    "<jvmarg line=\\"-Dswex.mport=1099 -Dcom.sun.management.jmxremote\\"/>" +
    "</java>" +
    "</target>" +
    try {
    FileObject zfO = FileUtil.createData(project.getProjectDirectory(), "ant-swe.xml");
    File zf = FileUtil.toFile(zfO);
    BufferedWriter out = new BufferedWriter(new FileWriter(zf.getAbsoluteFile()));
    FileObject zfo = FileUtil.toFileObject(FileUtil.normalizeFile(zf));
    ActionUtils.runTarget(zfo, new String[]{"swingexplorer-run"}, null);
    } catch (IOException e) {
    System.out.println("IO error: " + e);

    Open issue: Maybe use ContextualAwareAction instead of CookieAction, to somehow prevent the menu item from being shown when the current project is not a Java project? But what distinguishes Java projects from other projects?

    Another open issue is that ideally the JAR files would be put on the classpath in the background. Currently I'm putting them there manually. They should be hidden from the user, though, so that they're not visible in the Libraries node and are definitely not packaged into the application's JAR.

  3. Implement an Ant logger that listens for the above target and then connects to JMX:

    public class SwingExplorerListener extends org.apache.tools.ant.module.spi.AntLogger {
    public boolean interestedInSession(AntSession session) {
    return true;
    public boolean interestedInAllScripts(AntSession session) {
    return true;
    public String[] interestedInTargets(AntSession session) {
    return AntLogger.ALL_TARGETS;
    public void targetStarted(AntEvent event) {
    String targetName = event.getTargetName();
    if (targetName.equals("swingexplorer-run")) {
    Thread thread = new Thread() {
    public void run() {
    try {
    JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:" + "1099" + "/server");
    JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
    MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
    ObjectName name = new ObjectName("org.swingexplorer:name=IDESupport");
    ActOpenSourceLine actOpenSourceLine = new ActOpenSourceLine();
    mbsc.addNotificationListener(name, actOpenSourceLine, null, null);
    } catch (Exception ex) {
    Logger.getLogger(SwingExplorerListener.class.getName()).log(Level.SEVERE, null, ex);

    For the above to work, you need a file called 'org.apache.tools.ant.module.spi.AntLogger' in META-INF/Services. In that file, simply put the FQN of the above class: 'org.netbeans.swingexplorerplugin.SwingExplorerListener'.

  4. Define the NotificationListener as follows:

    public class ActOpenSourceLine implements NotificationListener {
    public void handleNotification(Notification notification, Object handback) {
    HashMap map = (HashMap) notification.getUserData();
    String className = (String) map.get("className");
    int lineNumber = (Integer) map.get("lineNumber");
    OutputWriter writer;
    InputOutput io = IOProvider.getDefault().getIO("Hello Output", false);
    writer = io.getOut();
    writer.println("Swing Explorer Output: " + className + " at " + lineNumber);


    Open issue: Now that I have the class name and the line number, how do I open the class into the IDE's Java editor?

Of course, once this is all working, it would be cool to get it to work for NetBeans Platform applications too. Then we'd be able to replay the creation of the entire NetBeans IDE...

Join the discussion

Comments ( 3 )
  • Vadiraj Saturday, May 10, 2008
  • Geertjan Thursday, May 15, 2008

    Hi Vadiraj. Thanks a lot for that tip, it came in very useful here: http://blogs.sun.com/geertjan/entry/zipping_a_suite_s_nbm

  • Ed Erwin Sunday, May 25, 2008

    Thanks for showing how to integrate this neat tool into the netbeans build.xml. It would have taken me ages to figure out how to do that.

    When I first tried to run this on my application it failed with a mysterious error:

    java.lang.SecurityException: class "org.swingexplorer.ProblemListener"'s signer information does not match signer information of other classes in the same package

    Unluckily, I have seen similar errors before, so I guessed that I could fix this by messing with the jar manifests. So I deleted the signatures from the manifest of the swexpl.jar file and then I was able to run the program.

    I don't really understand what was wrong with the signature, but if anyone else has this problem, too, that is a solution.

Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.