Download NetBeans!

20080226 Tuesday February 26, 2008

Getting Started Extending VisualVM (Part 1)

VisualVM, i.e., the bundling of the JDK's troubleshooting tools, is perfectly positioned to become the Java developer's troubleshooter of choice—free, open source, active mailing list, with a bunch of great programmers working on it. On top of all that, it is pluggable. Do I need to explain why that makes it even more viable? Probably not. But, imagine if it wasn't—there'd be no way to extend it independently of the rest of the code. Already, that is, without the APIs being 100% figured out, there's an external plugin available, that is, the plugin was not made by one of the core VisualVM developers: Thread Dump Analyzer for VisualVM, by Ingo Rockel. In this blog entry, I will look at how you can get started extending VisualVM yourself.

In theory, you can use any editor or IDE of your choice to extend VisualVM. In reality, since VisualVM is built on the NetBeans Platform, and since NetBeans IDE is currently the only IDE that has tools (i.e., wizards, code generators, etc) for that platform, NetBeans IDE is the IDE of choice when extending VisualVM. (And that fact, to a very small extent, explains my recent interest in IntelliJ. VisualVM should be extendable in Eclipse, IntelliJ, JDeveloper, JBuilder, and any other IDE. The "only" things needed there are tools for generating a project structure, a few wizards, and some interconnections with the layer file.)

Take the following steps to get going extending VisualVM:

  1. Get the Stuff You Will Need. The binary, the sources, and the Javadoc are all open sourced and freely available to you. Since you're creating a plugin, you're by definition an adventurous type of programmer. Hence, the more info you have the better and hence you will want to look at sources and Javadoc and anything else you can get your hands on. All of that's available thanks to the open sourcing of VisualVM.

  2. Register the VisualVM Binary in NetBeans IDE. When you put the binary in the NetBeans Platform Manager, you will be able to compile your plugin against that binary. The VisualVM's APIs are then available to your plugin. Here's me registering the VisualVM binary:

    Also, make very sure that you have JDK 6 registered in the IDE, because VisualVM will not work with any earlier version of the JDK. So, the plugin that you will create must use some version of JDK 6. Also, note that some of the tabs in VisualVM will not be visible for applications that are not running on JDK 6.

  3. Open the VisualVM Sources in the NetBeans IDE Favorites Window. Put the sources in the Favorites window so that... you can explore the "plugins" folder, which you will want to do to learn how the existing three plugins ("glassfish", "jconsole", "mbeans") are created:

    By the way, you could also open them in the Projects window, since they're all in a NetBeans module, as you can see above from the fact that there's a 'nbproject' folder.

  4. Examine the VisualVM Entrypoint Document. When you download the "Milestone 3 API Docs" link from https://visualvm.dev.java.net/, you will find, apart from the Javadoc, a very handy overview of all the places where your plugin can enrich VisualVM:

    The above screenshot doesn't do the document justice. You really need to see it live, while realizing that there is still some movement in terms of the API itself and hence the document itself may also change.

  5. Say "Hello World" to VisualVM. You now have all your resources and the time has come to make your first trip into the heart of VisualVM. If you're not familiar with how plugins for the NetBeans Platform work, you're in luck, because there are many wonderful things that you can learn about, here, in the "NetBeans Modules and Rich-Client Applications Learning Trail". Assuming that you are at least familiar with the NetBeans Module Quick Start, let's say "Hello World" to VisualVM.

    Now, we'll say "Hello World". We will create a new tab that looks as follows:

    There will be no data in our tab, we'll simply create it and add some placeholders for content. Next time we'll add some interesting data.

    Let's get started! Create a new NetBeans module and make sure to specify that you want to build against the VisualVM binary that you registered in step 2 above:

    Use the "Module Installer" wizard, which will create a class that is called when the plugin is installed into VisualVM. (And, now that I know a little bit about IntelliJ, I can inform you that IntelliJ's "ApplicationComponent" class is comparable to the NetBeans "ModuleInstall" class, which is the class that the Module Installer wizard creates.) Here's where you'll find the wizard in the New File dialog:

    Once you've completed the wizard, implement the ModuleInstall.restored method and the ModuleInstall.uninstalled methods as follows:

    @Override
    public void restored() {
        HelloWorldViewProvider.initialize();
    }
    
    @Override
    public void uninstalled() {
        HelloWorldViewProvider.unregister();
    }

    Now add a dependency (which you should know how to do after reading the NetBeans Module Quick Start) on "VisualVM-Core", which provides the APIs into the VisualVM. Create a class called "HelloViewProvider", as follows:

    class HelloWorldViewProvider implements DataSourceViewsProvider<Application> {
    
        private Map viewsCache = new HashMap();
        private static DataSourceViewsProvider HelloWorldViewProvider = 
                new HelloWorldViewProvider();
    
        @Override
        public boolean supportsViewFor(Application application) {
            //Always shown:
            return true;
        }
    
        @Override
        public synchronized Set<? extends DataSourceView> getViews(
                final Application application) {
            
            DataSourceView view = viewsCache.get(application);
            if (view == null) {
                view = new HelloWorldView(application) {
                    @Override
                    public void removed() {
                        super.removed();
                        viewsCache.remove(application);
                    }
                };
                viewsCache.put(application, view);
            }
            return Collections.singleton(view);
        }
    
        static void initialize() {
            DataSourceWindowFactory.sharedInstance().addViewProvider(
                    HelloWorldViewProvider, Application.class);
        }
        
        static void unregister() {
            DataSourceWindowFactory.sharedInstance().removeViewProvider(
                    HelloWorldViewProvider);
        }
        
    }

    By reading the Javadoc that you downloaded earlier, you'll know what the two overridden methods above are all about:

    You should be using the following import statements at this point:

    import com.sun.tools.visualvm.core.datasource.Application;
    import com.sun.tools.visualvm.core.model.jmx.JmxModel;
    import com.sun.tools.visualvm.core.model.jmx.JmxModelFactory;
    import com.sun.tools.visualvm.core.ui.DataSourceView;
    import com.sun.tools.visualvm.core.ui.DataSourceViewsProvider;
    import com.sun.tools.visualvm.core.ui.DataSourceWindowFactory;
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    import javax.management.MBeanServerConnection;

    Finally, we'll create our HelloView class, which is created by our provider above:

    class HelloWorldView extends DataSourceView {
    
        private DataViewComponent dvc;
        private Application application;
        private DataViewComponent view;
        private static final String IMAGE_PATH = 
                "org/geertjan/hellovisualvm/icon.png"; // NOI18N
    
        public HelloWorldView(Application application) {
            super("Hello World", new ImageIcon(Utilities.loadImage(
                    IMAGE_PATH, true)).getImage(), 60);
            this.application = application;
            view = createViewComponent();
        }
    
        @Override
        public DataViewComponent getView() {
            return view;
        }
    
        private DataViewComponent createViewComponent() {
    
            //Data area for master view:
            JEditorPane generalDataArea = new JEditorPane();
            generalDataArea.setBorder(BorderFactory.createEmptyBorder(14, 8, 14, 8));
    
            //Panel, which we'll reuse in all four of our detail views for this sample:
            JPanel panel = new JPanel();
    
            //Master view:
            DataViewComponent.MasterView masterView = new DataViewComponent.MasterView
                    ("Hello World Overview", null, generalDataArea);
    
            //Configuration of master view:
            DataViewComponent.MasterViewConfiguration masterConfiguration = 
                    new DataViewComponent.MasterViewConfiguration(false);
    
            //Add the master view and configuration view to the component:
            dvc = new DataViewComponent(masterView, masterConfiguration);
    
            //Add configuration details to the component, which are the show/hide checkboxes at the top:
            dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
                    "Hello World Details 1", true), DataViewComponent.TOP_LEFT);
            dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
                    "Hello World Details 2", true), DataViewComponent.TOP_RIGHT);
            dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
                    "Hello World Details 3 & 4", true), DataViewComponent.BOTTOM_RIGHT);
    
            //Add detail views to the component:
            dvc.addDetailsView(new DataViewComponent.DetailsView(
                    "Hello World Details 1", null, panel, null), DataViewComponent.TOP_LEFT);
            dvc.addDetailsView(new DataViewComponent.DetailsView(
                    "Hello World Details 2", null, panel, null), DataViewComponent.TOP_RIGHT);
            dvc.addDetailsView(new DataViewComponent.DetailsView(
                    "Hello World Details 3", null, panel, null), DataViewComponent.BOTTOM_RIGHT);
            dvc.addDetailsView(new DataViewComponent.DetailsView(
                    "Hello World Details 4", null, panel, null), DataViewComponent.BOTTOM_RIGHT);
    
            return dvc;
    
        }
        
    }

  6. Install the Plugin. Install the plugin and you'll see this new tab in VisualVM:

Hurray, you've said "Hello World" and are now on the road to integrating your own cool tools into VisualVM! Next time we'll add some data.

Feb 26 2008, 06:25:14 AM PST Permalink

Trackback URL: http://blogs.sun.com/geertjan/entry/getting_started_extending_visualvm
Comments:

Hi,

thx for the nice HowTo :)

Just a note: the API Entrypoints-Image could be a bit bigger.

kind regards,

Ingo

Posted by Ingo Rockel on February 27, 2008 at 12:47 AM PST #

Thanks Ingo! You can now click on the image and it will enlarge, so now you can see the texts.

Posted by Geertjan on February 27, 2008 at 05:38 AM PST #

Post a Comment:

Name:
E-Mail:
URL:

Your Comment:

HTML Syntax: NOT allowed